Change BodyGyro orientation according to object space

I’m currently making an airplane and I’ve run into an issue when it comes to orienting the plane. I only have the plane moving on an up and down axis right now and it works completely fine when the plane is turned a certain way but if I were to turn the plane 90 degrees then the plane tilts on the same axis because it’s using world space instead of object space.

Here’s a video of my issue:

Not rotated at all:

Rotated 90 degrees to the left:

How can I make the CFrame use object space instead of world space? Here’s the line that controls the BodyGyro:

plane.model.PrimaryPart.BodyGyro.CFrame = (CFrame.new(plane.model.PrimaryPart.Position) * CFrame.Angles(AngleX,0,0))

(AngleX is what tilts the plane up and down)

1 Like

for now, just rotate it on the z axis, that should do the job. plane.model.PrimaryPart.BodyGyro.CFrame = (CFrame.new(plane.model.PrimaryPart.Position) * CFrame.Angles(0,0,AngleX)). Just look at the axis of your objects using this thing →

.
So yea your math is correct there but the rotation of the actual PART is rotated 90 or 270(-90) degrees

It gives the exact same result just this time if I rotate it 90 degrees it’s correct but if I don’t rotate it at all it’s tilting side to side instead of up and down

hmm, how about this: plane.model.PrimaryPart.BodyGyro.CFrame = (CFrame.new(plane.model.PrimaryPart.Position) * CFrame.new(plane.model.PrimaryPart.Position) :ToWorldSpace(CFrame.Angles(0,0,AngleX))

Still gives the exact same result, if it does matter here’s the code for calculating AngleX:

local mouseHitP = Origin + Direction * 100
	
	
local HypLengthX = (Origin - (mouseHitP)).Magnitude
local AdjLengthX = (Origin - (Vector3.new(mouseHitP.X, Origin.Y, mouseHitP.Z))).Magnitude
local xX = AdjLengthX / HypLengthX
local AngleX = (math.acos(xX))
if plane.model.PrimaryPart.Position.Y - mouseHitP.Y > 0 then
	AngleX = -AngleX
end

this works, however to make it much simpler you can do

AngleX = -math.asin(mouse.UnitRay.Direction.Y)

This is because the UnitRay.Direction’s magnitude (hypotenuse) is always 1

1 Like

Man you guys are complicating this quite a bit. For object space rotation, use the CFrame from PrimaryPart instead of just the position.

plane.model.PrimaryPart.BodyGyro.CFrame = plane.model.PrimaryPart.CFrame * CFrame.Angles(AngleX,0,0)

If I do this the plane does a flip. I use CFrame.new() so this doesn’t happen. Is there anyway to construct new angle values for the CFrame without using CFrame.new()?

What I mean by the plane doing a flip:

What is the code doing the math here?

local mouseHitP = Origin + Direction * 100
	
	
local HypLengthX = (Origin - (mouseHitP)).Magnitude
local AdjLengthX = (Origin - (Vector3.new(mouseHitP.X, Origin.Y, mouseHitP.Z))).Magnitude
local xX = AdjLengthX / HypLengthX
local AngleX = (math.acos(xX))
if plane.model.PrimaryPart.Position.Y - mouseHitP.Y > 0 then
	AngleX = -AngleX
end

plane.model.PrimaryPart.BodyGyro.CFrame = plane.model.PrimaryPart.CFrame * CFrame.Angles(AngleX,AngleY,0)

I cast a ray from the camera to the mouse and “Origin” is the origin of that ray and “Direction” is the direction of the ray. This is wrapped inside of a RemoteEvent that fires every .RenderStepped on the client.

Have you looked into CFrame.lookAt? I feel like that could help you out quite a bit here.

That’s what I ended up doing and so far it’s working great. But I’m still having this extremely strange issue that I’ve been having since I started this and I still don’t know what’s causing it. The BodyGyro wont rotate towards the mouse’s position properly for a few seconds and then corrects itself afterward:

The plane wouldn’t go upward for a few seconds and then it corrected itself afterward. Here’s my code:

plane.model.PrimaryPart.BodyGyro.CFrame = CFrame.lookAt(plane.model.PrimaryPart.Position, Vector3.new(mouseHitP.X,mouseHitP.Y,mouseHitP.Z))

plane.model.PrimaryPart.BodyGyro.CFrame *= CFrame.Angles(0,0,math.rad(xOffset))
	
plane.model.PrimaryPart.BodyVelocity.Velocity = plane.model.PrimaryPart.CFrame.LookVector * plane.Throttle
-- All wrapped inside of a RemoteEvent that fires every .RenderStepped

– Code that is ran when a player sits in the seat of the airplane

self.model.Seat.ChildAdded:Connect(function(child)
	if child:IsA("Weld") then
		local BV = Instance.new("BodyVelocity")
		BV.MaxForce = Vector3.new(0,0,0)
		BV.Velocity = Vector3.new(0,0,0)
		BV.Parent = self.model.PrimaryPart
		local BG = Instance.new("BodyGyro")
		BG.MaxTorque = Vector3.new(0,0,0)
		BG.Parent = self.model.PrimaryPart
			
		local hb = game:GetService("RunService").Heartbeat:Connect(function()
			if self.Throttle >= 50 then
				self.model.PrimaryPart.BodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
				self.model.PrimaryPart.BodyGyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
				self.model.PrimaryPart.BodyGyro.D = 1000
			else
				self.model.PrimaryPart.BodyVelocity.MaxForce = Vector3.new(0, 0, 0)
				self.model.PrimaryPart.BodyGyro.MaxTorque = Vector3.new(0, 0, 0)
			end
		end)
			
		self.model.Seat.ChildRemoved:Connect(function(child)
			if child:IsA("Weld") then
				hb:Disconnect()
				self.model.PrimaryPart.BodyVelocity.MaxForce = Vector3.new(0, 0, 0)
				self.model.PrimaryPart.BodyGyro.MaxTorque = Vector3.new(0, 0, 0)
			end
		end)
	end
end)

I’m not sure if I can fix this. I can’t see where the issue is and I can’t imagine what it would be. It almost behaves like a replication or physics issue, but not enough to convince me. You might just need to mess around with it some more, but at the very least I’m not the guy to ask. Good luck with it.