[SOLVED minor bugs] Tile based movement (Bomberman style)

So I have this tile based game, with each tile being 20x20 pixels, and I draw them on a canvas in a loop like this:



for(int y = 0; y < 24; y++){
			for(int x = 0; x < 42; x++){
				int yOffset = 0;
				position.setX(x + player.getPosition().getX()-21);
				position.setY(y + player.getPosition().getY()-12);
                                //drawing stuff from positions
}


I now wonder how to make my player move smoothly from tile to tile when I press a button on my keyboard
Any ideas how to start? To get me going? :slight_smile:

When the player presses a key to move, figure out where on the next tile the player is to stop, this is your destination X and Y. Each loop move him closer to that position, Ignore movement key presses until the player’s X and Y are equal to the destination X and Y.

That part I have done, I can move from tile to tile, but I can’t make it smooth movement :slight_smile: Sorry for not being clear about that :slight_smile:

as gbeebe suggest, try (pseudocode!):

boolean moveUp = false;
boolean moveDown = false;
boolean moveLeft = false;
boolean moveRight = false;
Point current = null;
Point target = null;
int stepX = TILE_SIZE_HORIZONTAL;  // put your number here
int stepY = TILE_SIZE_VERTICAL;  // put your number here

gameLoop(){
  if (!playerMoving){
     checkPlayerInput();
  } else {
     updatePlayerPosition();
  }
}

checkPlayerInput(){
  if (player press UP){
     moveUp = true;
     move(0,-stepY);
  } else   if (player press DOWN){
     moveDown = true;
     move(0,stepY);
  } else   if (player press LEFT){
     moveLeft = true;
     move(-stepX,0);
  } else   if (player press RIGHT){
     moveRight = true;
     move(stepX,0);
  }
}


move(int dx, int dy){
  current = player.getCurrentPoint();
  target = new Point(current.x + dx, current.y +dy);
}

To update smoothly player position, my suggestion is to have some sort of Tween class.
I have implemented a Tween logic for MarteEngine, see here TweenTest and here a webstart example. Let me know if you need help!

Thanks so far, I will look into your suggestions :slight_smile:

General approach described by others is ok. However, make sure you implement some sort of time based movement rather then pixel based.

So, rather then saying every update move the character 1px you’d want to implement something like move character 1 tile / second. Usually you’d have a delta time between the last update and the current one.

As Gornova suggested there are stacks of tween type libs out there that will make this stuff trivial. I suggest however that for basic character movement you just implement your own.

If you need more advanced tweening stuff have a look at http://code.google.com/p/java-universal-tween-engine/. It’s brilliant.

Cheers,
Bach

@bach: java Universal Tweet engine is great, thanks for link!

I really want to implement all of the game myself, not using pre-coded stuff, but thanks for the link, will look into it… :slight_smile:

But I think this is a really hard part to do, I might be back for more help :slight_smile:

This is my move method so far, it’s kind of teleporting from one tile to another… I can’t figure out to do those tweens, as my draw method draws in tiles, and not pixels. How do I get started? :slight_smile:



public boolean moveUnitTo(Position from, Position to) {
		if(isAllowedUnitPosition(to)){
			Unit unit = this.tileMap.getUnitAt(from);
			this.tileMap.removeUnit(from);
			unit.setDirection(new Vector(to.getX()  + from.getX(), to.getY() + from.getY()));
			unit.setPosition(to);
			this.tileMap.addUnitAt(to, unit);
			this.tileMap.getTileAt(from).setOccupied(false);
			this.tileMap.getTileAt(to).setOccupied(true);
			return true;
		}
		return false;
	}


unit.setPosition(to) sets the unit on the next tile, no? You need to just set the unit’s position (say) 1 pixel towards the destination.

Yes it does, it is that which gives the teleporting effect, but I think my problem is, that I draw not in pixels, but tiles:



	public void render() {
		Graphics2D bbg = (Graphics2D) this.backBuffer.getGraphics();
		BufferedImage image = null;

		this.player = this.game.getTileMap().getPlayer();
		this.inventory = player.getInventory();
		nullTile = new Tile(position, "WATER");
		for(int y = 0; y < 24; y++){
			for(int x = 0; x < 42; x++){
				int yOffset = 0;
				position.setX(x + player.getPosition().getX()-21);
				position.setY(y + player.getPosition().getY()-12);
				//Draw tiles
				Tile tile = this.game.getTileMap().getTileAt(position);
				if(tile != null){
					image = tile.getImage();
				}else{
					image = nullTile.getImage();
				}

				//Draw items
				Item item = this.game.getTileMap().getItemAt(position);
				if(item != null){
					image = item.getImage();
				}

				//Draw buildings
				Building building = this.game.getTileMap().getBuildingAt(position);
				if(building != null){
					image = building.getImage();
				}

				//Draw units
				Unit unit = this.game.getTileMap().getUnitAt(position);
				if(unit != null){
					image = unit.getImage();
					yOffset = -40; //Unit height
				}

				bbg.drawImage(image, x*20, y*20+yOffset, this);

			}
		}

		bbg.drawImage(inventory.getImage(), 400, 480, this);

		bbg.setColor(Color.WHITE);
		bbg.fillRect(0, 580, 40, 20);
		bbg.setColor(Color.BLACK);
		bbg.drawString(String.valueOf(gui.getFps()), 10, 595);

		bbg.dispose();
	}


Or am I totally wrong? :stuck_out_tongue:

I really want a bomberman like movement. If you press a movement key, then it animates a movement between tiles…

Yes, that is why. make an array for your untis:

ArrayList<Unit> unit = new ArrayList();

Unit can have:

int currentX;
int currentY;
int currentTileX;
int currentTileY;
int destTileX;
int destTileY;

Create your unit:

unit.add(new Unit);
int lastUnit = unit.size() -1;

Then you can set the Unit’s properties:

unit.get(lastUnit).currentTileX = tx;
unit.get(lastUnit).currentX = tx * yourTileWidht;

//Draw units
for (int uc = 0; uc < unit.size(); uc++)
{
    image = unit.getImage();
    yOffset = -40; //Unit height
    bbg.drawImage(image, unit.get(uc).currentX, unit.get(uc).currentY*20+yOffset, this);
}

the drawing of units would go right above:


bbg.drawImage(inventory.getImage(), 400, 480, this);

I’m not going to code the whole thing for you, but for starters: To move the unit towards the destination tile, simply add or subtract from the unit’s currentX or currentY.

Hey,

I’d recommend you dig into some of the underlying math behind this kind of stuff. Once you know how to solve this mathematically I don’t think you’d have any problems coding it.

Have a read through this tutorial here: http://blog.wolfire.com/2009/07/linear-algebra-for-game-developers-part-1/

It beautifully explains how to work with Vectors etc. Basic requirements needed for any kind of game.

Cheers,
Bach

This is now my render method:



	public void render() {
		Graphics2D bbg = (Graphics2D) this.backBuffer.getGraphics();
		BufferedImage image = null;

		this.player = this.game.getTileMap().getPlayer();
		this.units = this.game.getTileMap().getUnits();
		this.inventory = player.getInventory();
		nullTile = new Tile(position, "WATER");
		for(int y = 0; y < 24; y++){
			for(int x = 0; x < 42; x++){
				position.setX(x + player.getPosition().getX()-xOffset);
				position.setY(y + player.getPosition().getY()-yOffset);
				//Draw tiles
				Tile tile = this.game.getTileMap().getTileAt(position);
				if(tile != null){
					image = tile.getImage();
				}else{
					image = nullTile.getImage();
				}

				//Draw items
				Item item = this.game.getTileMap().getItemAt(position);
				if(item != null){
					image = item.getImage();
				}

				//Draw buildings
				Building building = this.game.getTileMap().getBuildingAt(position);
				if(building != null){
					image = building.getImage();
				}

				//Old draw units
				/**
				Unit unit = this.game.getTileMap().getUnitAt(position);
				if(unit != null){
					image = unit.getImage();
					yOffset = -40; //Unit height
				}
				**/
				bbg.drawImage(image, x*tileDimension, y*tileDimension, this);

			}
		}
		
		for(Unit unit : units){
			image = unit.getImage();
		    bbg.drawImage(image, unit.getPosition().getX() + unit.getCurrentPixelX() + (xOffset * tileDimension), unit.getPosition().getY() + unit.getCurrentPixelY() + (yOffset * tileDimension) - unitHeightOffset, this);
		}

		bbg.drawImage(inventory.getImage(), 400, 480, this);

		bbg.setColor(Color.WHITE);
		bbg.fillRect(0, 580, 40, 20);
		bbg.setColor(Color.BLACK);
		bbg.drawString(String.valueOf(gui.getFps()), 10, 595);

		bbg.dispose();
	}


My move method:



	public boolean moveUnitToAnimated(Position from, Position to){
		Tween tween = new Tween();
		if(isAllowedUnitPosition(to)){
			Unit unit = this.tileMap.getUnitAt(from);
			this.tileMap.removeUnit(from);
			unit.setDirection(new Vector(to.getX()  + from.getX(), to.getY() + from.getY()));
			//Going left
			if(to.getX() < from.getX() && to.getY() == from.getY()){
				tween.tweenUnitLeft(unit);
			}
			//Going right
			else if(to.getX() > from.getX() && to.getY() == from.getY()){
				tween.tweenUnitLRight(unit);
			}
			//Going up
			else if(to.getX() == from.getX() && to.getY() < from.getY()){
				tween.tweenUnitUp(unit);
			}
			//Going down
			else if(to.getX() == from.getX() && to.getY() > from.getY()){
				tween.tweenUnitDown(unit);
			}
			this.tileMap.addUnitAt(to, unit);
			this.tileMap.getTileAt(from).setOccupied(false);
			this.tileMap.getTileAt(to).setOccupied(true);
			return true;
		}
		return false;
	}


And my tween class:



public class Tween {

	public void tweenUnitLeft(Unit unit){
		for(int i = 20; i > 0; i--){
			unit.setCurrentPixelX(i);
		}
		unit.setPosition(new Position(unit.getPosition().getX() - 1, unit.getPosition().getY()));
		unit.setCurrentPixelX(0);
	}
	
	public void tweenUnitLRight(Unit unit){
		for(int i = 0; i < 0; i++){
			unit.setCurrentPixelX(i);
		}
		unit.setPosition(new Position(unit.getPosition().getX() + 1, unit.getPosition().getY()));
		unit.setCurrentPixelX(0);
	}
	
	public void tweenUnitUp(Unit unit){
		for(int i = 20; i > 0; i--){
			unit.setCurrentPixelY(i);
		}
		unit.setPosition(new Position(unit.getPosition().getX(), unit.getPosition().getY() - 1));
		unit.setCurrentPixelY(0);
	}
	
	public void tweenUnitDown(Unit unit){
		for(int i = 0; i < 20; i++){
			unit.setCurrentPixelX(i);
		}
		unit.setPosition(new Position(unit.getPosition().getX(), unit.getPosition().getY() + 1));
		unit.setCurrentPixelY(0);
	}
}


Is that anything what you meant? :stuck_out_tongue:

So I’m back to square one, tried to draw units in a separate loop, but couldn’t get it to work. Is there any other approach, or would you get me started at drawing them the correct way?

This is my “old” render method:



	public void render() {
		Graphics2D bbg = (Graphics2D) this.backBuffer.getGraphics();
		BufferedImage image = null;

		this.player = this.game.getTileMap().getPlayer();
		this.inventory = player.getInventory();
		nullTile = new Tile(position, "WATER");
		this.unitHeightOffset = 0;
		for(int y = 0; y < 24; y++){
			for(int x = 0; x < 42; x++){
				int unitHeightOffset = 0;
				position.setX(x + player.getPosition().getX() - this.xOffset);
				position.setY(y + player.getPosition().getY() - this.yOffset);
				//Draw tiles
				Tile tile = this.game.getTileMap().getTileAt(position);
				if(tile != null){
					image = tile.getImage();
				}else{
					image = nullTile.getImage();
				}

				//Draw items
				Item item = this.game.getTileMap().getItemAt(position);
				if(item != null){
					image = item.getImage();
				}

				//Draw buildings
				Building building = this.game.getTileMap().getBuildingAt(position);
				if(building != null){
					image = building.getImage();
				}

				//Draw units
				Unit unit = this.game.getTileMap().getUnitAt(position);
				if(unit != null){
					image = unit.getImage();
					unitHeightOffset = -40; //Unit height
					unit.setVisible(true);
				}

				bbg.drawImage(image, x*tileDimension, y*tileDimension+unitHeightOffset, this);

			}
		}
		

		bbg.drawImage(inventory.getImage(), 400, 480, this);

		bbg.setColor(Color.WHITE);
		bbg.fillRect(0, 580, 40, 20);
		bbg.setColor(Color.BLACK);
		bbg.drawString(String.valueOf(gui.getFps()), 10, 595);

		bbg.dispose();
	}


What’s your unit class look like?

This is my unit class, and the getters and setters for each field:



public abstract class Unit implements WorldObject{
	private Position currentPosition;
	private Position destinationPosition;
	private String name;
	private String type;
	private int strength;
	private int agility;
	private int vitality;
	private int xp;
	private int level;
	private int chips;
	private double health;
	private double attackDamage;
	private double dodgeRating;
	private int luck;
	private int attackRange;
	private double maxHealth;
	private String unitClass;
	private Vector direction;
	private int energy;
	private BufferedImage image;
	private int currentPixelX;
	private int currentPixelY;
	private boolean visible;
	private boolean isMoving;

	public Unit(Position p, String name, String type) {
		this.currentPosition = p;
		this.setCurrentPixelX(0);
		this.setCurrentPixelY(0);
		this.name = name;
		this.type = type;
		this.direction = new Vector(1,0);
		loadImage();
	}

	private void loadImage() {
		this.image = null;
		try{
			this.image = ImageIO.read(new File("bin/image/unit/" + this.type + ".png"));
		}catch (Exception e) {
			System.out.println("Couldn't find " + this.type + "'s image file");
		}
	}
}

Maybe you could put a headTowards function in your Unit class. which would simply store the (x, y) the the next tile to go to call this destinationX and destinationY. Then have an advance() command in the Unit class that would simply check to see if the currentPixelX and Y is equal to the destinationX and Y. If not, add or subtract 1 from currentPixelX or Y . NO LOOPS GO IN THAT CODE. When drawing you draw the unit at the currentPixelX and Y.

[EDIT] Well it kinda works!

My only problem is that it never stops moving now :S
Can’t figure out where to put the check if the destination is reached…?

If you implemented it the way I said to, it should work how you want.

in pseudo code your main loop moves the units:


unit.advance()  //will move the unit, if it hasn't reached the destination yet.
if (player is pressing UP)
{
    if (unit.currentPixelX = unit.destinationX && unit.currentPixelY = unit.destinationY)
    {
      unit.headTowards(newX, newY);
    }
}

The player only has to tap UP to start the unit movement, if the unit is ready to move again(by checking to see if it’s resting on its destination). In the .advance() function, you should first check to see if the unit has arrived at it’s destination… If not, move one pixel towards it. If it has reached it destination, do nothing.

I wouldn’t worry about fancy tweening or smooth/time based movement until you at least get this right.