Turret inaccuracy problem when using :ToOrientation() and :ToEulerAnglesYXZ ()

Hello devforum,
I’m having some trouble making my turret system accurately aim at the mouse:

This happens when I use the 2 functions above to get the angles this happens:

However, when I use CFrame:ToEulerAnglesXYZ( ) it aims accurately, but it shows a different problem. It works only in π radians, when you want to aim to the opposite of that, the turret rotates in an inverse way:

This is the aiming problem when using CFrame:ToEulerAnglesXYZ()

Lastly, this is the code I’m using to rotate the turret:

game:GetService("RunService").Heartbeat:Connect(function()
	local cf = CFrame.lookAt(part1.CFrame.Position, mouse.Hit.Position):ToObjectSpace(base.CFrame)
	local cf2 = CFrame.lookAt(part2.CFrame.Position, mouse.Hit.Position)

	local _, y, _ = cf:ToEulerAnglesXYZ()
	local x, _, _ = cf2:ToOrientation()
	
	if maxRotLeft == nil and maxRotRight == nil then
		base.Joint.DesiredAngle = y
	else
		if math.clamp(y, maxRotLeft, maxRotRight) == y then
			base.Joint.DesiredAngle = y
		end
	end
	if math.clamp(x, maxDepression, maxElevation) == x then
		part1.Joint.DesiredAngle = x
	end
end)

And these are the parts:

image

Thanks for reading and thanks in advance.

3 Likes

Did you try using YXZ by itself, determining both X and Y rotation without any of the extra object space stuff? Id guess all of these things were attempted fixes but just to be sure

1 Like

Yeah I tried that, same problem.

Euler angles can be annoying to work with sometimes, but you can work around it. The angle is correct, even when it seems reversed. The problem is that the angles past pi will be flipped across the y-axis on the unit circle. That’s just a quirk with this method. You can account for this for any angles that seem “opposite” by taking the inverse and subtracting pi.

1 Like

That worked, thanks, but now there is like a wall that doesn’t let the turret get the shortest path.

1 Like

That seems like an issue with Motor6Ds, you probably don’t want to switch them out for something else this late into the turret though. I guess another option would be to snap the angle back to zero once it crosses the 2pi line?

1 Like

It never reaches 2pi, the ,max it reaches is pi/2 and -pi/2.

Is there a reason you can’t set the C0 or C1 of the motor and have to use desired angle? Using the benefits of CFrame:Lerp (and thereby quaternion double cover) will solve the positive/negative angle issue.

2 Likes

I’m having some problems limiting the rotation of the turret:

I tried to limit the turret to 100º, and it worked, but when i moved the mouse to the other side, the turret uses the shortest path. How could I do it so it takes the available path instead of a path that shouldn’t be used?

local function angleBetweenTwoVectors(a, b)
	return math.acos(a:Dot(b))
end

local function rotateY(bX, bY, mouseDirY)
	local cf = CFrame.new(base.Joint.C0.Position) * CFrame.Angles(bX, bY, mouseDirY + math.pi)
	base.Joint.C0 = base.Joint.C0:Lerp(cf, 0.1)
end

local bX, bY, bZ = base.Joint.C0:ToEulerAnglesXYZ()
--local pX, pY, pZ = part1.Joint.C0:ToEulerAnglesXYZ()

--Making the turret point to the mouse.
game:GetService("RunService").Heartbeat:Connect(function()
	local bcf = base.CFrame * CFrame.Angles(0, math.pi/2, 0)
	local mouseHitY = bcf:ToObjectSpace(mouse.Hit)
	local mouseDirY = math.atan2(mouseHitY.Z, mouseHitY.X)
	
	local angleLVY = angleBetweenTwoVectors(base.CFrame.LookVector.Unit, part1.CFrame.LookVector.Unit)
	local angleMouseY = angleBetweenTwoVectors(base.CFrame.LookVector.Unit,(mouse.Hit.Position - base.CFrame.Position).Unit)
	
	--Moving the turret in the Y axis.
	if math.clamp(angleLVY, maxRotLeft, maxRotRight) == angleLVY then
		rotateY(bX, bY, mouseDirY)
	else 
		if math.clamp(angleMouseY, maxRotLeft, maxRotRight) == angleMouseY then
			rotateY(bX, bY, mouseDirY)
		end
	end
	
	bX, bY, bZ = base.Joint.C0:ToEulerAnglesXYZ()
end)
1 Like

Makae a global variable called

OriginalAngle and this will be where it faces right at the start relative to the basepart. This can be calibrated lateron.

In the rotateY do a clamp in the
local cf = CFrame.new(base.Joint.C0.Position) * CFrame.Angles(bX, bY, mouseDirY + math.pi)

if mouseDirY + math.pi + originalangle > 100

or something like that.