Turret troubles (pls help)

I’m getting a very strange issue when attempting to turn 90 degrees in either direction with my turret system. The exact issue is shown in the video below:

And here’s the problematic function:

function LightTank:SetTurretLookVector(hitCFrame: CFrame)
	local idkMan = hitCFrame:ToObjectSpace(self.Model.Part.CFrame).LookVector
	local lookVector = idkMan --My bad for this one (didn't feel like changing four variable references)
	
	local side = math.deg(math.atan2(lookVector.Z, lookVector.X)) - 90
	local up = -math.deg(math.atan2(lookVector.Y, lookVector.Z))
	
	local cannon = self.Model.CannonAssembly
	local upHinge: CylindricalConstraint = cannon.GunMount.RollController
	local turretRing: CylindricalConstraint = cannon.TurretMount.TurretRing
	
	upHinge.TargetAngle = up
	turretRing.TargetAngle = side
	
	print("Tried to go to", side, up)
end

Does anyone have any idea why this is happening? It only occurs when the player aims 90 degrees off from the center of the vehicle in either direction.

2 Likes

Weird rotations at 90 degrees is called gimbal lock.
A rough description is that if you take an item that rotates in 3 axes (say x,y,z) and you rotate 1 axis by 90 degrees then it will match up with one of the other 2 axes and it’ll ‘flip’ the expected rotations.

Here’s another post that has a solution: How to change a single axis of a CFrame?. If you Search the forums for ‘gimbal lock’ you’ll see other solutions as well.

2 Likes

I’m pretty sure the problem is that you forgot to include the x component of lookVector when calculating the up angle.

local up = math.deg(math.atan2(lookVector.Y, math.abs(lookVector.Z) + math.abs(lookVector.X)

2 Likes

None of those fixes seem to be working. I tried the hitCFrame * CFrame.fromEulerAngles(0, y, z) but to no avail. Gimbal lock still occured. If you have any ideas pls send them, because I’m kinda stumped.

I also tried just setting the positional X value of the hitCFrame to 0, which didn’t work either…

1 Like

Maybe we can approach this differently? Like by using the xyz pos of the front target and the xyz pos of the turret. If the map is flat we can shorten it to xy. But however we could attempt to find a slope between both targets then get the degree from the slope. Once we do that we can fire projectile along those lines perfectly.

1 Like

That could work, I’ll try that rn. I imagine that’s a more common approach when making turrets now that I think about it…

2 Likes

Tried it: Now the turret just refuses to go past the position where it previously experienced gimbal lock.

2 Likes

Great! So its working? If theres any other problems dont hesitate to ask

2 Likes

I haven’t worked with physics constraints much, so this is assuming that the constraints reference angle is relative to the tank itself, and not world space but this should work if so

local TankRootCF = self.Model.PrimaryPart.CFrame -- Idk something
local RightVector = TankRootCF.RightVector
local UpVector = TankRootCF.UpVector
local LookVector = TankRootCF.LookVector
local Goal = hitCFrame:ToObjectSpace(self.Model.Part.CFrame).LookVector


local Side = math.deg(math.atan2(Goal:Dot(UpVector),Goal:Dot(LookVector)))
local Up = math.deg(math.atan2(Goal:Dot(RightVector),Goal:Dot(LookVector)))

2 Likes

No, it’s constrained within a 180 degree arc. I want 360 degree rotation. Probably an issue with how I implemented it

2 Likes

I still get gimbal lock with this solution for some reason.
Here’s my implementation of it if it helps, maybe I did something wrong.

function LightTank:SetTurretLookVector(hitCFrame: CFrame)
	local cannon = self.Model.CannonAssembly
	local origin = self.Model.Part
	local tankRootCF: CFrame = origin.CFrame
	local right, up, look = tankRootCF.RightVector, tankRootCF.UpVector, tankRootCF.LookVector
	local upHinge: CylindricalConstraint = cannon.GunMount.RollController
	local turretRing: CylindricalConstraint = cannon.TurretMount.TurretRing
	
	local goal = hitCFrame:ToObjectSpace(origin.CFrame).LookVector
	
	local up = math.deg(math.atan2(goal:Dot(up), goal:Dot(look)))
	local side = math.deg(math.atan2(goal:Dot(right), goal:Dot(look)))
	
	upHinge.TargetAngle = up
	turretRing.TargetAngle = side
end
1 Like

Thanks to everyone who tried to help me here, but I was able to fix this one on my own. I have no idea why the code attached below works, it just kinda does. Also once I got rid of the X value in the CFrame used to determine the up degree and converted it to object space it pushed the angle of “gimbal lock” back to 45 on either side, so the range of motion kinda looked like the overwatch logo. So then I just offset it by 180 if it was within that 90 degree cone and it worked…

function LightTank:SetTurretLookVector(hitCFrame: CFrame)
	local adjusted = hitCFrame:ToObjectSpace(self.Model.Part.CFrame)
	local idkMan = adjusted.LookVector
	local lookVector = idkMan
	
	local side = math.deg(math.atan2(lookVector.Z, lookVector.X)) - 90
	
	local x, y, z = adjusted:ToEulerAnglesXYZ()
	
	local filteredLV = (adjusted * CFrame.Angles(0, -y, z)).LookVector
	local up = -math.deg(math.atan2(filteredLV.Y, filteredLV.Z))
	
	if math.abs(up) < 45 then up += 180 end
	
	local cannon = self.Model.CannonAssembly
	local upHinge: CylindricalConstraint = cannon.GunMount.RollController
	local turretRing: CylindricalConstraint = cannon.TurretMount.TurretRing
	
	upHinge.TargetAngle = up
	turretRing.TargetAngle = side
	
	print(up, side)
end
2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.