Leave without saving?

Your editor changes will be lost.

Mario-6 adds Goombas. These enemies walk back and forth across the level. Land on one from above to stomp it out of the level. Touch one any other way and you’re sent back to the start.

Goombas from the Map

Goombas are placed in Tiled like any other tile. TileMap.initializeSpecialTiles() swaps them for Goomba objects the same way it swaps blocks, except that it leaves an empty cell behind. A Goomba walks away from its starting spot, so it mustn’t leave anything solid there:

} else if (tileId === Tile.GOOMBA) {
this.goombas.push(
new Goomba(
pixelX,
pixelY,
this.services.images.get(ImageName.Goomba),
this,
),
);
this.setTile(x, y, null);
}

Walking

A Goomba’s AI is about as simple as AI gets. It falls under gravity, walks in one direction, and turns around on hitting a wall.

update(dt) {
this.velocity.y += this.gravity * dt;
this.position.y += this.velocity.y * dt;
this.position.x += this.direction * this.speed * dt;
if (this.isOnFloor(this.map)) {
this.snapToFloor(this.map);
}
if (this.isWallAhead(this.map, this.direction)) {
this.direction *= -1;
}
this.walkAnimation.update(dt);
}

isOnFloor(), snapToFloor(), and isWallAhead() live in GameObject, so any future walking enemy or item can reuse them. They’re simpler cousins of the player’s collision code. A Goomba never jumps, so it only needs to land and bounce off walls.

Touching the Player

Player.update() checks every Goomba with AABB collision on their hitboxes:

checkGoombaCollision() {
this.map.goombas.forEach((goomba) => {
if (!goomba.shouldBeRemoved && this.collidesWith(goomba)) {
goomba.onCollideWithPlayer(this);
}
});
}

The Goomba’s hitbox is inset by 4 pixels on every side, so brushing past one only counts once the two clearly overlap. Combined with the player’s narrower hitbox from Mario-4, near misses feel fair.

Stomp or Hurt?

When they touch, the Goomba decides what happened. It’s a stomp if the player is falling and their feet were above the Goomba’s hitbox on the previous frame:

const isStomp =
player.velocity.y > 0 && player.previousBottom <= this.getHitbox().y;
if (isStomp) {
this.shouldBeRemoved = true;
} else {
// Goomba hits the player
player.die();
}

Player records previousBottom at the end of every update for exactly this check.

A stomped Goomba sets shouldBeRemoved. TileMap.update() filters those out after updating, so nothing is removed from the array while it’s being looped over.

Back to the Start

Player.die() sends the player back to the spawn point from map.js:

die() {
this.stateMachine.change(PlayerStateName.Falling);
this.position.x = this.spawn.x;
this.position.y = this.spawn.y;
this.velocity.x = 0;
this.velocity.y = 0;
}

It changes state first. If the player was ducking, leaving that state restores their height and moves their top edge, which would otherwise nudge them away from the spawn point.

PlayerFallingState also calls die() if the player falls below the bottom of the map. This level has no pits yet, but any gap you add to the ground in map.js is now deadly.

Turn on Debug Watch Panel to see the Goomba count drop as you stomp them.

📚 References

Powered by WebContainers
Files
Preparing Environment