So I’m pretty sure a lot of people have had this problem before and couldn’t find any solutions, including myself.
Here’s a little preview of what I’m talking about. This is a Scythe model from my game Sound Space. It rotates and moves up and down and over time the individual parts shift apart like so:
Now I’d like to share with you my solution as well as an explanation as to why this happens.
So we have a Model
full of parts, the PrimaryPart
is one of them, all the parts are Anchored
.
Now let’s look at what :SetPrimaryPartCFrame()
does. It sets the CFrame of the PrimaryPart
to the given CFrame and it repositions all the other parts within that model accordingly.
The precision is lost is when the CFrame
offsets from the PrimaryPart
to each Part
are calculated in the model, rotated and translated to the final CFrame
, you’ve probably heard of the floating-point precision problem.
Now let’s say that you tried to Weld
/Snap
all the parts to the PrimaryPart
. You’ll find that nothing will change in terms of preserving the model’s original form. This is where I thought to myself:
“Well, how is it possible, that the parts just move out of place if they are welded together using Snaps, which have a set CFrame offset that never changes?”
Knowing all this, a theory as to why this might happen came to my mind:
Assume that the engine is optimized in a way such that the Welds
/Snaps
don’t need to update the part’s CFrame
every frame, meaning that they only update when need to, such as when a part is out of place.
Now when the parts are Anchored, the only way you could move the Parts is by code. What I mean by this is that it is not done by the game’s physics engine itself(such as the gravity force pulling it down).
Then came the time to test my theory. I needed the Welded Parts to essentially be moved by the physics engine itself. I kept the PrimaryPart
Anchored since that one can’t really shift anywhere on its own.
So I created a little function that Snap
s a Model
together, un-Anchores everything except the PrimaryPart
and puts all welds in a folder called ___Welds___
Parented to the PrimaryPart
:
function Snap(model)
local pp = model.PrimaryPart;
local objs = model:GetDescendants();
local folder = pp:FindFirstChild("___Welds___");
while folder do
folder:Destroy();
folder = pp:FindFirstChild("___Welds___");
end
folder = Instance.new("Folder");
folder.Name = "___Welds___";
folder.Parent = pp;
for _, obj in ipairs(objs) do
if obj:IsA("BasePart") or obj:IsA("MeshPart") or obj:IsA("UnionOperation") then
if obj == pp then
obj.Anchored = true;
else
obj.Anchored = false;
local snap = Instance.new("Snap");
snap.Name = pp.Name .. "->" .. obj.Name;
snap.Part0 = pp;
snap.Part1 = obj;
snap.C1 = obj.CFrame:Inverse() * pp.CFrame;
snap.Parent = folder;
end
end
end
end
After testing this, my theory was proven to be correct and indeed the reason why the Snap
s now work is that the game’s physics engine is now affecting the un-Anchored parts and thus forces the Snap
s to snap the parts back to their correct place.
One thing to note:
Welds
/Snaps
update the parts only in the Workspace! (the only place where the physics engine has any effect on them)
One area to look out for is ViewportFrames
. More often than not you don’t parent them to the Workspace
so be careful when moving models inside of them. The way I get around this is before I set the CFrame, I quickly parent it to the Workspace, set the CFrame and parent it back.
Feel free to use this snippet of code, it can come in handy.
You use it like this: Snap(game.Workspace.MyModel)