0
\$\begingroup\$

I have a turret game object and a player game object, say it's a spaceship. Right now, every frame the turret updates its rotation, so it's always pointing at the player, giving it potentially infinite angular velocity.

What I want is to restrict the turret's aiming angle. As an example, say the turret may only track the player within an angle of 0 to 180 degrees (where orientation 0 would be "to the right", or Vector(1, 0)).

I can do this in code, but my issue is that if the player then enters the "tracking arc" of the turret, the turret immediately adjusts its rotation, seemingly "jumping" to tracking the player, whereas I want it to slowly rotate towards the player, based on some agility / angular_velocity variable.

How can implement an "angular velocity" so my turret rotates only at a certain speed?

My current code (scope turret):

function update(delta) 
turret.rotation = getAngleFromTo(self.position, player.position)
\$\endgroup\$
2
  • \$\begingroup\$ I recently answered a question about how to implement a RotateTowards method (ie. one that rotates at a controllable rate/step per frame). Does this help you? \$\endgroup\$
    – DMGregory
    Commented Nov 2, 2022 at 10:34
  • \$\begingroup\$ @DMZGregory That is sort of helpful, however would it be rude if i would ask you to repost this but for a 2D variant ? I have almost no experience in 3D so im sort of unable to adjust your code for my usecase. Sorry. \$\endgroup\$ Commented Nov 2, 2022 at 11:18

1 Answer 1

1
\$\begingroup\$

First we'll make a couple utility functions:

// Map an angle into a standard range [-180, 180) degrees.
// (This matches Atan2 return range, avoids special cases for angles > 360 etc.,
//  and makes it easy to rotate "the shortest way" between two angles.)
float NormalizeAngle(float degrees) {
    degrees += 180f;
    float fullTurns = Mathf.Floor(degrees/360f);
    return degrees - fullTurns * 360f - 180f;
}

// Clamp an angle between a clockwise limit and counterclockwise limit (all in degrees),
// snapping out of bounds angles to whichever bound is closest around the circle.
float ClampAngle(float degrees, float cwLimit, float ccwLimit) {
    // Get all inputs into standard range, to minimize wrap-around cases to deal with.
    degrees = NormalizeAngle(degrees);
    cwLimit = NormalizeAngle(cwLimit);
    ccwLimit = NormalizeAngle(ccwLimit);
    
    // If in bounds, return the angle. Two cases for whether the allowed 
    // range does not / does cross the -180/180 wrap-around point.
    if (ccwLimit >= cwLimit) {
        if (degrees <= ccwLimit && degrees >= cwLimit)
            return degrees;
    } else {
        if (degrees >= cwLimit || degrees <= ccwLimit)
            return degrees;
    }

    // If we get here, the angle is outside our range.
    // Find its difference from each limit:
    float cwDifference = Mathf.Abs(NormalizeAngle(degrees - cwLimit));
    float ccwDifference = Mathf.Abs(NormalizeAngle(degrees - ccwLimit));

    // Clamp to the closest of the two limits:
    if (cwDifference <= ccwDifference)
        return cwLimit;
    else
        return ccwLimit;
}

Now we can compute your new rotation angle like so:

float GetNewRotation(float degreesPerSecond, float deltaTimeSeconds) {

    // Form a vector pointing from the turret to the player.
    Vector2 toTarget = player.position - turret.position;

    // Extract the bearing angle from this vector, in range [-180, 180).
    float targetAngle = Math.Atan2(toTarget.y, toTarget.x) * Mathf.Rad2Deg;

    // Clamp to within our turret's min/max range.
    targetAngle = ClampAngle(targetAngle, turret.cwLimit, turret.ccwLimit);

    // Find shortest signed angle between current and target rotation.
    float difference = NormalizeAngle(targetAngle - turret.rotation);

    // Work out maximum travel this frame, and clamp difference to those bounds.
    float maxAngleStep = degreesPerSecond * deltaTimeSeconds;

    // Can be replaced with a Clamp(value, min, max) function if you have one.
    float step = Mathf.Min(Mathf.Max(difference, -maxAngleStep), maxAngleStep);

    // Increment our rotation angle by this limited step, and return a normalized version.
    return NormalizeAngle(turret.rotation + step);
}
\$\endgroup\$
1
  • \$\begingroup\$ fantastic, you are a genius \$\endgroup\$ Commented Nov 6, 2022 at 10:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .