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.
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:
- The screen →
app.stage.removeChild(item.sprite) - The active tracking map →
activeItems.delete(key) - 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
gameobject, 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.