Take shortest rotation

http://zippy.gfycat.com/MilkyKnobbyBorer.gif

So I have a spaceship that is rotating towards the mouse, but whenever the cursor crosses the positive x-axis (0-360) the spaceship takes the long route all the way around (see gif). How can I fix this?

LookAt code:


public float lookAt(Vector2 target) {
		return (target.sub(new Vector2(position.x, position.y)).angle());
}

Other:


	rot = 270 + lookAt(new Vector2(tarX, tarY));//get rotation towards mouse position (tarX,tarY)
	angle += (rot - angle) * 0.1f;//smooth out rotation; this is what is applied 

Thanks

What’s 270 for, is position the mouse position, and why do you need “angle +=” when you can probably just set the rotation?

target.sub(new Vector2(position.x, position.y)).angle()

Is all you need, unless I’m missing something here.

When your ship is looking in this direction:
|
|

.angle() is 90 I think

When your ship is looking in this direction:

|
|

.angle() is -90

Angles make thing hard. Use the dot product.

The answer I found for this is pretty simple. Instead of say

float degrees = 270 + vec.angle();

Do this:


vec.rotate(270);
float degrees = vec.angle();

Express both angles as complex numbers, do a dot product (like Roquen said) cross product, and the sign of the result will tell you which direction to rotate to.

This way you get the ‘shortest route’ from, say, 359 to 1deg.


class Rotation {
   public float cos, sin; // AKA complex number (i,j)

   public Rotation(float dx, float dy) {
      float length = (float) Math.sqrt(dx*dx + dy*dy);
      cos = dx / length;
      sin = dy / length;
   }

   public Rotation(float radians) {
      cos = (float) Math.cos(radians);
      sin = (float) Math.sin(radians);
   }

-   public static float dot(Rotation a, Rotation b) {
-      return a.cos * b.cos + a.sin * b.sin;
-   }

+   public static float cross(Rotation a, Rotation b) {
+      return a.cos * b.sin - a.sin * b.cos;
+   }

   public static boolean isCCW(Rotation a, Rotation b) {
      return cross(a, b) < 0.0f;
   }
}

If working with angles in degrees, I usually use the code below for this problem.

public static boolean rotateCounterClockwise(float currentAngle, float targetAngle) {
		float diff = (currentAngle - targetAngle) % 360;
		return diff > 0 ? diff < 180 : diff < -180;
}

Simply put in the current angle and the target angle you want to face, and it will return the direction which is shortest i.e. true if you should rotate counter clockwise or false if you should rotate clockwise.

On cross vs. dot. Same thing. Parallel projection vs. orthogonal…it all depends on what you’re using as a reference. You can skip on the if’s if you want to. The sign points the way. :slight_smile:

In this case it was the cross-product, or you’d have to add/subtract 270 or 90, to make it orthogonal, as the OP did.

As for removing the ifs, indeed, you’d do:


angle += Math.signum(Rotation.cross(..., ...)) * angularVelocity;

The issue occurs when you are continuing the turn so a super simple approach to this would be an if clause that detects if the mouse is going from its last angle (359 or 1) to the opposite value. Only issue is it may cause a slight jult so going to the closer float values might be more useful for you.