[Solved] Player Movement based on Mouse Clicked

Hey guys, I’ve been experimenting with moving my player based on Mouse Clicked. But, It always ends up performing in a undesirable manner. I would post my code, But I’ve cycled through so many different variations. That I can’t post them all.

A common problem I’ve noticed is that the player never moves in a straight line. So, If someone who has already written their own implementation of player movement based on mouse clicked could give me a push in the right direction. It would be greatly appreciated, thanks in advance.

Vectors, yo.

I posted about this problem yesterday I believe it was.
Run this code, then check out Player.update()
http://pastebin.java-gaming.org/c78fb8263001d

While that’s probably the best solution, It’s also the least appropriate given my position. I guess I’m stuck on figuring this out on my own. Thanks for trying.

How is it unfit for use may I ask?

It’s hard to explain, Easier to show you. Below you’ll find two classes that handle the player movement, Along with keyboard input. I’m adding the mouse listener similar to the key listener.

Player.class (http://pastebin.com/jzYXKQRa)
Keyboard.class(http://pastebin.com/bFuuuLmF)

Sorry if I skipped any conventions :frowning:

Well, if you’ve separated the axes, you can still adapt a similar method:

if there is a destination: move towards it at [u]speed[/u], but don't surpass it if at destination, remove destination

To do this with your code it might be something like this:


Point destination;

// in tick(), before up/down/left/right checks

if (destination != null) {
    int dx = destination.x - x;
    int dy = destination.y - y;

    if (dx < 0 )
        left = true;
    else if (dx > 0)
        right = true;

    if (dy < 0)
        down = true;
    else if (dy > 0)
        up = true;

    if (dx == 0 && dy == 0)
        destination = null;
}


This is the working code I have right now, Except it only moves the player diagonally.


package project.x;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

public class Mouse implements MouseListener, MouseMotionListener {

    private static int mouseX = -1;
    private static int mouseY = -1;
    private static int mouseB = -1;
    private static int x = Display.getPlayer().get_playerX();
    private static int y = Display.getPlayer().get_playerY();
    private static String xDir, yDir;
    
    public void mouseClicked(MouseEvent e) {
         
    }    


    public void mousePressed(MouseEvent e) {
        mouseB = e.getButton();
        mouseX = e.getX();
        mouseY = e.getY();
        
        if (mouseX > x) { // Move Right
            int xDir = 1;
            Display.getPlayer().right = true;
            Display.playerMoved = true;
            Player.playerDirection = 2;
        } else if (mouseX < x) { // Move Left
            int xDir = 2;
            Display.getPlayer().left = true;
            Display.playerMoved = true;
            Player.playerDirection = 1;
        }
        
        if(mouseY > y) { // Move Down
            int yDir = 0;
            Display.getPlayer().down = true;
            Display.playerMoved = true;
            Player.playerDirection = 0;
        } else if (mouseY < y) { //Move Up
            int yDir = 3;
            Display.getPlayer().up = true;
            Display.playerMoved = true;
            Player.playerDirection = 3;
        }
    }

    public void mouseReleased(MouseEvent e) {
            Display.getPlayer().left = false;
            Display.getPlayer().right = false;
            Display.getPlayer().up = false;
            Display.getPlayer().down = false;
            Display.playerMoved = false;
    }

    public void mouseEntered(MouseEvent e) {
    
    }

    public void mouseExited(MouseEvent e) {
    
    }

    public void mouseDragged(MouseEvent e) {
        mouseX = e.getX();
        mouseY = e.getY();
    }

    public void mouseMoved(MouseEvent e) {
        mouseX = e.getX();
        mouseY = e.getY();
    }

    public static int getX() {
        return mouseX;
    }
    
    public static int getY() {
        return mouseY;
    }
}


You could use brensenham to generate a List to get to the destination, and go from there.

EDIT: Probably something along the lines of traversing the list in a loop until the destination has been reached, or the number of ‘moves per turn’ (aka speed) has been exhausted.
Alternatively built that into the line algorithm and just return the final point.

Having really good click-to-move movement is actually a little bit advanced.

There’s simpler solutions (like what BP posted) but when you start needs to go around complex structures (like a floor plan in a building, a hedge maze, etc), you’ll have to learn how to do pathfinding, and have the character create a path to the destination. Then mixed with that, you implement something like what BP posted to generate “way points” to walk to.

There’s about 50 million ways to do it though. What I do is build a path, then I tell them to walk from one tile to the next on a grid until they reach the destination tile.

But, pathfinding is a advanced topic, I wouldn’t recommend even trying until you get some basic movement down (again, like what BP showed you).

Here’s a super basic movement system that moves an entity from one tile to the another. It’s pretty similar to BP’s (Almost identical really), just written a little differently. Might help to give you multiple perspectives on the same problem.


	protected void walkTo(int destinationX, int destinationY) throws SlickException{
		if (entityY > destinationY){entityY -= speed;}
		else if (entityY < destinationY){entityY += speed;}
		else if (entityX < destinationX){entityX += speed;}
		else if (entityX > destinationX){entityX -= speed;}
	}

Read my topic, I solved this issue in my game now it’s working epicly :smiley:

Implementing pathfinding at the moment and dwamn it’s sexy XD

http://www.java-gaming.org/topics/simple-walkto-x-y-method-for-starter-game/34242/view.html

@Ray
See the problem with that is that the axes are separated the whole way through, so it’s like manhattan distance, and the entity doesn’t move in a straight line.

You might want to take a look at my GObject class. It’s so old (more than 3 years) but is written with swing in mind.


/**
 * Moves this object to a specified point with a specific speed. Note that
 * the velocity used is independent of vertical or horizontal velocities of
 * this object.
 *
 * @param nx The new x-position
 * @param ny The new y-position
 * @param speed The speed with which to move
 * @return True if the new point has been reached
 */
public boolean moveTo(float nx, float ny, float speed)
{
    boolean _x = false;
    boolean _y = false;
  
    int distance = (int) Math.sqrt((double)((x - nx) * (x - nx) + (y - ny) * (y - ny)));
  
    float vel = Math.min(distance, speed);
    float newx = x;
    float newy = y;
  
    if (x > nx)
    {
        // We should move left
        newx -= vel;
    }
    else if (x < nx)
    {
        // We should move right
        newx += vel;
    }
    else
    {
        _x = true;
    }
  
    if (y > ny)
    {
        // We should move up
        newy -= vel;
    }
    else if (y < ny)
    {
        // We should move down
        newy += vel;
    }
    else
    {
        _y = true;
    }

    x = newx;
    y = newy;

    return (_x && _y);
}

I think that helps you. Call it with the coordinates reported by mouse click.

The problem I’m having now, Is that when I click directly left of the player. The player moves left and, up or down according to the position I clicked. I’m trying to get the player to move left and, Have it’s y travel reflect the variation in the mouse click.

Like if the player is at x50 y50 and, I click at x1 y45. Obviously I want the player to move to x1, But currently it will try to goto y1 as well. Because it magnifies the y coordinate for some reason.

Sounds like you have typo in there somewhere. Easy to type x instead of a y, and there’ll be no error.

I still don’t really see why you can’t use my original solution in your code.

Slight modification:


if (destination != null) {
    Point current = position;
         
    double dx = destination.x - x;
    double dy = destination.y - y;
         
    double dist = Math.sqrt(dx * dx + dy * dy);
         
    double dx2 = dx / dist * MOVEMENT_SPEED;
    double dy2 = dy / dist * MOVEMENT_SPEED;
         
    dx2 = Math.min(Math.abs(dx), Math.abs(dx2)) * Math.signum(dx2);
    dy2 = Math.min(Math.abs(dy), Math.abs(dy2)) * Math.signum(dy2);
         
    if (!Double.isNaN(dx2) && !Double.isNaN(dy2)) {
@@        move((int) Math.round(dx2), (int) Math.round(dy2));
    }
         
    // we reached the destination
    if (dist < MOVEMENT_SPEED)
        destination = null;
}  

@BurntPizza - It breaks my original player movement class and, breaks my collision system.

/Edit: I found a work around, Not the greatest hack ever. But, It does the trick. I implemented a variable called jitter, which is equal to 16 currently. So when the mouse is clicked and, The x coordinate or y coordinate is less than the jitter variable. The condition in the if statement that tells the player to move is set to false. In Java this is what it looks like:


        if (mouseX > x) { // Move Right
            int xDir = 1;
            int xVariation = mouseX - x;
            if (xVariation > jitter) {
                Display.getPlayer().right = true;
                Display.playerMoved = true;
                Player.playerDirection = 2;
            }
        } else if (mouseX < x) { // Move Left
            int xDir = 2;
            int xVariation = x - mouseX;
            if (xVariation > jitter) {
                Display.getPlayer().left = true;
                Display.playerMoved = true;
                Player.playerDirection = 1;
            }
        }
        
        if(mouseY > y) { // Move Down
            int yDir = 0;
            int yVariation = mouseY - y;
            if (yVariation > jitter) {
                Display.getPlayer().down = true;
                Display.playerMoved = true;
                Player.playerDirection = 0;
            }
        } else if (mouseY < y) { //Move Up
            int yDir = 3;
            int yVariation = y - mouseY;
            if (yVariation > jitter) {
                Display.getPlayer().up = true;
                Display.playerMoved = true;
                Player.playerDirection = 3;
            }
        }

Then use brensenham like I said:

(copy-paste w/ modification from Wikipedia)

function line(x0, x1, y0, y1)
@@     Point current = new Point(x0, y0);
     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
@@         if (Math.max(Math.abs(x0 - x), Math.abs(y0 - x)) >= SPEED) // traverse line until we've reached length SPEED
@@             return current;
@@         current.x = x;
@@         current.y = y;
         error := error + deltaerr
         if error ≥ 0.5 then
             y := y + 1
             error := error - 1.0

Insert any other collision checks, etc on each loop iteration.

If you want to try the working “as is” demo, The link to download it is posted below;

Project X v1.1.zip

If it works, it works.
Hopefully, as you gain experience, you will see your code evolve from “pile of hacks” to elegance. Happens with any skill.

I agree and, I’m thankful for the suggestions an time you put into helping everyone here. Unfortunately, This was a bug that was unforeseen and, I chose to make a work around. Rather than rewrite a bunch of code. Like you said, The code will evolve over time to something a lot better.