The Hero
The hero is a data object that stores position, size, and a reference to its sprite. Separating data from visuals keeps the game logic independent of how things are drawn.
The hero object
The hero stores two coordinate systems alongside its sprite reference:
const hero = {
// Which tile the hero occupies
tileX: 2,
tileY: 1,
// Exact pixel coordinates (calculated from tile position)
x: 0,
y: 0,
// Collision box size in pixels
width: 20,
height: 20,
// PixiJS sprite (assigned when created)
sprite: null,
speed: 2
};
tileX/tileY are used for collision checks — “is the tile at (3, 2) a wall?” — because that requires an array index, not a pixel value. x/y are used for rendering, because PixiJS works in pixels. Both are needed; they serve different parts of the system.
Placing the hero on screen
The sprite is a separate object from the data. Create it, attach it to the hero, then position it:
function createHero(heroData, tileSize) {
const heroSprite = new PIXI.Graphics()
.rect(-heroData.width / 2, -heroData.height / 2, heroData.width, heroData.height)
.fill(0xff4444);
heroData.sprite = heroSprite;
// Calculate pixel position from tile position
updateHeroPosition(heroData, tileSize);
return heroSprite;
}
function updateHeroPosition(heroData, tileSize) {
heroData.x = (heroData.tileX * tileSize) + (tileSize / 2);
heroData.y = (heroData.tileY * tileSize) + (tileSize / 2);
if (heroData.sprite) {
heroData.sprite.x = heroData.x;
heroData.sprite.y = heroData.y;
}
}
The pixel calculation: tileX * tileSize reaches the left edge of the tile, + tileSize / 2 centres it. A hero at tile (2, 1) with 30px tiles appears at pixel (75, 45).
After rendering the map, add the hero sprite to the stage:
const heroSprite = createHero(hero, game.tileW);
app.stage.addChild(heroSprite);
Customising the sprite
The sprite can be any shape. Change the .fill() colour, use .circle() for a round hero, or load a texture:
// Circle
new PIXI.Graphics().circle(0, 0, 10).fill(0xff4444);
// Loaded sprite
const heroTexture = await PIXI.Assets.load('hero.png');
const heroSprite = new PIXI.Sprite(heroTexture);
heroSprite.anchor.set(0.5); // Centre the sprite on its position
The rest of the hero system — movement, collision, state — doesn’t care which approach you use.
What you built:
- A hero data object with both tile and pixel coordinates
- A
createHerofunction that builds the sprite and positions it - A
updateHeroPositionfunction that converts tile coordinates to pixels for rendering