Ok thank you for clarifying. I think it should be added to the documentation that you can instead listen to CFrame. If only I knew that in time…
It is actually documented, though the DevHub is having issues indexing the new documentation right now, so it’s a bit hard to find the page, someone is looking into the issue:
This is really cool and useful, thanks for adding this!
Should I still use Model.MoveTo if I want the model to be moved up until it doesn’t overlap any parts/terrain, or would it be better to implement this behavior myself with Model.PivotTo?
You could move over to Model:PivotTo
for that, but we’re not going to be deprecating Model:MoveTo()
at the same time as Model:SetPrimaryPartCFrame()
for exactly the reasons that you’re thinking of: It can still have useful behavior in some circumstances that’s not easy to replicate with other APIs (it’s possible, for instance the Collision based dragging in Studio is implemented with the normal Lua APIs, but it takes a lot of work to pull off).
It might eventually get deprecated at some point when we have shape-casts or something like that that would allow you to easily place an object on a surface, but currently Model:MoveTo()
still has some good uses.
1 odd thing I discovered while benchmarking SetPrimaryPartCFrame vs PivotTo is that PivotTo performs significantly slower when it get sets to the same CFrame over and over. When this is not the case, PivotTo obliterates SetPrimaryPartCFrame.
Same CFrame
Code
local ModelCFrame = CFrame.new(2, 5, 0)
function PivotTo(Model, PivotCFrame)
Model:PivotTo(PivotCFrame)
end
function SetPrimaryPart(Model, PrimaryPartCFrame)
Model:SetPrimaryPartCFrame(PrimaryPartCFrame)
end
wait(10)
local GamerModel = workspace.Trampoline
local StartTime = os.clock()
for i = 1, 10000 do
local NewVector3 = Vector3.new(2)
PivotTo(GamerModel, ModelCFrame + NewVector3)
end
local EndTime = os.clock() - StartTime
print("Pivot To: ".. EndTime)
local StartTime = os.clock()
for i = 1, 10000 do
local NewVector3 = Vector3.new(2)
SetPrimaryPart(GamerModel, ModelCFrame + NewVector3)
end
local EndTime = os.clock() - StartTime
print("SetPrimaryPart: ".. EndTime)
Different CFrame:
Code
local ModelCFrame = CFrame.new(2, 5, 0)
function PivotTo(Model, PivotCFrame)
Model:PivotTo(PivotCFrame)
end
function SetPrimaryPart(Model, PrimaryPartCFrame)
Model:SetPrimaryPartCFrame(PrimaryPartCFrame)
end
wait(10)
local GamerModel = workspace.Trampoline
local StartTime = os.clock()
for i = 1, 10000 do
local NewVector3 = Vector3.new(i % 2)
PivotTo(GamerModel, ModelCFrame + NewVector3)
end
local EndTime = os.clock() - StartTime
print("Pivot To: ".. EndTime)
local StartTime = os.clock()
for i = 1, 10000 do
local NewVector3 = Vector3.new(i % 2)
SetPrimaryPart(GamerModel, ModelCFrame + NewVector3)
end
local EndTime = os.clock() - StartTime
print("SetPrimaryPart: ".. EndTime)
Obviously, you shouldn’t be setting a model to the CFrame over and over again.
Is there any way to disable this? I’m often finding that my pivots are way off in Narnia for no reason whatsoever, and it makes my building workflow a lot slower with default tools. I find myself reverting to plugins a lot more.
I don’t want to have to set the PrimaryPart on every single model I create.
Edit: I should mention… These is happening on models that existed before pivots existed! This means a number of models in my existing build is a complete mess to work with using roblox’s default tools.
I’m guessing it’s because these models were created and parts under it were deleted later on, leaving vestigial pivot points? Whatever the case may be, it’s super hard to work with.
I remember loading gun models into my game, then the pivot point being somewhere else, I just ungrouped it, and grouped it. Should work.
Have you tried resetting the pivot? (next to the move, scale and rotate tools)
Yes, the above two suggestions work. But that’s not the point. The point is that I have hundreds of pivots from models copied/pasted that are messed up in my game, which weren’t messed up prior to this update.
Going through each of them is just annoying to deal with.
What 3rd party plugins do you use with the game?
The pivot API got turned on a few months ago, so if you were using a plugin which moves stuff around using custom code (rather than model movement functions like PivotTo or SetPrimaryPartCFrame) but doesn’t update pivots the pivots could have gotten screwed up in that interval without you noticing because you didn’t have the pivot visuals turned on yet.
I made sure that the top SBS and F3X distributions were updated to avoid that issue, but I didn’t go through other smaller plugins. Can you reset the bad pivots for now and try to figure out specifically when they’re being messed up?
Also, imported meshes have their pivot set to be equal to the origin of the space you imported from, so you should make sure that the mesh is positioned relative to the origin how you actually want it positioned now in your mesh editor where that wasn’t relevant before.
That’s because SetPrimaryPartCFrame
checks if the CFrames are very close to equal and does nothing in that case. The way that I design my APIs I prefer not to second-guess the developer if possible. There is an expectation that setting a property redundantly doesn’t do extra work if possible, but I don’t think there’s a similar expectation for functions.
TL;DR: If you want to PivotTo
to a location 0.00001 studs away from where you currently are I didn’t want to make the API second-guess you.
Just a random bit of fun: Here’s the full announcement thread from when SetPrimaryPartCFrame
was introduced back in 2014 (I also had a hand in designing that API way back in the day!). It actually had one of the (at the time) longest discussion threads of any feature announcement on the DevForum:
I just enabled a change bringing BasePart:PivotTo
in line with Model:PivotTo
: Now when you call BasePart:PivotTo
, both the part and any descendants parts will be moved just like for a Model, where previously only the part you called PivotTo
on would be moved. This was the initially intended behavior but things were implemented wrong.
This should not affect any live games because we contacted the potentially affected place owners ahead of time.
I was told to listen to CFrame since position changes will not be triggered now - CFrame does not work as well. I tried to listen to CFrame changes on primary part under model - NOT WORKING (just a simple print for every signal change).
There is now no way to listen to CFrame/position changes on models. Needs a solution, especially for a live game that depends on this.
You’re likely listening on the on a part that isn’t the root part of the assembly (it may have been the root part before but changed as you changed the assembly in question) – Position / CFrame / etc events have never been fired for non-root parts, they only get fired for the root part of the assembly.
The root part of the assembly is usually the largest part within a set of parts that are connected together with Welds / other rigid joints. You can see which part is the root part by looking at the “AssemblyRootPart” property of the part you’re interested in. To change which is the root part you can set the RootPriority property.
Another small update. With today’s release, pivot indicators are now hidden while box selecting, so if that was causing you issues, please try opting back in!
I don’t think my builder uses any third-party plugins; regardless of this, these models are all vestigial and were created before pivots were even in roblox. So I’m pretty sure there are bugs with the default tools/backwards compatibility in general.
I opted to write a script (which I run using a macros plugin) to fix random wayward pivots:
--!strict
local SIZE_SCALE_DISTANT_THRESHOLD = 1.5 -- scalar relative to total size (i.e. diameters) from the
-- centroid at which a pivot is considered distant
local changed = 0
local descs = workspace:GetDescendants()
for i = 1, #descs do
if descs[i]:IsA('Model') then
local base = descs[i] :: Model
local savePivot = base.WorldPivot
local cf: CFrame, size: Vector3 = base:GetBoundingBox()
local relCF = cf:ToObjectSpace(savePivot)
if math.abs(relCF.X) > size.X * SIZE_SCALE_DISTANT_THRESHOLD
or math.abs(relCF.Y) > size.Y * SIZE_SCALE_DISTANT_THRESHOLD
or math.abs(relCF.Z) > size.Z * SIZE_SCALE_DISTANT_THRESHOLD then
base.WorldPivot = cf
changed = changed + 1
end
elseif descs[i]:IsA('BasePart') then
local base = descs[i] :: BasePart
local relCF = base.PivotOffset
local size = base.Size
if math.abs(relCF.X) > size.X * SIZE_SCALE_DISTANT_THRESHOLD
or math.abs(relCF.Y) > size.Y * SIZE_SCALE_DISTANT_THRESHOLD
or math.abs(relCF.Z) > size.Z * SIZE_SCALE_DISTANT_THRESHOLD then
base.PivotOffset = CFrame.new()
changed = changed + 1
end
end
end
print('Adjusted the pivot of ' .. changed .. ' instances')
I wish I could just disable pivots in general, or on demand. Pivots have literally done nothing but slow down my workflow thus far.
I like this update, except for the new pivots on imported meshes. It bothers me not being able to center the pivot on the mesh. I know this is fixable by just going back into blender and fixing it, but with all the effort of reuploading the meshes, No thank you.
If you don’t care about the pivot at all, you don’t have to reimport, you can simply click the “Reset” button in the Pivot section of the ribbon bar.
FWIW figuring out what to do with this is the holdup on rolling out the feature further. We may remove pivot from import by default (putting it behind an option) until we have the new importing workflow available.