How to convert a mouse.hit direction into degrees

I have a weapon whis always looksat via CFrame to the mouse position.
Which works fine, however, I want to limit it to only -45 degrees to 45 degrees in the lookat direction.

At the moment I can “lookat” in the mouse direction, however, I dont want to shoot through the car, thats why I want to limit it to certain degrees

And thats the tricky part, now I have no idea how I can get this calculation.#
Is it possible to convert the mouse direction from the PrimaryPart into degrees ?
The model is weopon of 3 parts with 1 part set as PrimaryPart
the model itself is in a “weaponslot” on a car Chassis

grafik

that is my current code

gunPosition = game.Workspace.Cars:FindFirstChild(carName).Body.WeaponSlot
weapon:SetPrimaryPartCFrame(CFrame.new(gunPosition.Position, Vector3.new(mouse.Hit.p.X, gunPosition.Position.Y, mouse.Hit.p.Z)))

I know how to rotate the weapon… than I can easily change it to the: cframe * CFrame.Angles(…)
I just need the conversion…

maybe I searched for the wrong keywords, but I was not able to find any reference to this.

any help is highly appreciated

2 Likes

Limit the axis it’s rotating around and apply that. I don’t know which one specifically but I’ll assume that it’s the X.

local gunPosition = workspace.Cars:FindFirstChild(carName).Body.WeaponSlot
local pointPosition = mouse.Hit.Position
local X = math.clamp(pointPosition.X, -45, 45)

-- SetPrimaryPartCFrame tears apart models, should avoid using
weapon:SetPrimaryPartCFrame(CFrame.new(gunPosition.Position, Vector3.new(X, gunPosition.Position.Y, pointPosition.Z)
1 Like

I wrote this:

local limit = math.pi/4 -- equivalent to 45 degrees
local current = weapon:GetPrimaryPartCFrame().LookVector
local dir = (mouse.Hit.p - gunPosition.Position).Unit * Vector3.new(1, 0, 1)
local axis = current:Cross(dir)
weapon:SetPrimaryPartCFrame(CFrame.new(gunPosition.Position) * CFrame.fromAxisAngle(axis, math.clamp(axis.Magnitude, -limit, limit)))

Just know that the limit is supposed to be radians, if you have degrees and want to convert it to radians, use math.rad

@colbert2677 I don’t think that’s going to work

It’d be nice if you explained how it wouldn’t work rather than just saying that it may not work and leaving me off there. Though I realised myself after reading my code again, because I’m limiting the X position instead of doing anything in regards to rotation.

It’s a weird solution at heart because it sets a completely new CFrame in respect to the old one with the LookVector argument used instead of actually rotating the gun in a proper mannerism by current standards, e.g. through a weld rotation or constraints.

Thanks…
Just tried it, it works to a great extent, that its limited now to 45 degrees…
But it always stays in the direction the car spawned. so when I drive the car around, it still uses the original direction.
also, sometimes when the mouse is to close to the weapon and its arround 0 degrees, the Barrel moves veeery fast between 2 positions

Just a quick note, so we can see better I recommend using Open Broadcaster Software

1 Like

Hmm so the short answer to your question is that you need to use math.atan2, but there’s a lot more to it than that so keep reading onwards.

First off, the way you currently approach this problem is in world space. This would be okay if you weren’t clamping, but because you want that 45 degree limit to be relative to the car chassis you need to convert to object space, clamp, and then convert back.

I’ve made a little demo place that has a turret attached to a car chassis just like you so you can see what I’m referencing.

turret.rbxl (18.5 KB)

local mouse = game.Players.LocalPlayer:GetMouse()

local car = game.Workspace.Car
local chassis = car.Chassis
local turret = car.Turret

local clampAngle = math.rad(45)
local turretOffset = chassis.CFrame:ToObjectSpace(turret.CFrame)

game:GetService("RunService").RenderStepped:Connect(function(dt)
	local turretCF = chassis.CFrame * turretOffset
	local lPos = turretCF:PointToObjectSpace(mouse.Hit.p)
	local theta = math.clamp(math.atan2(-lPos.x, -lPos.z), -clampAngle, clampAngle)
	turret.CFrame = turretCF * CFrame.fromEulerAnglesYXZ(0, theta, 0)
end)

Now this explains all those CFrame methods, but I still haven’t explained why I’m using arc tangent. What we’re doing is finding the angle of a point laying on the edge of a 2D circle. Because the point we’re checking is in object space we have to flip the signs of the x and z values. If we do that then we’re left with a situation like below:

We can’t use math.acos or math.asin to find the angle here because using either will limit our range. Instead we have to math.atan2 to get the full 360 degrees (think SOHCAHTOA).

From there we simply clamp to your desired value and then convert back to world space with the rotation applied.

Hope that helps! :+1:

8 Likes

wonderful… it worked… I just had to adjust it to my coding.
Thanks a lot. :+1:

well, I still have this wired Artefacts…

do you have any idea, why this comes up ?
can it be due to the fact, that it is connected via weldconstraints ?
in your example, everything looks smooth.

I can’t verify for sure since I can’t see the model, but the weld could very well be the issue.

You can get the same effect as what I posted above with a weld or motor6D.

local mouse = game.Players.LocalPlayer:GetMouse()

local car = game.Workspace.Car
local chassis = car.Chassis
local turret = car.Turret
local weld = chassis.Weld

local clampAngle = math.rad(45)
local turretOffset = weld.C0

game:GetService("RunService").RenderStepped:Connect(function(dt)
	local turretCF = chassis.CFrame * turretOffset
	local lPos = turretCF:PointToObjectSpace(mouse.Hit.p)
	local theta = math.clamp(math.atan2(-lPos.x, -lPos.z), -clampAngle, clampAngle)
	weld.C0 = turretOffset * CFrame.fromEulerAnglesYXZ(0, theta, 0)
end)

turret weld.rbxl (18.9 KB)

Beyond that, I won’t be able to help any more without seeing your model.

1 Like

So, I quickly recreated it in a sample file.
I tried out something again…

in your version → when I drive the car, the gun stays at the same location
in my versioni → and thats when the gun makes this wired behaviour, gun moves with the car

-- Your Version
weapon.PrimaryPart.CFrame = turretCF * CFrame.fromEulerAnglesYXZ(0, theta, 0)
-- my Version
weapon.PrimaryPart.CFrame = gunPosition.CFrame * CFrame.fromEulerAnglesYXZ(0, theta, 0)

model test.rbxl (95.6 KB)
attached is the sample file

Seems to be because you’re basing it on the current gunPosition.CFrame of that render frame. You want to store the turretOffset outside the render frame. Here’s a quick fix that was working for me.

model test.rbxl (95.4 KB)

1 Like

thanks @EgoMoose - that really did the trick…
the offset - pointer has to be outside the render frame.

1 Like