Part rotating the wrong way if it faces backwards

So there’s this floating thing i made, works well and all, but i noticed that it started to act like a drunk person on a motorboat.

Basic operation:
There is the Body (floating part with BodyAngularVelocity), and i calculate the ideal body orientation from the normal of the part bellow. From that i can just get the AngularVelocity required to rotate suchly.

Suspected problem:
I think the IdealBodyCframe's LookVector doesn’t follow the Body's LookVector.

I’ve narrowed it down, and found out that if the part looks backwards, my math acts like the part was looking forward.

Ignore the nightmare fuel. I thought it was funny.

The problematic code in question.

Body is… the part.
GroundAngularVelocity is so that it follows the ground.
RotSpeed is provided by the client and can be ignored.

local CurrentBodyCF = Body.CFrame
local NormalVector = RaycastResult.Normal
local IdealBodyCF = CFrame.fromMatrix(Vector3.new(0,0,0), CurrentBodyCF.lookVector:Cross(NormalVector), NormalVector, -CurrentBodyCF.lookVector)
local DifferenceCF = CurrentBodyCF:ToObjectSpace(IdealBodyCF)
local Axis, Angle = DifferenceCF:ToAxisAngle()
BodyAngularVelocity.AngularVelocity = GroundAngularVelocity + (Axis*Angle+RotSpeed)*10

So how can i calculate this correctly?
Thanks in advance.

You need to make sure that the LookVector is perpendicular not only to the RightVector, but also to the NormalVector, otherwise it is not a valid CFrame value. You can do this by changing your code to the following:

local right = NormalVector:Cross(-CurrentBodyCF.LookVector).unit
local forward = -NormalVector:Cross(right).unit
local IdealBodyCF = CFrame.fromMatrix(Vector3.new(0,0,0), right, NormalVector, forward)

This also solves another issue you will encounter in the future as soon as your character faces any other angle that does not just so happen to be perfectly perpendicular to the surface normal. In your initial code, though, you need to ensure that your code follows the same pattern. For instance, your LookVector is -CurrentBodyCF.LookVector, yet you use CurrentBodyCF.LookVector in your calculation of the right vector. (note the difference of the negative sign in your right vector calculation) While this will still, technically, give a valid CFrame, it won’t be the one you’re looking for, so I might instead suggest:

Edit: I forgot the backward vector is used in the CFrame matrix for a second… whoops. What I said regarding -LookVector/etc is not valid. Here’s the incorrect code I wrote that I am referring to:

local IdealBodyCF = CFrame.fromMatrix(Vector3.new(0,0,0), (-CurrentBodyCF.lookVector):Cross(NormalVector), NormalVector, -CurrentBodyCF.lookVector)

Like this?

local CurrentBodyCF = Body.CFrame
local NormalVector = RaycastResult.Normal
local Right = NormalVector:Cross(-CurrentBodyCF.LookVector).unit
local Forward = -NormalVector:Cross(Right).unit
local IdealBodyCF = CFrame.fromMatrix(Vector3.new(0,0,0), Right, NormalVector, Forward)
local DifferenceCF = CurrentBodyCF:ToObjectSpace(IdealBodyCF)
local Axis, Angle = DifferenceCF:ToAxisAngle()
BodyAngularVelocity.AngularVelocity = GroundAngularVelocity + (Axis*Angle+RotSpeed)*10

Because it’s still drunk.


Did i miss something?

Can you check what occurs when facing the other direction for me?

It follows the ground normal as usual.

Also this does this:

I’m starting to think there might be other problems, but nothing touches theese values outside of that 4-6 lines, and nothing writes to BodyAngularVelocity either.

For the sake of clarifying the actual orientation of IdealBodyCF, can you instead of using a BodyAngularVelocity, use a BodyGyro, setting its CFrame to the orientation of the IdealBodyCF (this is to verify that there are no problems with the current method you are using to rotate your “character”)

In my quick tests with the code I provided, I was able to create this little “pet” that does the following based on my Character’s LookVector, and the ground’s surface normal:

Screenshot_4

1 Like


I was hoping it would flip. But yes the IdealBodyCF is correct. So the velocity calculations are wrong i guess.

In that case I might just recommend using a BodyGyro instead of a BodyAngularVelocity, since it does all of the calculations for you that you would otherwise need to do manually, and more efficiently/smoothly at that.

I reverted to the old version to measure Axis.
Output of Axis Forward:
image

Backward:
image

Isn’t it just double rotated? Both outputs should be the same.
Like the part is facing forward, it’s not rotated because… the part is facing forward. But if it faces backward, it rotates the axis to face backward from the part?
Is this too dumb? NO IT IS’NT! It should be relative to world.

Wait, i’ve dealt with something like this before.

You are correct in that the Axis is relative to the world. I’m not sure what RotSpeed is but it could be overriding your Axis*Angle multiplication by adding a vector that might be always positive or always negative to your calculation?

If you could print Axis*Angle in both cases, and then Axis*Angle + RotSpeed it should give us a clearer picture of what’s going on.

Or, ideally, you could just add the code:

print("Axis:", Axis, "Angle:", Angle, "RotSpeed:", RotSpeed, "Final:", Axis*Angle + RotSpeed)

If I had to make a guess, RotSpeed is for the XZ plane of rotation (XY plane in standard vector math)?

1 Like

No RotSpeed just adds MouseDelta.X as Y axis rotation. Even after i’ve removed it, it flipped over.

Yeah but it seems like it somehow gets rotated to be relative to the Body.
Since AngularVelocity is relative to world, the output should be the same.

See? One gets rotated ~1 on the X axis. The other one does too but it’s negative when it shouldn’t be. Positive “rotates towards” the normal and negative “rotates away” from it. It should rotate to it in both cases.

Check the 2nd value changes!

DUDE!
I did it.
For future reference:

local CurrentBodyCF = Body.CFrame
local NormalVector = RaycastResult.Normal
local IdealBodyCF = CFrame.fromMatrix(Vector3.new(0,0,0), CurrentBodyCF.lookVector:Cross(NormalVector), NormalVector, -CurrentBodyCF.lookVector)
local DifferenceCF = CurrentBodyCF:ToObjectSpace(IdealBodyCF)
local Axis, Angle = DifferenceCF:ToAxisAngle()
Axis = ((CurrentBodyCF  - CurrentBodyCF.Position) * CFrame.new(Axis)).Position

It auto smoothed itself. Also i can smooth it out, with some PID experience. (the floating is smoothed)

This feels so botched. :smile:

2 Likes