So, I have a ship with a "Hardpoint" attached to it that weapons can be placed on to. Once placed, they aim at the mouse position and have a restricted rotational range: See here
The rotational range is based off minAngle and maxAngle, and are calculated by the eulerAngles.z of the parent hardpoint slot, plus/minus the angle modifier (in this case, 20). I had to do some weird rotational voodoo (see OneEightyToThreeSixty() in the code snippet) to get the mouse look angle to match the same format as the minAngle/maxAngle.
angle was coming in as -180 -> 180 degrees instead 0 -> 360 like the hardpoint's rotation. Wrote a method to convert it to 360 during runtime.
I've rewritten this script more times than I can count, but ultimately I'm hit with this issue.
As you can see, for 270 degrees of the 360 rotation, the aiming and restrictions work totally fine. But at the top right quarter of the rotation, it all goes haywire.
I've tried so many different implementations but they all seem to have this consistent issue. I cannot figure out what I'm missing. Here's the code:
public class HardpointSlot : MonoBehaviour
{
public HardpointController ship;
public GameObject hardpoint;
public float minAngle;
public float maxAngle;
public float angle;
public float offsetAngle;
public bool switched;
void Update()
{
AimAtMouse();
}
public void AimAtMouse()
{
Vector3 dir = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
minAngle = (transform.eulerAngles.z - 20) + 90f;
maxAngle = (transform.eulerAngles.z + 20) + 90f;
angle = OneEightyToThreeSixty(angle);
if (angle < minAngle)
{
angle = minAngle;
}
else if (angle > maxAngle)
{
angle = maxAngle;
}
hardpoint.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
public float OneEightyToThreeSixty(float angle)
{
float newAngle = 0f;
if (angle >= 0 && angle <= 180)
{
return angle;
} else
{
return newAngle = 360 + angle;
}
}
}
So quick recap:
Any ideas? Am I missing something very obvious?
I would take another approach, making use of Vector3.SignedAngle to get the angle from the neutral direction to the mouse direction. Then, use Mathf.Clamp to clamp it between the boundaries and then Quaternion.AngleAxis to create the resulting direction. Finally, use Quaternion.LookRotation to assign the rotation to the hardpoint's transform:
public class HardpointSlot : MonoBehaviour
{
[SerializeField] HardpointController ship;
[SerializeField] GameObject hardpoint;
[SerializeField] float minAngle;
[SerializeField] float maxAngle;
[SerializeField] float angle;
void Update()
{
AimAtMouse();
}
public void AimAtMouse()
{
Vector3 dir = Camera.main.ScreenToWorldPoint(Input.mousePosition)
- transform.position;
// flatten z axis
dir.z = 0;
Vector3 neutralDir = transform.up;
float angle = Vector3.SignedAngle(neutralDir, dir, Vector3.forward);
minAngle = -20f;
maxAngle = 20f;
angle = Mathf.Clamp(angle, minAngle, maxAngle);
// rotate neutral dir by the clamped angle
dir = Quaternion.AngleAxis(angle, Vector3.forward) * neutralDir;
// set the rotation so that local up points in dir direction
// and local forward in global forward
hardpoint.transform.rotation = Quaternion.LookRotation(Vector3.forward, dir);
}
}