Getting Items

Collectibles are a staple of tile-based games — coins in Mario, rupees in Zelda, rings in Sonic. The pickup system needs to do three things: track what items exist and where, detect when the player steps on one, and remove it permanently so it doesn’t reappear.

Loading editor…

Items are different in what they do - coins add a little to your score, gems are worth ten times more. That’s the same trick games use to reward you for exploring off the beaten path. In this tutorial all items just give points, but the same pattern works for health potions, ammo pickups, power-ups, or anything else you want to create!

Tracking your score

Add a points property to your game object:

const game = {
    currentRoom: 1,
    tileSize: 30,
    points: 0    // Score lives here, safe across all rooms
};

To show the score on screen, use a PixiJS Text object positioned in the top-left corner, outside the tile area so walls can’t cover it:

import { Text, TextStyle } from 'https://unpkg.com/pixi.js@8.0.0/dist/pixi.min.mjs';

const pointsStyle = new TextStyle({ fill: 0xFFFFFF, fontSize: 14, fontFamily: 'monospace' });
const pointsDisplay = new Text({ text: 'Points: 0', style: pointsStyle });
pointsDisplay.x = 8;
pointsDisplay.y = 8;
app.stage.addChild(pointsDisplay);

// Call this whenever points change:
pointsDisplay.text = `Points: ${game.points}`;

Something to pick up

Like enemies, items are stored in a data array - one sub-array per room. Each item is three numbers: [type, tileX, tileY].

// myItems[roomIndex] = [[type, tileX, tileY], ...]
const myItems = [
    [],                             // Room 0 - unused
    [[1,1,1],[1,1,2],[2,1,3]],     // Room 1: 3 items
    [[2,1,3],[2,6,3],[1,5,4]]      // Room 2: 3 items
];

Then define what each type looks like and what it’s worth:

// Item type definitions - add as many as you like!
const ITEM_TYPES = {
    1: { points: 1,  color: 0xFFD700, size: 5, label: 'coin' },  // Gold coin
    2: { points: 10, color: 0x00BFFF, size: 4, label: 'gem'  }   // Blue gem
};

The type number in myItems is the key into ITEM_TYPES. So [2,6,3] means “a gem (type 2) at tile column 6, row 3”. Type 1 gives 1 point, type 2 gives 10.

Placing items on the map

When you call buildMap(), loop through the items for the current room and create a PixiJS graphic for each one. Store everything in a JavaScript Map object keyed by tile position so you can do instant lookups during pickup checks.

// Active items on screen: key = "tileX_tileY", value = item data + sprite
const activeItems = new Map();

function buildItems() {
    const roomItems = myItems[game.currentRoom];

    for (const [type, tileX, tileY] of roomItems) {
        const itemType = ITEM_TYPES[type];

        // Draw a colored circle for the item
        const sprite = new Graphics()
            .circle(0, 0, itemType.size)
            .fill(itemType.color);

        // Center it inside its tile
        sprite.x = tileX * game.tileSize + game.tileSize / 2;
        sprite.y = tileY * game.tileSize + game.tileSize / 2;

        app.stage.addChild(sprite);

        // Store by tile position for instant lookup
        const key = `${tileX}_${tileY}`;
        activeItems.set(key, { sprite, type, tileX, tileY, pointValue: itemType.points });
    }

    // Show current score (player may already have points from other rooms)
    pointsDisplay.text = `Points: ${game.points}`;
}

The key is just "tileX_tileY" - for example a gem at column 6, row 3 gets the key "6_3". When the hero steps on that tile you can check activeItems.get("6_3") instantly, no looping required.

Collecting items

Add this check at the end of your movement function. After every step, look up the player’s current tile in activeItems:

function checkItemPickup() {
    // Which tile is the center of the player on?
    const tileX = Math.floor((player.x + player.width / 2) / game.tileSize);
    const tileY = Math.floor((player.y + player.height / 2) / game.tileSize);

    const item = activeItems.get(`${tileX}_${tileY}`);

    if (item) {
        // Got one — add points and update display
        game.points += item.pointValue;
        pointsDisplay.text = `Points: ${game.points}`;

        // Remove the sprite from screen
        app.stage.removeChild(item.sprite);

        // Remove from the tracking map
        activeItems.delete(`${tileX}_${tileY}`);

        // Remove from source data so it won't respawn
        myItems[game.currentRoom] = myItems[game.currentRoom].filter(
            ([, tx, ty]) => !(tx === item.tileX && ty === item.tileY)
        );
    }
}

Each collected item needs to vanish from three places:

  1. The screenapp.stage.removeChild(item.sprite)
  2. The active tracking mapactiveItems.delete(key)
  3. The source data → filter it out of myItems

That third step is the critical one. Without it the item reappears every time the player re-enters the room!

What you built:

  • Score tracking in the game object, updated on pickup
  • Multiple item types defined in a single lookup table
  • Instant pickup detection via tile-position Map key
  • Permanent removal from screen, tracking Map, and source data

Items don’t have to give points. The same pattern works for health pickups, ammo, power-ups, or anything else — the item type definition determines the effect.

Next up: Open the Door — build a multi-room world your items can live across.