7

I'm trying to move a sprite across the screen in a straight line, towards on the location where've I touched the screen, what i did was upon the update() in each loop , it checks to see if the current sprite's location x y is == to the destination x ,y . if it hasn't sprite's x++ and y++... the thing is ..it ain't moving in a straight line... as there are cases where the x or y coordinate reaches the destination x or y first... how do i changed it so that the both x and y meets the destination together?

my current pseudo code for the sprite object

             destX = destination X
             destY = destination Y

             posX = current X
             posY = current Y
               public void update(){
                if(destX > posX && destY < posY)
                {

                    posX++;
                    posY--;
                }
                else if (destX > posX && destY > posY){
                    posX++;
                    posY++;
                }
                else if(destX < posX && destY > posY)
                {
                    posX--;
                    posY++;
                }
                else if(destX < posX && destY < posY){
                    posX--;
                    posY--;
                }
                else if(destX < posX)
                    posX--;
                else if(destX > posX)
                    posX++;
                else if(destY < posY)
                    posY--;
                else if(destY > posY)
                    posY++;
Shizumaru18
  • 129
  • 1
  • 2
  • 8
  • even with bresenham or any other algorithm it is unavoidable that x or y can reach their target before the other in some cases (suppose the target x is the same as the original x?) – harold Sep 16 '11 at 18:22
  • Bresenham's doesn't work for vertical lines. The main problem with the OPs approach is that he increments x and y at the same rate. – Josh Sep 16 '11 at 18:32
  • @Josh: I'm pretty sure it does, the full version anyway not the half one you showed in your answer. Doesn't matter though, the same holds for target.y = current.y and any other line where the last step is not diagonal. My point is, I'm pretty sure the OP does not actually want what he said he wants. – harold Sep 16 '11 at 18:37
  • @harold Good point. You are probably correct. Its been a while since I actually did this type of thing. – Josh Sep 16 '11 at 18:40
  • hmm.. well its sort of like those rts games where the user right clicks on a destination and a unit goes there in a straight line but minus the pathing system.. – Shizumaru18 Sep 16 '11 at 18:58
  • so you want that line to be as straight as possible, and avoid the "diagonal part and straight part", right? Bresenham should be fine. – harold Sep 16 '11 at 19:19

3 Answers3

6

Check out: http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

This simple algorithm will tell you each X,Y coordinate on a line between two points. You could use this algorithm to compute all of the positions it needs to visit, store the coordinates in an array, and iterate over the array as you update the position.

From the Article:

  function line(x0, x1, y0, y1)
         int deltax := x1 - x0
         int deltay := y1 - y0
         real error := 0
         real deltaerr := abs (deltay / deltax)    // Assume deltax != 0 (line is not vertical),
               // note that this division needs to be done in a way that preserves the fractional part
         int y := y0
         for x from x0 to x1
             plot(x,y)
             error := error + deltaerr
             if error ≥ 0.5 then
                 y := y + 1
                 error := error - 1.0

This is the most primitive version. The article contains a better generalized algorithm that you should look at.

Josh
  • 376
  • 1
  • 8
  • do i have to store the last current x y coordinate before getting the latest destination x y ? – Shizumaru18 Sep 16 '11 at 18:23
  • So at the beginning, you know where you are and where you need to go. Build a method based on the above algorithm that returns an array of java.awt.Points (for example). Then your update method would simple set current x and y to the values of the ith entry in the array and then update your index; – Josh Sep 16 '11 at 18:29
  • BTW: This is not Java like he specified. – tasteslikejava Jan 20 '14 at 13:51
2

I am dealing with a similair problem as yours. (I have an arraylist holding the history of positions my player has gone and I want to use that to rewind the game.) Instead of simply increasing x and y position with 1 you can:

  1. Calculate the angle between the source postion and your destination position.
  2. Calculate the new direction using a variable which represents the speed
  3. Update your postion using calculated direction

I made a class of that. I hope it is usefull.

import java.awt.geom.Point2D;

public class MyVelocityCalculator {

    public static void main(String[] args) {
        Point2D.Double currentPosition = new Point2D.Double();
        Point2D.Double destinationPosition = new Point2D.Double();
        currentPosition.setLocation(100, 100);
        destinationPosition.setLocation(50, 50);
        Double speed = 0.5;
        Point2D.Double nextPosition = MyVelocityCalculator.getVelocity(currentPosition, destinationPosition, speed); 

        System.out.println("player was initially at: "+currentPosition);
        System.out.println("player destination is at: "+destinationPosition);
        System.out.println("half seconds later player should be at: "+nextPosition);

    }

    public static final Point2D.Double getVelocity(Point2D.Double currentPosition, Point2D.Double destinationPosition, double speed){
        Point2D.Double nextPosition = new Point2D.Double();
        double angle = calcAngleBetweenPoints(currentPosition, destinationPosition);
        double distance = speed;
        Point2D.Double velocityPoint = getVelocity(angle, distance);
        nextPosition.x = currentPosition.x + velocityPoint.x;
        nextPosition.y = currentPosition.y + velocityPoint.y;
        return nextPosition;
    }

    public static final double calcAngleBetweenPoints(Point2D.Double p1, Point2D.Double p2)
    {
        return Math.toDegrees( Math.atan2( p2.getY()-p1.getY(), p2.getX()-p1.getX() ) );
    }

    public static final Point2D.Double getVelocity(double angle, double speed){
        double x = Math.cos(Math.toRadians(angle))*speed;
        double y = Math.sin(Math.toRadians(angle))*speed;
        return (new Point2D.Double(x, y));
    }
}
J. Rahmati
  • 735
  • 10
  • 37
1

Don't use integers. This is a very bad idea to work with ints. Use floats. The main concept is: define the number of steps you want to perform (s). Compute differences in X and Y (diffX and diffY). Don't take absolute values: Compute them this way

float diffX = destX - currentX;

Then compute the xMove and yMove values by dividing diffX and diffY by s (number of steps).

float moveX = diffX / s;
float moveY = diffY / s;

And now you have to add for each iteration the moveX and moveY values to the current position.

And for drawing it, you should use Graphics2D, which supports floating points. If you don't want to use Graphics2D, you can round the floats to ints, using Math.round(float).

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • 1
    `double` gives you more precision than you need for GUI coordinates. It'd just be a waste of 4 bytes per variable. – David Z Sep 16 '11 at 18:34
  • Are you kidding me? The algorithm you show is not even correct, precisely due to the usage of floats. Even though `s * movex` might equal `diffx`, the sum of `s` `movex`'s almost never will. – harold Sep 16 '11 at 18:41
  • When using Graphics2D (Java AWT graphics library) to render things, it gives you the ability to specify positions in floating point. This means that the algorithm Martijn provided will be sufficient in most cases. It breaks down when you are limited to working in integer coordinate systems. Bresenham's is essentially the same thing with an error correction bit. – Josh Sep 16 '11 at 18:45
  • @Josh sufficient perhaps, but subtly wrong nonetheless. For every fraction which isn't exactly representable as a double (or float) it won't actually reach the destination, even if the error is essentially invisible. – harold Sep 16 '11 at 18:49
  • @harold You are quite right. In cases where you actually care, you could skip the last iteration and directly set the position to the final destination. This would mean that your final iteration would move slightly further or shorter than the previous iterations, thus absorbing the accumulated error. Without more context, its difficult to know just how precise the algorithm needs to be. It would seem he is working in an integer coordinate system, so this whole discussion is probably moot. – Josh Sep 16 '11 at 18:54