I want to make a turret that receive command to aim at a target, then try its best to rotate towards that direction.
It has a limit in rotation, so that it won’t rotate past the limit.
The cyan lines are debug ray to show where the limit should be.
Currently, my code was able to limit it at the correct angle. However, when I change target from A1 to A2 (in picture), it rotates by picking the shortest way, which is wrong since it will make it rotate past the limit to go there. It should rotate the other way.
My code, this run in FixedUpdate():
public void AimAt(Vector3 targetPosition)
{
Vector3 directionFromHorizontalPivot = targetPosition - horizontalPivot.position;
Vector3 horizontalDirection = new Vector3(directionFromHorizontalPivot.x, 0f, directionFromHorizontalPivot.z).normalized;
if (horizontalPivot != null)
{
float targetHorizontalAngle = Mathf.Atan2(horizontalDirection.x, horizontalDirection.z) * Mathf.Rad2Deg;
float currentHorizontalAngle = Mathf.DeltaAngle(_startingHorizontalEuler.y, targetHorizontalAngle);
float clampedHorizontalAngle = currentHorizontalAngle;
if (currentHorizontalAngle < horizontalLimit.x || currentHorizontalAngle > horizontalLimit.y)
{
float distanceToMinLimit = Mathf.Abs(Mathf.DeltaAngle(targetHorizontalAngle, horizontalLimit.x));
float distanceToMaxLimit = Mathf.Abs(Mathf.DeltaAngle(targetHorizontalAngle, horizontalLimit.y));
if (distanceToMinLimit < distanceToMaxLimit)
{
clampedHorizontalAngle = horizontalLimit.x;
rotationDirection = currentHorizontalAngle > clampedHorizontalAngle ? -1 : 1;
}
else
{
clampedHorizontalAngle = horizontalLimit.y;
rotationDirection = currentHorizontalAngle > clampedHorizontalAngle ? 1 : -1;
}
}
float finalHorizontalAngle = _startingHorizontalEuler.y + clampedHorizontalAngle;
Quaternion targetHorizontalRotation = Quaternion.Euler(0f, finalHorizontalAngle, 0f);
Quaternion nextRotation = Quaternion.RotateTowards(
horizontalPivot.rotation, targetHorizontalRotation, rotationDirection * horizontalSpeed * Time.deltaTime
);
float nextHorizontalAngle = Mathf.DeltaAngle(_startingHorizontalEuler.y, nextRotation.eulerAngles.y);
if (nextHorizontalAngle < horizontalLimit.x || nextHorizontalAngle > horizontalLimit.y)
{
rotationDirection *= -1;
nextRotation = Quaternion.RotateTowards(
horizontalPivot.rotation, targetHorizontalRotation, rotationDirection * horizontalSpeed * Time.deltaTime
);
}
horizontalPivot.rotation = nextRotation;
Debug.Log(
$"Target: {targetHorizontalAngle}, Current: {currentHorizontalAngle}, Clamped: {clampedHorizontalAngle}, Direction: {rotationDirection}");
}
if (verticalPivot != null)
{
Vector3 directionFromVerticalPivot = targetPosition - verticalPivot.position;
float targetVerticalAngle = Vector3.SignedAngle(horizontalDirection, directionFromVerticalPivot, verticalPivot.right);
float clampedVerticalAngle = Mathf.Clamp(targetVerticalAngle, -verticalLimit, verticalLimit);
Quaternion targetVerticalRotation = Quaternion.Euler(clampedVerticalAngle, 0f, 0f);
verticalPivot.localRotation = Quaternion.RotateTowards(
verticalPivot.localRotation, targetVerticalRotation, verticalSpeed * Time.deltaTime
);
Debug.DrawRay(verticalPivot.position, verticalPivot.forward * 50f, Color.magenta, 2f);
}
}
How to make it respect the limit and rotate in the correct direction? I want this system to be able to work with changeable horizontalLimit, which can be anything like (0, 270), (-90, 160), or (150, 270)…