SetPrimaryPartCFrame() is very choppy when looped

I decided to use SetPrimaryPartCFrame() for an elevator in a building I’m working on because it’s something rather new and I wanted to test it out. However, as soon as I tested it out, I realized it’s very choppy.

I’m assuming this is a bug because it would only make sense that if you move the primary part, all the other parts would move instantly. It appears there’s a short delay in-between each part’s movement.

Note: this is only happening in a server, NOT studio.

GIF to show what’s happening:

I’ll probably end up using an alternative to this method, but I wanted to mention it so that it could possibly be fixed in the future.

3 Likes

Is that entire model anchored? Any welds? When are you calling this method? In a loop, in stepped / renderstepped, etc? Code snippet please!

1 Like

Sure:

if level > currentFloor then
	repeat
		wait(0)
		elevator:SetPrimaryPartCFrame(CFrame.new(elevator.PrimaryPart.Position + Vector3.new(0,0.1,0))*CFrame.Angles(0,math.rad(180),0))
	until
		elevator.PrimaryPart.Position.Y > script.Parent:findFirstChild("Floor"..targetLevel).Position.Y
	Reset()
end

All the parts are anchored inside the model.

1 Like

Since you’re using wait(0) it isn’t synced up to rendering - this may be why it’s awkward.

Personally, I would use something like this, on the client:

local floorCFs = {} -- table of levels referring to floors

function navigateToFloor(floorNum)
    local endPos = floorCFs[floorNum]
    local startPos = elevator.PrimaryPart:GetRenderCFrame()

    local t = tick()
    local percent = 0
    local conn
    conn = game:GetService("RunService").RenderStepped:connect(function()
        local delta = tick() - t
        percent = percent + delta
        if percent >= 1 then
            elevator:SetPrimaryPartCFrame(endPos)
            conn:disconnect()
        else
            elevator:SetPrimaryPartCFrame(startPos:lerp(endPos, percent))
        end
        t = tick()
    end)
end

This way, you make sure that it runs smoothly on every client (in theory)

2 Likes

Why not weld the entire elevator, and use a BodyPosition to position it?

Use RunService:BindToRenderStep with a RenderPriority of Camera

I was thinking this too, but I’m only setting one part’s CFrame, not each of the individually. Since it’s moving the primary part, it’s technically moving the model as well, so it should be in sync not each part individually.

I find BodyPositions unreliable. I didn’t want to do what everyone else does with elevators because I always see them drift out of realignment/get stuck.

Are you doing this on the client or the server?
Due to how CFrames replicate, it causes weird behavior if done on a server.

Here’s what I do, and I mentioned it to Defaultio and he implemented it practically right away for the bridge in LT2: weld all the parts in the elevator to a central part using Motor6D, and unanchor all but that central part. Instead of CFraming an n number of parts that have to replicate, you’re only CFraming 1 part that the clients can then fill in the gaps for the rest of the elevator.

2 Likes

I just used a BodyPosition for an elevator I made.
The only problem is: shared physics.
If there are multiple players in the elevator, it’s very buggy.
I solved this by (temporary) setting the character’s physics to be owned by the server.
(Granted, they can’t move during this time, but it’s just a short elevator ride anyway)
Very smooth indeed.

How do you think the rest of the parts get moved? SetPrimaryPartCFrame probably calls a C-side loop that translates the rest of the parts with the same matrix that the primary part was translated.

You aren’t setting any part’s CFrame; you’re calling SetPrimaryPartCFrame. Roblox should be doing it right and it isn’t, so there’s the bug.