Add additional status images to tiles in a tilemap with multiple layers

Hi,

how to add a status image (e.g. health bar, text, etc…) to a tile additionally to the tile sprite itself?
I use different layers to represent the terrain, buildings and units on a tilemap. What is the proposed way to add multiple status images to e.g. the units or the buildings?

Thanks!

Get the x,y coordinates of the sprite body, like:

sprite.body.x
sprite.body.y

Maybe offset the status bar by 10px or something like:

let ox = sprite.body.x - 10
let oy = sprite.body.y -10

Place a status bar or text:

this.add.sprite(ox, oy, 'statusbar');
this.add.text(ox, oy, {
    fontFamily: "Arial",
    fontSize: "16px",
    color: "#ffffff"
}, status);

You can write a new tile sprite class of your own (which extends the default sprite class).

Or you can add a HP bar class, and create a instance of HPBar in your tile sprite class.

class SpriteHUDBar {
    constructor (scene, x, y, spriteWidth, spriteHeight,strName, hpMax, hexNormalColor=0x00ff00, hexDangerColor=0xff0000) {
 ...}
    ...
}

class WallSprite extends Phaser.Physics.Arcade.Sprite{
    constructor(scene,x,y,texture,scale,intHP) {
    ...
    this.hud = new SpriteHUDBar(scene,x,y,this.displayWidth,this.displayHeight,"",intHP,0xe74c3c,0xf39c12);
    ...
    }
}

I struggle a bit to understand your suggestion.

Here is some sample code on how the game map is created, Tilemap is used as the game is all bird-eye-view with rectangular tiles:

this.map = this.make.tilemap({ data: tiles, tileWidth: 32, tileHeight: 32 });
const terrainTileSet = this.map.addTilesetImage("terrainTileSet");
const towersTileSet = this.map.addTilesetImage("towersTileSet");
const armiesTileSet = this.map.addTilesetImage("armiesTileSet");
const terrainLayer = this.map.createLayer(0, terrainTileSet!, 0, 0);
const towersLayer = this.map.createBlankLayer("towersLayer", towersTileSet!);
if (towersLayer) {
  for (const tower of towers) {
    towersLayer.putTileAt(tower.id, tower.x, tower.y, false)
  }
}
const armiesLayer = this.map.createBlankLayer("armiesLayer", armiesTileSet!);
if (armiesLayer) {
  for (const army of armies) {
    armiesLayer.putTileAt(army.id, army.x, army.y, false);
  }
}

The terrain layer does not change at all, it is generated once, all tiles are filled and it stays the same all game long. Towers and armies are different: Towers can be build and destroyed so only some tiles show the tower tile (or the destroyed tower tile). Armies can move arround, have different types, health points, strength etc …

Here is a visual example:

Armies are drawn as tiles from the tileset, the army images are static. But how can I add dynamic objects to those army tiles like health bars and other stuff? They should for sure stay with the correct amy tiles but should also move correctly when the map is scrolled.

Sure, the first step is I would add your armies and towers as sprites instead of a tile layer. In preload:

this.load.image('tower', 'path/to/tower.png');
this.load.image('army', 'path/to/army.png');

When you spawn them, you just need to use this instead of putTileAt:

this.add.sprite(x, y, 'tower');

If they need physics:

this.physics.add.sprite(x, y, 'tower');

In addition, I would add these all to an array. In create:

this.towers = [];
this.armies = [];

When you spawn your sprites, push them into these arrays:

this.towers.push(this.physics.add.sprite(x, y, 'tower'));

In update, you can iterate these arrays in real time now, and manipulate them as you please. What I suggested was to get the sprite body, calculate an offset and then add a status bar image. You can also add text. With a status bar, I would make a tile sprite with different tiles representing different stages of health, but you can also draw a status bar with stuff like this:

const rect = this.add.rectangle(200, 150, 200, 150, 0x00ff00);
rect.setStrokeStyle(4, 0x000000);

To iterate them in update:

this.towers.forEach((tower) => {
	let ox = tower.x - 10;
	let oy = tower.y -10;
	this.add.sprite(ox, oy, 'statusbar');
	this.add.text(ox, oy, {
		fontFamily: "Arial",
		fontSize: "16px",
		color: "#ffffff"
	}, '6');
});

I don’t know how you want to do the status bar logic. If it has to be completely dynamic (like they have 100 hit points and an enemy can take away any number of points per hit), then don’t use a sprite and just draw it, unless you want to make a tile sprite with 100 frames, which is possible!

Ok, understood. Means you would prefer using tile map only for the terrain and any “dynamic” object better to use sprites.

Yes, I believe that’s the optimal way to do it.

You can store tile data in properties and locate them with getLeft() etc.

Thanks, that helped a lot. I used armies as spritesheets which worked perfectly. Camera scrolling does not affect the actual x/y position so everything scrolls as exptected.

1 Like