AngularVelocity unstable along Y-Axis

I am creating my own AlignOrientation implementation to allow me to enable/disable control on specific axes. The intended behavior is like this.
https://gyazo.com/0b91eb88f3b915f388dd70ceae2e8639

However I’ve noticed when my goal is set to +/-90 degrees along the Y axis and if the X and Z axis are not set to 0 the part becomes unstable like this.
https://gyazo.com/c8772700a1354ca056b0cb0c01716c1a

This is my script and explorer layout.

local RunService = game:GetService("RunService")
local Part = script.Parent
local AngularVelocity = Part.Attachment.AngularVelocity
local GoalAngle = workspace.GoalAngle

AngularVelocity.MaxTorque = math.huge

RunService.Heartbeat:Connect(function()
 local goalX, goalY, goalZ = math.rad(GoalAngle.Value.X), math.rad(GoalAngle.Value.Y), math.rad(GoalAngle.Value.Z)
 local curX, curY, curZ = Part.CFrame:ToEulerAnglesXYZ()
 local diffX, diffY, diffZ = goalX - curX, goalY - curY, goalZ - curZ

 AngularVelocity.AngularVelocity = Vector3.new(diffX, diffY, diffZ) * Part.Mass
end)

image

File download if you don’t want to set it up yourself.
Custom AlignOrientation.rbxl (23.3 KB)

Doesn’t AlignOrientation already have an option to disable controls on an axis via the following bool setting:

PrimaryAxisOnly If set to true, then the AlignOrientation will only apply torque if the primary axis of its Attachment0 becomes unaligned with Attachment1. This means that any rotation about the Attachment0’s primary axis will not create a torque.

Moreover, you can change the axis of the primary axis by modifying the orientation or CFrame of attachment 0 and attachment 1.

Currently, I believe two things can be wrong with your script.

  1. Rotation axis could be incorrect
Vector3.new(diffX, diffY, diffZ) 
  1. Angular velocity needed to apply to the target angles are incorrect and leads to overshooting the goal which leads to the unstable spin. This can be possibly resolved by looking into Proportional-Integral-Derivatives PID like within Aero framework provided in order to adjust the angular velocity in order to adjust the velocity in order to reach the target.

I would suggest visually debugging the issues with these as it’s hard to exactly tell why it’s unstable.

Maybe I can suggest looking into further mimicking what AlignOrientation does which is to find the angle between the attachment axis and applying a rotation in the cross product of that direction which is what I believe after some experimentation in the screenshot below:

But yeah it seems like a lot of work to debug and fix when you can just use AlignOrientation since it already has an option to control the axis of rotation through that bool option.

ToEulerAngles was not reliable enough. This is the solution I found following a ScriptingHelpers post. The modulus statements are for instances when traversing between <-90 and >90 degrees as by default it’ll take the longer route >180 degrees rotation.

local RunService = game:GetService("RunService")

local Part = script.Parent
local AngularVelocity = Part.Attachment.AngularVelocity
local GoalAngle = Part.GoalAngle

AngularVelocity.MaxTorque = math.huge

local pi = math.pi

RunService.Heartbeat:Connect(function()
	local goalX, goalY, goalZ = math.rad(GoalAngle.Value.X), math.rad(GoalAngle.Value.Y), math.rad(GoalAngle.Value.Z)
	
	local x, y, z, m00, m01, m02, m10, m11, m12, m20, m21, m22 = Part.CFrame:components()
	local curX, curY, curZ = math.asin(-m12), math.atan2(m02, m22), math.atan2(m10, m11)
	local diffX, diffY, diffZ = goalX - curX, goalY - curY, goalZ - curZ
	
	if math.abs(diffX) > pi then
		diffX %= pi
	end
	if math.abs(diffY) > pi then
		diffY %= pi
	end
	if math.abs(diffZ) > pi then
		diffZ %= pi
	end
	
	AngularVelocity.AngularVelocity = Vector3.new(diffX, diffY, diffZ) * Part.Mass
end)
1 Like