How to make a part linearly rotate towards a desired rotation via AngularVelocity?

Hello, I am currently writing a system to allow moving platforms to follow a series of waypoints. Part of my needs for the system is for the platforms to be able to rotate according to the orientation of the waypoint.
The platform, as you could guess, is entirely physics based, and I have made it a point to only use physics objects to act on it.

That being said, the meat of my problem is this:
For a given two angles and rotational speed variable, how can I get a vector that describes an angular velocity for one of these angles rotating towards the other?

This is what I have tried so far:

-- Get the difference between the two angles
local getAngleDelta = function(cf1, cf2)
	local diff = cf1:Inverse() * cf2
	local x, y, z = diff:ToEulerAnglesYXZ()
	
	local eulers = Vector3.new(x, y, z)
	
	return eulers
end
-- Get the desired velocity, given the delta angle and the turn speed.
local getAngVel = function(deltaAngle)
	local turnSpeed = ride.Model.TurnSpeed.Value
	local dir = deltaAngle.Unit
	local turnVel = dir * turnSpeed
	
	return turnVel
end
local deltaAng = getAngleDelta(modelRoot.CFrame, targetCf)	-- The change in angle between the ride and the target
local newAngVel = getAngVel(deltaAng)							-- The angular velocity towards the change
local turnEst = deltaAng.Magnitude/newAngVel.Magnitude		-- The estimate, in seconds, to get to the location

The “newAngVel” variable is then applied to the angular velocity instance.

This seems to work in very select situations with very particular rotational values. I haven’t pinned down exactly what causes the inconsistencies, but it never produces the desired effect 100% of the time.

This is the code that defines this debugging:

Summary
for x = 1,10 do
	local pos = Vector3.new(0 + x * 5, 30, 0)
	local part = Instance.new("Part", game.Workspace)
	part.Anchored = true

	local per = x/10
	local effectiveVel = newAngVel
	effectiveVel = newAngVel * turnEst * x
	part.CFrame = CFrame.new(pos) * (modelRoot.CFrame - modelRoot.CFrame.p) * CFrame.Angles(effectiveVel.X, effectiveVel.Y, effectiveVel.Z)

	table.insert(parts, part)
end
				
coroutine.resume(coroutine.create(function()
	wait(updateRate)
	for i,v in pairs(parts) do
		v:Destroy()
	end
end))

Here is a video of what I have in action, with some debug parts I have created. These parts represent, from right to left, 10 states of rotation, from the current rotation of the ride, to the desired rotation, based on my code from earlier. Ideally, there would be a linear progression from the current state of the ride to the final rotation of the waypoint.


As you can see, the various states are all over the place in terms of accuracy. Sometimes it shows a linear progression, sometimes it doesn’t. This is how I know that there has to be something I’m doing fundamentally wrong.

I hope there comes to be a solution to this. I was never the best with trig nor angles and general. A trigonometric explanation would be great!

1 Like

Have you considered using BodyGyros or is there a reason you don’t want to use them? They implement smooth transitioning towards a desired rotation matrix.
AlignOrientation would be another great option.

2 Likes

I have avoided using BodyGyros for this because they do not provide a truly linear transition, and also because it is impossible (to my knowledge) to calculate an estimate for how long they will take to reach their target (I plan to use this estimate to determine whether the platform will reach the waypoint within the next update so the platform does not overextend past the target)

I am using BodyGyros to stabilize the rotation of the platforms once they reach a certain threshold, however. This helps keep the angle perfect once the rotation has reached its target so that there aren’t any slight inaccuracies.

If all else fails though, I do plan to use BodyGyros for the entire process.

EDIT: By sheer chance, I’ve come across an explanation of the dot and cross products of a vector that contains an application related to my problem. I’ll attempt implementing this later tonight, and mark it as a solution if this helps with my application.

Solution:
EDIT 2: I’ve used the described post and got a system that works. It’s a little janky, but I’ll have to smooth it out later. I’ve realized that my confusion came from a misconception about angular vectors and positional vectors being mathematically comparable. I tried to use magnitude and unit vectors of the eulers of a CFrame as I would a positional vector, and that just doesn’t work.

For others that may find this post in the future and have a similar problem, the way that helped me conceptualize the problem was this:
The angle between vectors, the arccos of the dot product, is essentially the angular magnitude.
The cross product of the two vectors is like the angular direction.
You combine the direction and magnitude with the CFrame.fromAxisAngle constructor to create an angular velocity.
You can then get the XYZ euler angles to make it a vector, and then apply it to your AngularVelocity instance.

Here’s a video of my example in action:

1 Like

can u help with a code example for this?