Weld constraints not working when lerping and rotating cframe at the same time

You can write your topic however you want, but you need to answer these questions:
I have a script that lerps a model towards the player while moving it up in an arc. moving the primarypart of the model in an arc along with the parts its welded works perfectly fine when there is no rotation
video:

script:

local function ThrowProjectile(PlayerPos)

	local ProjectileClone = tomatoProjectile:Clone() -- clones a model of a tomato
	ProjectileClone.Parent = workspace.Storage
	local root = ProjectileClone.PrimaryPart-- root/primary part already welds every other part 
	root.Anchored = true
	accumDt = 0
	local xAdd = math.random(2, 10) -- adding a random rotation value for x,y,z
	local yAdd = math.random(2, 10)
	local zAdd = math.random(2, 10)
	local landPos = PlayerPos -- puts the end position of lerp at player position
	while true do
		accumDt = math.min(accumDt + frameTime, 1) -- maximum 1

		local lerpedPos = tomatoProjectile.MainPart.Position:Lerp(landPos, accumDt) - Vector3.new(0, 5, 0)

		local lerpedPosY = lerpedPos.Y -- modify Y position to not make it go straight like X and Z axis
		local slerpYOffset = math.sin(math.pi * accumDt) * yOffset -- arc math here
		root.Position = Vector3.new(lerpedPos.X, lerpedPosY + slerpYOffset, lerpedPos.Z)
		root.CFrame = root.CFrame * CFrame.Angles(math.rad(xAdd), math.rad(yAdd), math.rad(zAdd))

		if accumDt >= 1 then
			ProjectileExplode(ProjectileClone)
			break
		end
		task.wait(frameTime)
	end
end

Because rotation is a semi-deprecated property that bypasses the logic of CFrame, the same is true with position.
You should always be using CFrame anyway λ

The position property doesn’t play nice with weld constraints, you have to use CFrames. The solution is simply to position and rotate the root part in a single operation.
With parts welded together, if you want to move the whole assembly, you need to change the CFrame. Changing the Position/Orientation of a part will only affect that single part

local newPosition = Vector3.new(lerpedPos.X, lerpedPosY + slerpYOffset, lerpedPos.Z)
local angle = root.CFrame.Rotation * CFrame.Angles(math.rad(xAdd), math.rad(yAdd), math.rad(zAdd))
local newCFrame = CFrame.new(newPosition) * angle
root.CFrame = newCFrame

Rotation and position are neither semi-deprecated nor bypass any CFrame logic

They do.
Read documentation next time about their behavior with assemblies.

The documentation only describes the Position and CFrame properties as having different behaviors, not that one bypasses the other

It’s… the same thing.
What are you even talking about?
Point is to explain OP in a grounded language without buzzwords.
Why are you nitpicking?
Makes zero sense what you are saying.

I’m not nitpicking, where did you get “semi-deprecated” from? Deprecated implies you shouldn’t be using it at all because it’s been superseded, but these are two different behaviors.


If you want to reposition an assembly, you use CFrame, if you want to offset a part, you use Position/Orientation.

Please stop shifting the topic as your last defense - just accept defeat peacefully.
You were talking about a completely different thing.

If you wish to continue argument about this please dirrect message me instead of derailing the topic.

Yarik is my mortal enemy, but he is somewhat correct. Orientation, Position, and Rotation are post-process mathematical operations to the physics engine and exist solely as formalities to developers. Assigning .CFrame is the preferred operation to alter block’s locations. For example, do you ever notice how if you do .Position, the block respects CanCollide if it’s supposed to intersect another block, even if Anchored is true, but it doesn’t with .CFrame?

The reason for why is because the game uses Coordinate Frames for its mathematical model of 3D space. Position just alters the block’s CFrame by updating its position after it’s placed by the physics engine. Orientation and Rotation do similarly, where they take the input and then have to solve the input via EulerAnglesXYZ (thus why there’s a method called CFrame.FromOrientation(x,y,z) and why it’s equivalent to EulerAnglesXYZ. It’s an alias for the internal logic.

This is because CFrames are matrices; you can’t just give them raw input. It has to be solved per the relative coordinate space (global or another cframe). When you do .CFrame, you are doing this at the lowest level because the constructor CFrame.new() has already done the math for you and can be used quicker in the physics update cycle to update things like WeldsConstraint’s. So, Position, Orientation and Rotation are all simply formalities, even having custom logic. In the documentation, it states that you can use Position to update a block’s location and WeldConstraint’s and Welds will have their C0’s updated. However, issue!

It says, quote, ’ WeldConstraints will also be temporarily disabled and re-enabled during the move.’

That means that when OP does root.Position and root.CFrame in the same frame/heartbeat, the WeldConstraints are likely still disabled and thus when he does his CFrame assignment, the weld constraints think the new offset (as it evaluates when enabled) are actually the new value. Thus they “break”, aka just unpredictable behavior. It’s also possible though Roblox accounted for this and its simply the blocks rotating at the proper alignment but growing at their position (as Position adjusts the weld offsets)

He needs to either use Position and Rotation if he wants to make sure the new part wants to be at a further offset, or use CFrame if he wants the parts to stay at their relative distances/rotation. He just can’t use both within the same frame; a task.wait() would be required.

2 Likes