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.

Loading editor…

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 createHero function that builds the sprite and positions it
  • A updateHeroPosition function that converts tile coordinates to pixels for rendering

Next: Keys to Move