Smooth Tile by tile movement

Im having trouble with “smooth” tile by tile movement. I’ve got the basics down but on the very first one it stops after it reaches its point and continues on smoothly. I was wondering if thats because of the way I check if a key is pressed or if its the movement itself. Both the keyboard class and the player class are down below. I was hoping someone could help me with this issue. Thank you :).


package net.gasquake.game.input;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Keyboard implements KeyListener {

	public int keyDown, keyUp;
	
	public boolean keyDown(int k) {
		if (k != this.keyDown) {
	      return false;
	    }
	    this.keyDown = 0;
	    return true;
	}
	
	public boolean keyUp(int k) {
		if (k != this.keyUp) {
	      return false;
	    }
	    this.keyUp = 0;
	    return true;
	}
	
	public void keyPressed(KeyEvent e) {
		this.keyDown = e.getKeyCode();
	}
	  
	public void keyReleased(KeyEvent e) {
		this.keyUp = e.getKeyCode();
	}
	  
	public void keyTyped(KeyEvent e) {}
	
}


package net.gasquake.game.entities;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import net.gasquake.game.Zaffre;
import net.gasquake.game.entities.core.Entity;

public class Player extends Entity {

	private boolean lockedMovement = false;
	private int oldX, oldY;
	
	public Player(int x, int y, Zaffre zaffre) {
		super(x, y, zaffre);
		w = 64; h = 64;
	}
	
	public void update() {
		if (oldX == x || oldY == y) {
			if ((x % 64 == 0 && y % 64 == 0)) {
				lockedMovement = false;
				dx = 0; dy = 0;
			}
		}
		if (this.oldX != this.x) {
			this.oldX = this.x;
		}
		if (this.oldY != this.y) {
			this.oldY = this.y;
		}
		if (!lockedMovement) {
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_LEFT)) {
				dx = -4;
				lockedMovement = true;
			}
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_RIGHT)) {
				dx = 4;
				lockedMovement = true;
			}
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_UP)) {
				dy = -4;
				lockedMovement = true;
			}
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_DOWN)) {
				dy = 4;
				lockedMovement = true;
			}
			if (zaffre.getKeyboard().keyUp(KeyEvent.VK_LEFT)) {
				dx = 0;
			}
			if (zaffre.getKeyboard().keyUp(KeyEvent.VK_RIGHT)) {
				dx = 0;
			}
			if (zaffre.getKeyboard().keyUp(KeyEvent.VK_UP)) {
				dy = 0;
			}
			if (zaffre.getKeyboard().keyUp(KeyEvent.VK_DOWN)) {
				dy = 0;
			}
		}
		move(dx, dy);
	}
	
	public void render(Graphics g) {
		g.setColor(Color.white);
		g.fillRect(x, y, w, h);
	}

}

Can anyone tell me how to remedy this problem? Thanks btw I hope I made this thread correctly. Im new.

It sounds like an issue with the keyboard input using the same type of input as you would use for typing, which is no good for testing which arrow keys are pressed, etc in a game.

I dont know what type of input handler is in awt, but theres probably another one that works differently and more precicely.

Wait do you mean a library? I would like to stay away from libraries if possible. If you mean a different method I can’t seem to find one for some reason. Maybe its just me being dumb. I should probably look on Youtube. If you have any suggestions that would be great.

I FIXED IT. thanks liquid. :slight_smile:

It would be more nicer if you could share your findings.

Oh ok lol.

Well I made a “clickingArrows” boolean and I set it to true if you were and false if you weren’t. When you check when you are on a tile and you should set dx & dy to 0 you also check if they are clicking arrows. If they are don’t set dx and dy to 0.

Hope that helped :).

so it wasnt the key repeat thing? i think i need more sleep…

I guess not lol. But now im having this problem where I can get out of the grid even though im locking the input… hmmm.

you just have to limit the players position. if its less than 0, make it 9, if its bigger than the map size, make it =mapsize, etc.
you can either do that after the movement has been updated each frame, or check it before allowing a keypress to be completed.

Im not talking about the boundaries of the screen if thats what you mean. Im talking about the 32x32 “box” that the player moves along the grid. This is really weird. I’ve never done a movement system like this before.

i see, does it have that problem constantly, or just some of the time?

(When constantly moving across multiple tiles) When you go in any direction and then you stop moving midway it doesn’t finish the tile. It just stops

so when you are at the top of the update method, oldX will always == x and oldY will always == y right? so you might be able to just remove all that unless you need it somewhere else. or unless you should only set oldX and oldY when a key is pressed?

the key up events dont appear to be able to be triggered unless movement isnt locked… but maybe theyre causing the problem, cos movement shouldnt be stopped by releasing a key, it should only be stopped by the character moving into grid-snap position.

Something like this?

You can check out how I did that tile movement from Pokémon here., particularly where the tick() method is on line 758. Just use Ctrl+F to navigate around.

Ok this is like the weirdest thing. Now im having the same problem as I did at first but I fixed the other one. This is so awkward.

Heres by code:


package net.gasquake.game.entities;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import net.gasquake.game.Zaffre;
import net.gasquake.game.entities.core.Entity;

public class Player extends Entity {
  
	private boolean lockedMovement = false, clickingArrows = false;
	private String direction = "null";
	
	public Player(int x, int y, Zaffre zaffre) {
		super(x, y, zaffre);
		w = 64; h = 64;
	}
  
  	public void update() {
  		if (x % 64 == 0 && y % 64 == 0) {
  			if (!clickingArrows || dy > 0 && dx > 0) {
  				dx = 0; dy = 0;
  			}
  			direction = "null";
  			lockedMovement = false;
  		}
  		input();
  		if (direction == "left") {
  			dx = -4;
  			dy = 0;
  		}
		if (direction == "right") {
		  	dx = 4;
		  	dy = 0;
		}
		if (direction == "up") {
			dx = 0;
			dy = -4;
		}
		if (direction == "down") {
			dx = 0;
			dy = 4;
		}
		if (dx > 0) {
			dy = 0;
		}
		if (dy > 0) {
			dx = 0;
		}
  		move(dx, dy);
  	}
  
  	public void render(Graphics g) {
  		g.setColor(Color.white);
  		g.fillRect(x, y, w, h);
  	}
  	
  	public void input() {
  		if (!lockedMovement) {
  			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_LEFT)) {
	  			lockedMovement = true;
	  			clickingArrows = true;
	  			direction = "left";
  			}
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_RIGHT)) {
			  	lockedMovement = true;
	  			clickingArrows = true;
	  			direction = "right";
			}
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_UP)) {
				lockedMovement = true;
	  			clickingArrows = true;
	  			direction = "up";
			}
			if (zaffre.getKeyboard().keyDown(KeyEvent.VK_DOWN)) {
				lockedMovement = true;
	  			clickingArrows = true;
	  			direction = "down";
			}
  		}
		if (zaffre.getKeyboard().keyUp(KeyEvent.VK_LEFT)) {
			clickingArrows = false;
  		}
		if (zaffre.getKeyboard().keyUp(KeyEvent.VK_RIGHT)) {
		  	clickingArrows = false;
		}
		if (zaffre.getKeyboard().keyUp(KeyEvent.VK_UP)) {
			clickingArrows = false;
		}
		if (zaffre.getKeyboard().keyUp(KeyEvent.VK_DOWN)) {
			clickingArrows = false;
		}
  	}
  	
}

Does anyone see how im being dumb? lol

There’s a hidden bug in your code snippet. Have you ever tried pressing the inputs very fast? Pressing two different combinations of inputs very fast? You may want to consider that.

I fixed that problem in my project by returning upon inside the IFs, and delegating the input logics to a separate method.

Yes I see and I added the return thing. Im still having the first movement “choppy” effect though?

Yeah, I should have probably did that post after all. I deleted it right after you said you solved the issue. I regret deleting it because, you just forget everything you wrote. :cranky:

Anyway, I was going to show you this program I wrote. It is a Pac-man clone, it is big, and it is totally written in Java2D. But, it covers a lot of the problems you spoke of here.

Alpha Java Pac-man Build

In reality, the main two things you need for smooth movement is interpolation (making the image move a small distance between each node) and a steady timer (making sure that collisions are handled correctly and you are using delta to control movements). In JPac-man though, I don’t actually use delta… but it’d be far easier. For collision, I binded it directly to the tiles.

I recommend just looking directly at the source and trying to soak it all in, there is a lot of good stuff that deals with collision detection, interpolation, and proper movement timing. The jar is completely stand alone, and should run right out the box. The jar, assets, and source are in the zip download just in case something goes wrong. I just thought I’d share it, just in case someone wanted a fully working example of the issue presented in Java2D. There is a lot of good information in this thread regarding this issue though, so it was probably not necessary… :-\