How to get HingeConstraint's TargetAngle to be equal to the angle between two points?

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

Can someone help me out here?

2 Likes

Shouldn’t degrees be

local degrees = math.deg(math.atan((p2.X - p1.X)/(p2.Z-p1.Z))

The hypotenuse isn’t needed to get the degrees. You only need the inverse tangent of the slope.

2 Likes

Why not just use CFrame instead of a constraint? Would just be a simple CFrame.LookAt with a CFrame.Angles

3 Likes

This didn’t work, unfortunately, as seen below:

The angle it kept picking was (+)78/79 degrees. Obviously, that’s not the right angle here.

@DataSigh using a constraint allows for better movement visualizations

CFrames use radians, maybe that’s why the angles aren’t working in the direction you expect

(also, a lerped cframe would have the same visual effect)

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.

Previous post:

1 Like

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.

1 Like

This did not work either. As you can see in this video, it constantly keeps rotating and rotating and rotating, and never gets the right angl:

Maybe part of the issue is how my HingeConstraint is setup? Here’s something to give everyone a better visualization of it:

1 Like

Almost every solution I have tried has resulted in this same thing happening:

  1. 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.
  2. The cannon reaches the target, but keeps moving right, and eventually gets a weird angle of 104 degrees or something.
  3. 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!

1 Like

Same issues in the previous topic I sent,

it is

Would you like to provide your model so we can see the hinges? Feel free to delete any meshes you wouldn’t like to share, of course.

Here’s an example .rbxm file for you though if you’d prefer to figure out the hinges yourself:

SomeCannonExample.rbxm (44.7 KB)

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)

:warning: 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

I switched the base attachment around, and the same thing happened, just started veering to the left instead.

See attached.
cannon.rbxm (49.5 KB)

Changes:

  • 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

File:
cannon.rbxm (49.1 KB)


P.S. This will now work on any arbitrary surface unlike the prev. example I had sent you.

2 Likes

Thank you! I really appreciate this

2 Likes