# 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!