Fix model parts shifting apart over time

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 Snaps 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 Snaps now work is that the game’s physics engine is now affecting the un-Anchored parts and thus forces the Snaps 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)

I hope I explain it well enough and I hope you can use this in the future.

2 Likes

The post explains how I solved the issue.

2 Likes