Collecting a coin
This example focuses on the core pickup loop:
Coinis a non-solid trigger hitbox.- On overlap with the player, coin plays SFX, sends a score message, and destroys itself.
CoinCounterlistens to the score message and updates HUD text.WalkerPlayermoves withW/A/S/Dinside fixed bounds.
1) Coin trigger (the important part)
The coin does not block movement (solid: false), it only reacts to overlap:
class Coin extends GameObject {
constructor(position: Vector) {
super("coin", position.clone());
this.addHitbox(
new SquareHitbox(Vector.zero(), new Vector(16, 16), this, {
solid: false,
}),
);
}
override onColision(other: GameObject): void {
if (other.name !== "player") return;
this.getContext()?.getSoundManager().playSound("coin_pickup");
this.sendMessage("score:coin_collected", { amount: 1 });
this.destroy();
}
}
2) Message-based HUD update
The HUD does not need to know where the coin is. It only listens to a channel:
class CoinCounter extends GameObject {
private coins = 0;
override onAddedToScene(): void {
this.onMessage<{ amount: number }>("score:coin_collected", ({ amount }) => {
this.coins += amount;
});
}
}
3) Player movement
The player resets velocity each tick, lets the held keys write the current movement direction, and clamps position to the play area in tick():
override tick(): void {
this.speed = Vector.zero();
super.tick();
}
@onKeyHold<WalkerPlayer>("w", (obj) => obj.speed = new Vector(0, -PLAYER_SPEED))
@onKeyHold<WalkerPlayer>("a", (obj) => obj.speed = new Vector(-PLAYER_SPEED, 0))
@onKeyHold<WalkerPlayer>("s", (obj) => obj.speed = new Vector(0, PLAYER_SPEED))
@onKeyHold<WalkerPlayer>("d", (obj) => obj.speed = new Vector(PLAYER_SPEED, 0))
override handleEvent(event: GameEvent): void {
super.handleEvent(event);
}
4) Scene wiring + sound preload
We preload the sound before starting the game and unlock audio on first user input:
const boot = async (): Promise<void> => {
const ctx = game.getContext();
await ctx.getSoundManager().loadSound("coin_pickup", coinAudioUrl, ["sfx"]);
const unlockAudio = () => void ctx.getSoundManager().unlock();
window.addEventListener("pointerdown", unlockAudio, { once: true });
window.addEventListener("keydown", unlockAudio, { once: true });
game.start();
};
Interactive example
This sandbox demonstrates trigger pickups and score messages.
- Edit
Coin.tsto change pickup behavior. - Edit
WalkerPlayer.ts,Hud.ts, andmain.tsto tweak movement, HUD, and scene setup. - Press Run to apply changes.
import { CircleHitbox, GameObject, SquareHitbox, Vector } from "sliver-engine"; const COIN_SIZE = 8; export class Coin extends GameObject { constructor(position: Vector) { super("coin", position.clone()); this.addHitbox( new CircleHitbox(Vector.zero(), COIN_SIZE, this, { solid: false, }), ); this.setRenderFunction((obj, canvas) => { const pos = obj.getPosition(); canvas .getShapeDrawer() .drawCircle(pos.x, pos.y, COIN_SIZE, "#facc15", true, false); }); } override onColision(other: GameObject): void { if (other.name !== "player") return; //Unnecessary but good to have // Play coin collect sound this.getContext()?.getSoundManager().playSound("coin_pickup", { volume: 0.1, }); // Notify the HUD that a coin is collected this.sendMessage("score:coin_collected", { amount: 1 }); this.destroy(); } }