4

I'm trying to move a square from it's original position to the coordinates of my mouse when I click. The code I have somewhat works, but the square does not go directly to the mouse click. It goes diagonally a bit off, then it goes to the mouse click, as shown in the picture. I tried to make it a straight movement path but I could not think of a way to do so. I'm pretty sure the error is something to do with the 1st method below.

           ________X ← the mouse click
          /        
         /   ↑
        / ←  = movement path
       /
      /
_____/
|   | ← character
|___|

Here are the 3 methods involved so far (pls dont criticize my code too much)

 //int x and int y are the positions of the mouse, champion = the character

public static Champion calculateChampionMovement(int x, int y, Champion champion) {

    if (x != champion.x || y != champion.y) {

         //x and y dist = the distance between the character and the cursor
        int xDist = x - champion.x;
        int yDist = y - champion.y;

         //the angle
        plrAngle = Math.atan2(yDist, xDist) * 180 / Math.PI;

         //the speed of the character on the x and y axis
         //(character diagonally moves at the speed of "champion.speed")
        plrXSpeed = champion.speed * Math.cos(plrAngle * Math.PI / 180);
        plrYSpeed = champion.speed * Math.sin(plrAngle * Math.PI / 180);

         //calls the method below that actually moves the character
        playerMain.champion = (Champion) Entity.moveChampions(x, y, champion, champion.speed, plrXSpeed, plrYSpeed);

        champion.moving = true;
    }
    return playerMain.champion;
}

And the 2nd one...

 //called by the method above
public static Entity moveChampions(int x, int y, Champion champion, float speed, double xSpeed, double ySpeed) {

     //if the distance between the character on the x and y axis is not
     //exactly divisible by "speed", then this helps the character stop.
    if (Math.abs(x - champion.x) <= speed) {
        champion.x = x;
    }
    if (Math.abs(y - champion.y) <= speed) {
        champion.y = y;
    }
     //stops the character
    if (x == champion.x && y == champion.y) {
        champion.moving = false;
    }
     //moves the character
    if (champion.moving) {
        champion.x += xSpeed;
        champion.y += ySpeed;
    }
    return champion;
}

The last method calls "calculateChampionMovement" and "moveChampions", and it moves the character while "moving" is true

public static void buttonTest() {
    if (RIGHTCLICK == true) {

         //mouse x and y positions
        cursorClickX = (int) (mapX + MOUSE_X);
        cursorClickY = (int) (mapY + MOUSE_Y);

         //first method (setup the x and y speed)
        playerMain.champion = PlayerMain.testMainChampionMove(cursorClickX, cursorClickY, playerMain.champion);

     // if character is already moving
    } else if (playerMain.champion.moving == true) {
         //move the character
        playerMain.champion = (Champion) Entity.moveChampions(cursorClickX, cursorClickY, playerMain.champion, champAsdf.speed, plrXSpeed, plrYSpeed);
    }
}

hi riot games pls dont sue me im too young anyways

  • Consider providing a [runnable example](https://stackoverflow.com/help/mcve) which demonstrates your problem. This is not a code dump, but an example of what you are doing which highlights the problem you are having. This will result in less confusion and better responses – MadProgrammer Jun 25 '15 at 05:43
  • "Off the top of my head" - You need to calculate the amount of vertical/horizontal movement you need to make over the time you want to move and update the position by those deltas – MadProgrammer Jun 25 '15 at 05:44
  • Why do you convert angles from rad to deg and vice versa? It's about `180` and `Math.PI` – skozlov Jun 25 '15 at 05:54
  • Don't you think you should move k spaces at a time?... and I guess there has to be a loop. k may range from 1 to any significantly small number compared to the distance. – dharam Jun 25 '15 at 06:08
  • ok ill make a runnable example next time thanks for the advice – Justin Chan Jun 25 '15 at 21:49

2 Answers2

6

When faced with a problem, I tend to go back to basics, what do I know how to do?

I know I can:

  • Calculate the distance between two points
  • Move an object a long a line over a given duration.

So we know:

  • Start point
  • End point

From this we can calculate the distance between the two points.

double distance = Math.sqrt(
                    (startX - targetX) * (startX - targetX)
                    + (startY - targetY) * (startY - targetY));

With this, we can calculate the time needed to travel between the two points based on the desired speed

time = distance / speed

where speed is a constant (0.1 in my example, make it smaller to make it slower)

With this information, we know how long we must travel for and we can calculate the progress along the line/path based on the difference between when we started (when the mouse was clicked) and now.

Assuming the startTime is the time that we started moving, runningTime is the amount of time we need to run for in order to maintain a constant speed

Then we can calculate our current progress using something like...

long duration = System.currentTimeMillis() - startTime;
double progress = duration / runTime;

From this we can calculate the position along the line based on the current duration...

double x = (int) (startX + ((targetX - startX) * progress));
double y = (int) (startY + ((targetY - startY) * progress));

MoveAlongLine

As a proof of concept. Sorry, you didn't mention what framework your were using ;)

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestMove {

    public static void main(String[] args) {
        new TestMove();
    }

    public TestMove() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Rectangle champion;
        private Line2D path;

        private double speed = 0.1;

        private Timer timer;
        private Long startTime;

        private double targetX, targetY;
        private double startX, startY;
        private double runTime;

        public TestPane() {
            champion = new Rectangle(95, 95, 10, 10);

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    timer.stop();
                    calculateChampionMovement(e.getX(), e.getY(), champion);
                    startTime = System.currentTimeMillis();
                    timer.start();
                }
            });

            timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (targetX == champion.getCenterX() && targetY == champion.getCenterY()) {
                        System.out.println("Stopped as same");
                        timer.stop();
                    }

                    long duration = System.currentTimeMillis() - startTime;
                    double progress = duration / runTime;

                    if (progress >= 1.0) {
                        System.out.println("Stopped out of time");
                        progress = 1.0;
                        timer.stop();
                    }

                    double x = (int) (startX + ((targetX - startX) * progress));
                    double y = (int) (startY + ((targetY - startY) * progress));

                    // x/y are the center points, need to adjust them so the shape
                    // moves about the center point
                    champion.setRect(x - 5, y - 5, 10, 10);

                    repaint();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.draw(champion);
            if (path != null) {
                g2d.setColor(Color.RED);
                g2d.draw(path);
            }
            g2d.dispose();
        }

        public void calculateChampionMovement(double x, double y, Rectangle champion) {

            if (x != champion.getCenterX() || y != champion.getCenterY()) {

                targetX = x;
                targetY = y;

                startX = champion.getCenterX();
                startY = champion.getCenterY();

                path = new Line2D.Double(
                                champion.getCenterX(),
                                champion.getCenterY(),
                                x, y);

                double distance = Math.sqrt(
                                (startX - targetX) * (startX - targetX)
                                + (startY - targetY) * (startY - targetY));

                runTime = distance / (double)speed;

            }
        }
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
1

Using the pythagorian theorem we can calculate the distance D ie sqrt((targetx-startx)^2+(target-starty)^2).

Using your velocity variable (ill call it V)we can calculate a time variable T as D/V ie distance over velocity.

We can then get the separate x and y velocity as xdistance/T and ydistance/T or (targetx-startx)/T and (targety-startat)/T (Note that an exception for if T == 0 must be implementerad to avoid errors)

Using time fragments to calculate the x and y velocity so that the total velocity remains the same is computationally unnecessary with the only upside being that we avoid dividing by zero but then again that is easily fixable with a single if statement(if(T != 0.0)).

Using this or similar methods will make your program run faster as less computations are needed. The time save is too small to be noticeable with modern computers but it keeps the code cleaner. Good luck with your project.