How to rotate a spacecraft correctly with BodyAngularVelocity?


#1

Hello again. Yes, I’m still trying to figure out newtonian flight mechanics. Problem of the week is I can’t for the life of me figure out how to rotate the spacecraft correctly using BodyAngularVelocity. I can rotate around a single axis perfectly fine, but the moment I try rotating around another axis as well, it all just falls apart and the ship decides to spin in whichever way it feels like.

In other words: I have no idea how rotations work. I get that BodyAngularVelocity applies angular velocity around world axes, so I tried to use vectorToObjectSpace to account for that and make it rotate around local axes, but that didn’t work. Disclaimer: I’m hopeless at mathematics, and usually just scour the wiki for functions that look like they’ll work, I plug them in and hope for the best. With that being said, here’s my dumpster fire of a script (or, at least, the important part):

local deltaTime = tick() - lastTick
local netThrust = Vector3.new(0,0,0)
local netTorque = Vector3.new(0,0,0)

for _,thruster in pairs(script.Parent.Thrusters:GetChildren()) do
	local throttle = 0
	local rotateTags = {}
	
	for _,tag in pairs(_G.svc.collect:GetTags(thruster)) do
		if string.find(tag,"Thruster:") and thrusterGroups[string.sub(tag,10,string.len(tag))] then
			throttle = math.clamp(throttle + thrusterGroups[string.sub(tag,10,string.len(tag))],0,1)
			
			if string.find(tag,"Pitch") or string.find(tag,"Roll") or string.find(tag,"Yaw") then
				table.insert(rotateTags,string.sub(tag,10,string.len(tag)))
			end
		end
	end
	
	if throttle > 0 then
		local impulseFactor = math.max(math.pi*thruster.Size.Z^2,0.01)*throttle
		netThrust = netThrust + ((thruster.CFrame.rightVector*-1)*impulseFactor)
		
		for _,tag in pairs(rotateTags) do
			if tag == "PitchUp" then
				netTorque = netTorque + Vector3.new(impulseFactor,0,0)
			elseif tag == "PitchDown" then
				netTorque = netTorque + Vector3.new(-impulseFactor,0,0)
			elseif tag == "RollCounter" then
				netTorque = netTorque + Vector3.new(0,0,-impulseFactor)
			elseif tag == "RollClockwise" then
				netTorque = netTorque + Vector3.new(0,0,impulseFactor)
			elseif tag == "YawLeft" then
				netTorque = netTorque + Vector3.new(0,-impulseFactor,0)
			elseif tag == "YawRight" then
				netTorque = netTorque + Vector3.new(0,impulseFactor,0)
			end
		end
	end
end

script.Parent.Handle.TotalVelocity.Velocity = script.Parent.Handle.TotalVelocity.Velocity + (netThrust*deltaTime)
-- ROTATION: GET ALL FIRING THRUSTERS, THEIR IMPULSE FACTORS AND DISTANCES FROM COM TO DETERMINE TURN RATE
script.Parent.Handle.TotalTorque.AngularVelocity = script.Parent.Handle.CFrame:vectorToObjectSpace(netTorque)

Wonderful, the forums decided to post this as-is when I pressed tab. Please give me a few moments to finish this post.

I get the feeling that the last line’s the important one here:
script.Parent.Handle.TotalTorque.AngularVelocity = script.Parent.Handle.CFrame:vectorToObjectSpace(netTorque)

Tried changing vectorToObjectSpace to pointToObjectSpace. No luck, but the results were… interesting.


#2

What you need is vectorToWorldSpace, since you are converting from Object Space (the part’s rotation) to World Space (rotation that is compatible with the Angular Velocity)

script.Parent.Handle.TotalTorque.AngularVelocity = netTorque:vectorToWorldSpace(script.Parent.Handle.CFrame:toEulerAnglesXYZ())

Give that a go, I think this works the way you want.


#3

D’oh, I feel like an idiot. Was using the complete opposite method. Yeah, that’s the one. This has been annoying me all day, many thanks!