1
\$\begingroup\$

I have a Sprite class like this:

class Sprite
{
  double rotation ; // in degrees 0-360
  double x;
  double y;
}

I'd like to have one sprite to rotate to another point with a constant rotation rate, the sprite should not rotate directly but mor like "rotate more left, rotate more right" enter image description here The reason for this is that I want to implement a homing lethargic missile.

Someone knows the math behind this? :)

\$\endgroup\$
  • \$\begingroup\$ Can you please make your question more clear? \$\endgroup\$ – Hamza Hasan Jan 22 '16 at 7:54
  • \$\begingroup\$ Visual representation of your question would be more appreciated \$\endgroup\$ – Hamza Hasan Jan 22 '16 at 7:58
  • 1
    \$\begingroup\$ I hope this representaiton helps :) \$\endgroup\$ – Raildex Jan 22 '16 at 15:47
1
\$\begingroup\$

The steps required are;

  1. Calculate velocity based on the speed and the current direction of travel.
  2. Add velocity to position.
  3. Calculate difference between direction of travel and direction to target.
  4. Add the angle to the rotation of the missile.

If the Sprite class is defined as;

public static class Sprite {
    public double rotation;
    public double x;
    public double y;

    public Sprite(double rotation, double x, double y) {
        this.rotation = rotation;
        this.x = x;
        this.y = y;
    }
}

The the logic could look something like this;

    Sprite missile = new Sprite(0, 0, 0);
    Sprite target = new Sprite(0, 100, 100);

    double missileSpeed = 1.0f; // in units/s
    double missileRotationSpeed = 1.0f; // in degrees/s

    // This loops simulates the time running up to 100 seconds, extract the calculations as you see fit
    for(double t = 0; t < 100; t += 0.1f) {
        // First, calculate new position based on direction
        double vx = Math.cos(Math.toRadians(missile.rotation));
        double vy = Math.sin(Math.toRadians(missile.rotation));
        double vl = Math.sqrt(vx*vx + vy*vy); // normalize the velocity

        // Add the velocity to the old position to get the new position
        missile.x += (vx /vl) * missileSpeed * t;
        missile.y += (vy /vl) * missileSpeed * t;

        // Second, calculate the difference in direction compared to the direction to the target
        double dm2tx = target.x - missile.x; // This is the direction from missile to target
        double dm2ty = target.y - missile.y;  
        double dm2tl = Math.sqrt(dm2tx*dm2tx + dm2ty*dm2ty); // Need the length of the vector to normalize

        // angle is signed difference between direction of travel and direction to target
        double angle = Math.toDegrees(Math.atan2(dm2ty / dm2tl, dm2tx / dm2tl) - Math.atan2(vy / vl, vx / vl)); 

        // Snap the angle to the rotation speed
        if (angle < 0) 
            angle = Math.max(-missileRotationSpeed, angle);
        else
            angle = Math.min(missileRotationSpeed, angle);

        missile.rotation += angle * t;
    }

As a side note; you really should look into using some sort of Vector class to represent the coordinates, all of this logic would compress down to a few lines if you had that.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

We can achieve this through trigonometry.

Angle calculation (pseudo code):

public double calculateAngle(double yourX, double yourY, double targetX, double targetY) {
    double distanceX = yourX - targetX;
    double distanceY = yourY - targetY;
    return Math.toDegrees(atan2(distanceY, distanceX));
    //For radians just remove 'Math.toDegrees()' around 'atan2(distanceY, distanceX)'.
}

then you just type this where you update your object:

if(rotation < calculateAngle(yourX, yourY, targetX, targetY))
    rotation += calculateAngle(yourX, yourY, targetX, targetY)/someNumber;
else if(rotation > calculateAngle(yourX, yourY, targetX, targetY))
    rotation -= calculateAngle(yourX, yourY, targetX, targetY)/someNumber;
// the bigger 'someNumber' is the less you rotate every time step.
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I tried to use your method. but somehow the rocket behaves weird. pastebin.com/tqbTtbT8 \$\endgroup\$ – Raildex Jan 25 '16 at 7:44
  • \$\begingroup\$ @Raildex Sorry for the late answer. How is the rocket behaving? Maybe you could link to a short video/gif showing how the rocket behaves? \$\endgroup\$ – Charanor Jan 27 '16 at 21:33
  • \$\begingroup\$ it mostly rotates on the same spot. If the player moves, its rotation jitters but moves more foward instead of rotating towards the player \$\endgroup\$ – Raildex Jan 28 '16 at 10:28
  • \$\begingroup\$ @Raildex try using atan2 instead of atan and tell me if the behavior changes. \$\endgroup\$ – Charanor Jan 28 '16 at 20:46
  • \$\begingroup\$ I'm using atan2 (java) all the time. atan expects only one parameter. \$\endgroup\$ – Raildex Jan 28 '16 at 21:47
0
\$\begingroup\$

I'm not entirely sure I get what you are asking, but here goes.

Mathematically all rotations happen around the origin. So if you want to rotate something around a different point, you'd move it so that the difference from the origin is the same as it would be from that point and then moving it back by the same amount.

Now I think what you are asking is how to rotate one point smoothly so that it ends up on another specific point. Let's call the first point a and the second point b.

You first need to pick a point that has the same distance from both of them. If you are in 2D you can pick any point like this:

//t is some arbitrary value
x=(a.x+b.x)/2+t*(b.y-a.y);
y=(a.y+b.y)/2-t*(b.x-a.x);
p=new Vector(x,y);

In 3D it's slightly more complicated, but you can always find a plane that goes through both points and treat it like it's a 2D problem.

Now if you have your point p that you want to rotate around, you need to figure out the angles here. That's where the atan2 function is super helpful. It tells you the angle that a 2d vector has relative to the positive x axis.

So you get:

startAngle = atan2(a-p);
endAngle = atan2(b-p);

Now you can smoothly rotate from a to b. (Note that you will always rotate counter clockwise if you do this naively, so check if clockwise is faster)

Be advised that you want to figure out how fast to rotate depending on the length of the ark. So you basically want to divide your rotation speed by 2*pi*length(a-p).

Now with all that being said:
If you want to make tracking rockets this is a bad way to go about it.
I think what you actually want is to shoot the tracking rocket in a direction that is slightly off target and let it slowly correct it's path. Potentially allow it to change it's direction faster over time or something. That way your rocket will react much more naturally to for example an enemy spaceship moving after it's been fired instead of looking like it's been glued to an invisible ark attached to the spaceship.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Calculate the angle between the first and the second sprite using trigonometry, then modify the first sprite's angle according to the angle you got.

To calculate the angle between two points, you need to have the distance in the x and y axis between them. x1 and y1 are the positions of the first sprite, x2 and y2 are for the second second sprite:

x = x2 - x1
y = y2 - y1

angle = atan2(y, x) //The correct order is y, x, don't mix them up
/*
Also, it's very important, to use atan2, you could use atan, but then,
it wouldn't return the correct angle, and you would need to handle -x and -y
values differently as x and y values.
*/

After you got the angle you just do

if (angle % (2 * Math.PI) < currentAngle) {
    currentAngle -= 2 * Math.PI / 180;  //Rotates the object with 2 degrees
} else if (angle % (2 * Math.PI) > currentAngle) {
    currentAngle += 2 * Math.PI / 180;
}

I do here angle % (2 * Math.PI), so if the angle is 540, it wouldn't turn in the wrong direction, if the angle between the sprites is 190.

Hope I helped.

BTW.: If you use degrees instead of radians, then always convert the angle to degrees, before you use it. Math.atan2 returns the angle in radians

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.