:SetPrimaryPartCFrame() gradually offsets component parts

I made an animated barrier thing that uses :SetPrimaryPartCFrame() to move portions of the barrier. Here is its progress over time:

When I first open it up in run mode:

I minimized studio and left it alone for a while. A little short of an hour later and it’s turned into this:

Notice how some of the pieces are no longer connected. Leaving it for a day resulted in this:

Here is a picture that shows the individual models:

local interpolation = require(script.CFrameInterpolator)

local root = script.Parent
local center = root.Center
local arms = {root.Arm1, root.Arm2}
local feet = {root.Foot1, root.Foot2, root.Foot3, root.Foot4}

local delta = {
	sideFoot = CFrame.new(0, 0, -0.15198);
	otherFoot = CFrame.Angles(0, math.pi/2, 0);
	arm = CFrame.Angles(-math.pi/2, 0, 0);
}
--currentSize = 104.18369293213

root = script.Parent
function getModelScale()
	return root.Center:GetModelSize().magnitude
end

function toUnitCFrame(cframe)
	return (cframe - cframe.p + cframe.p/getModelScale())
end

function fromUnitCFrame(cframe)
	return (cframe - cframe.p + cframe.p*getModelScale())
end

local offsets = {
	foot1 = center:GetModelCFrame() * fromUnitCFrame(CFrame.new(2.37998208e-007, -0.40409416, 0.191968918, -1.00000036, 1.61602384e-006, -2.65690829e-007, 1.61602418e-006, 1.0000006, -2.98019387e-008, 2.65691085e-007, -4.47039739e-008, -1.0000006));
	foot2 = center:GetModelCFrame() * fromUnitCFrame(CFrame.new(3.11228433e-007, -0.404094189, -0.191968471, 1.00000036, 1.61602384e-006, 2.65690829e-007, -1.61602418e-006, 1.0000006, 2.98019387e-008, -2.65691085e-007, -4.47039739e-008, 1.0000006));
	foot3 = center:GetModelCFrame() * fromUnitCFrame(CFrame.new(0.0364743434, -0.456885636, -6.59071986e-007, -1.00000036, -4.47281536e-008, -1.48290587e-006, 1.4829061e-006, 2.15568534e-007, -1.0000006, 4.47284378e-008, -1.0000006, -2.00666733e-007));
	foot4 = center:GetModelCFrame() * fromUnitCFrame(CFrame.new(-0.0364743061, -0.456885636, 3.84458644e-007, 1.00000036, 2.80591706e-007, -1.81424491e-006, -1.81424525e-006, 1.27260016e-008, -1.0000006, -2.80591991e-007, 1.0000006, 2.76281096e-008));
	
	arm1 = center:GetModelCFrame() * fromUnitCFrame(CFrame.new(1.61106482e-006, 0.388737112, 0.137256429, 0.99999392, 1.6160194e-006, 8.68786856e-008, -1.61601292e-006, 0.999997735, 1.55712144e-006, -8.68760992e-008, -1.57202317e-006, 0.99999702));
	arm2 = center:GetModelCFrame() * fromUnitCFrame(CFrame.new(1.24491373e-006, 0.388736904, -0.137257516, -1.00000036, 1.61602384e-006, -2.65690829e-007, 1.61602418e-006, 1.0000006, -2.18651941e-008, 2.65691085e-007, -3.67672293e-008, -1.0000006));
}

require(feet[3].ModuleScript)
require(feet[4].ModuleScript)

local startFootOffset = CFrame.Angles(0,0,0)
while true do
	local interpolate1 = interpolation.new(offsets.foot1, offsets.foot1 * fromUnitCFrame(delta.sideFoot))
	local interpolate2 = interpolation.new(offsets.foot2, offsets.foot2 * fromUnitCFrame(delta.sideFoot))
	local interpolate3 = interpolation.new(offsets.foot3, offsets.foot3 * delta.otherFoot)
	local interpolate4 = interpolation.new(offsets.foot4, offsets.foot4 * delta.otherFoot)
	local interpolatea1 = interpolation.new(offsets.arm1, offsets.arm1 * delta.arm)
	local interpolatea2 = interpolation.new(offsets.arm2, offsets.arm2 * delta.arm)
	for i=1,25 do
		feet[1]:SetPrimaryPartCFrame(interpolate1(i/25))
		feet[2]:SetPrimaryPartCFrame(interpolate2(i/25))
		feet[3].Foot:SetPrimaryPartCFrame(interpolate3(i/25))
		feet[4].Foot:SetPrimaryPartCFrame(interpolate4(i/25))
		arms[1]:SetPrimaryPartCFrame(interpolatea1(i/25))
		arms[2]:SetPrimaryPartCFrame(interpolatea2(i/25))
		wait()
	end
	for i=24,0,-1 do
		feet[3].Foot:SetPrimaryPartCFrame(interpolate3(i/25))
		feet[4].Foot:SetPrimaryPartCFrame(interpolate4(i/25))
		feet[1]:SetPrimaryPartCFrame(interpolate1(i/25))
		feet[2]:SetPrimaryPartCFrame(interpolate2(i/25))
		arms[1]:SetPrimaryPartCFrame(interpolatea1(i/25))
		arms[2]:SetPrimaryPartCFrame(interpolatea2(i/25))
		wait()
	end
end

None of that code sets the CFrame of any of the individual components inside those models. The only thing that moves them is :SetPrimaryPartCFrame(). Regardless of what I do with :SetPrimaryPartCFrame(), the models that make up the barrier should stay together – they don’t. This is an issue with :SetPrimaryPartCFrame() that causes components to gradually offset from each other over time.

16 Likes

I wonder if Roblox changed how part rotations fundamentally work now, as posted here: http://developer.roblox.com/forurotating an object sometimes rotates by less than 90 the rotation tool is also having rotation issues where it didn’t have any before… did Roblox change to using eular angles, or quaternions perhaps, instead of matrices?

1 Like

I believe there could be float data loss overtime and you’d be better off with a method that saves object space offset CFrames.

Maybe a module that saves those offsets and then gives you a function to position a model without exploding like that?

[quote] I believe there could be float data loss overtime and you’d be better off with a method that saves object space offset CFrames.

Maybe a module that saves those offsets and then gives you a function to position a model without exploding like that? [/quote]

The issue is that setting the model’s CFrame shouldn’t affect the Children’s relative offsets, and requiring someone to make a complex workaround isn’t all that nice, but it’s a possibility I guess :-/

SetPrimaryPartCFrame wasn’t designed to work with this. That’s the problem. It’s designed to look at the current position, take the new position, and move all the parts keeping those relative positions. On the other hand, what YOU want to be doing is different. You want that relative position to stay the same no matter what. SetPrimaryPartCFrame does this in theory, but rounding point errors cause that to be impossible. You’ll have to write your own code for this. Don’t worry, though – it’s not super complicated.

1 Like

Oh, alright @Davidii – thanks. I already have something I use to CFrame an entire (anchored) first-person model, so I should just be able to use that. If this is just something that we’ll have to live with, I have no problem using my own.

It’s not really something “we have to live with” it’s just physically impossible to have that degree of precision unless you know the way the model is supposed to be shaped every time you move it.

Of course – I didn’t mean it in that way. By having to live with it I was stating that it’d be impossible to fix the offset over time.

I wonder if there would be a way to make a function that wouldn’t have the floating point data loss, life if you could pass it some other variable, like the original model, or something like that, and it would make the new positions of the parts based on that…

Did anyone ever solve this? I’ve just began using SetPrimaryPartCFrame to move a model forward/backwards and eventually pieces are just floating everywhere.

2 Likes

You should be using Welds to define fixed transformations between parts once instead of using SetPrimaryPartCFrame, which computes the “offsets” of all parts to the PrimaryPart, then computes all new CFrame values using that computed offset and the new PrimaryPart CFrame. The constant recomputations of these “offsets” causes the effect you see. By using Welds, you store the offset permanently (which you compute once at start-up with e.g. CFrame::toObjectSpace()).

3 Likes