I am working on a cannon system where a player can use it to target another player that is in-round during a minigame. The projectile system for it works pretty nicely, but I am having issues with getting its orientation right.
Right before the cannon will fire at the target, the cannon is supposed to rotate via a HingeConstraint to (mostly) face towards the target. It does not do that well, as seen below:
It’s been a while since I’ve done any trig calculations. Attached is the script used for calculating the angle that is needed to make the cannon face the target:
local function getAngle(targetPos:Vector3)
local p1 = RootPart.Position
--- 'RootPart' is the origin point of the cannonball, in the barrel of the cannon
local p2 = targetPos
local hyp = math.sqrt((p1.X - p2.X)^2 + (p1.Z - p2.Z)^2)
local sad = p1.X - p2.X -- side
local degrees = math.deg(math.asin(sad/hyp))
return degrees
end
Are you trying to rotate around the Y axis, or are you trying to rotate both the yaw and pitch such that it aligns with the projectile’s trajectory at v0?
If it’s the former, you can see the following related post I made a while back in which I compute the yaw. If it’s the latter then lmk and I can come back to help.
I am only trying to have the cannon rotate along the X / Yaw Axis. The cannon’s pitch will remain the same at all times, since it allows for a nice parabolic movement.
Almost every solution I have tried has resulted in this same thing happening:
The turret moves in the correct direction of where the target is, e.g. the target is to the right of the target, the cannon moves to the right.
The cannon reaches the target, but keeps moving right, and eventually gets a weird angle of 104 degrees or something.
It stays there, at the incorrect angle.
I don’t know if this is something to do with the constraint, but I have rotated/edited the hinge (via doing so to the attachments), and the same thing happens every time!
Or, if you prefer, here’s the code from the model:
Code example
local RunService = game:GetService('RunService')
-- compute signed angle
-- i.e. the angle of rotation from a->b around the axis
-- of rotation
local function computeSignedAngle(a, b, axis, epsilon)
axis = axis or Vector3.yAxis
epsilon = epsilon or 1e-6
local ax, ay, az = a.X, a.Y, a.Z
local bx, by, bz = b.X, b.Y, b.Z
local m0 = ax*ax + ay*ay + az*az
local m1 = bx*bx + by*by + bz*bz
local m = m0*m1
local d = math.sqrt(m)
if d < epsilon then
return 0
end
d = math.clamp(a:Dot(b) / d, -1, 1)
d = math.deg(math.acos(d))
local x = ay*bz - az*by
local y = az*bx - ax*bz
local z = ax*by - ay*bx
local s = math.sign(axis.X*x + axis.Y*y + axis.Z*z)
return d*s
end
--- USAGE
local base = script.Parent.TurretBase
local hinge = base.YawHinge
local origin = base.Position
local lookVector = base.CFrame.LookVector
local dbgObj = Instance.new('Part')
dbgObj.Size = Vector3.one
dbgObj.CFrame = CFrame.new(0, 5, 0)
dbgObj.Anchored = true
dbgObj.CanTouch = false
dbgObj.CanQuery = false
dbgObj.CanCollide = false
dbgObj.BrickColor = BrickColor.Red()
dbgObj.Parent = workspace
RunService.Heartbeat:Connect(function (dt)
local displacement = dbgObj.Position - origin
local angle = computeSignedAngle(lookVector, displacement.Unit)
hinge.TargetAngle = angle
end)
Edit: Just noticed I’d flipped the a: vector3, b: vector3 variables in the example and had to negate the angle when setting the Hinge’s TargetAngle - not sure why I did this. I’ve fixed it in the example script above, but ofc, the model hasn’t been updated; you may want to do that yourself.
P.S. This one only really works on flat surfaces right now unless you set the axis to the TurretBase’s UpVector, but tbh, I would fallback to the previous example I sent above if you want it to work on any arbitrary surface
The rotate attachment was set as the Attachment0 instead of the Attachment inside the base object - this should have been reversed since base is the anchored object
The hinge’s attachment(s) were aligned on the object-space x-axis instead of world aligned
I swapped out your calculateServoAngle method(s) for the method I had most recently written but now that the motor has been fixed: any of the ones previously sent in this thread would work, incl. those by @dthecoolest and myself etc.
Feel free to change it out to whichever one you understand the most - that’s the most important part, esp. since there’s very little difference between them