Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/examples/src/examples/platformer/createGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const createGame = () => {
!video.init(800, 600, {
parent: "screen",
scaleMethod: "flex-width",
renderer: video.WEBGL,
renderer: video.AUTO,
preferWebGL1: false,
depthTest: "z-buffer",
subPixel: false,
Expand Down
92 changes: 92 additions & 0 deletions packages/examples/src/examples/platformer/entities/minimap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Camera2d, event, game, level } from "melonjs";

const MINIMAP_WIDTH = 180;
const MINIMAP_HEIGHT = 100;

/**
* A minimap camera that shows a zoomed-out view of the entire level
* in the top-right corner of the screen.
*/
export class MinimapCamera extends Camera2d {
private boundOnResize: (w: number) => void;

constructor() {
super(0, 0, MINIMAP_WIDTH, MINIMAP_HEIGHT);
this.name = "minimap";
this.screenX = game.viewport.width - MINIMAP_WIDTH - 10;
this.screenY = 10;

// prevent canvas resize from resetting this camera's dimensions/bounds
this.autoResize = false;

// reposition on canvas resize (keep anchored to top-right)
this.boundOnResize = (w: number) => {
this.screenX = w - MINIMAP_WIDTH - 10;
};
event.on(event.CANVAS_ONRESIZE, this.boundOnResize);

const currentLevel = level.getCurrentLevel();
if (currentLevel) {
const lw = currentLevel.cols * currentLevel.tilewidth;
const lh = currentLevel.rows * currentLevel.tileheight;
this.setBounds(0, 0, lw, lh);
this.zoom = Math.min(MINIMAP_WIDTH / lw, MINIMAP_HEIGHT / lh);
}
}

/**
* Draw minimap overlays: viewport highlight, player marker, and border.
*/
override postDraw(renderer: any): void {
const viewport = game.viewport;
const screenPx = 1 / this.zoom; // 1 screen pixel in world units
const savedLineWidth = renderer.lineWidth;

// main camera's visible area in world space
const view = viewport.worldView;
renderer.setGlobalAlpha(0.9);
renderer.setColor("#ffffff");
renderer.lineWidth = 1.5 * screenPx;
renderer.strokeRect(view.left, view.top, view.width, view.height);
Comment on lines +48 to +50
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderer.lineWidth is modified here, but in WebGL the renderer save/restore stack does not track lineWidth, so this value can leak into subsequent draws even after super.postDraw() restores. Consider saving the previous lineWidth and restoring it before returning (or explicitly resetting it after drawing the minimap overlays).

Copilot uses AI. Check for mistakes.

// player position marker
const players = game.world.getChildByProp("name", "mainPlayer");
if (players.length > 0) {
const player = players[0];
const markerSize = 4 * screenPx;
renderer.setGlobalAlpha(1.0);
renderer.setColor("#00ff00");
renderer.fillEllipse(
player.pos.x + player.width / 2,
player.pos.y + player.height / 2,
markerSize,
markerSize,
);
}

// minimap border
renderer.setGlobalAlpha(0.8);
renderer.setColor("#ffffff");
renderer.lineWidth = 2 * screenPx;
renderer.strokeRect(
0,
0,
MINIMAP_WIDTH / this.zoom,
MINIMAP_HEIGHT / this.zoom,
);

// restore lineWidth
renderer.lineWidth = savedLineWidth;

// restore the camera context
super.postDraw(renderer);
}

/**
* Cleanup event listeners.
*/
override destroy(): void {
event.off(event.CANVAS_ONRESIZE, this.boundOnResize);
super.destroy();
}
}
6 changes: 6 additions & 0 deletions packages/examples/src/examples/platformer/play.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { audio, device, game, level, plugin, Stage } from "melonjs";
import { VirtualJoypad } from "./entities/controls";
import UIContainer from "./entities/HUD";
import { MinimapCamera } from "./entities/minimap";
import { gameState } from "./gameState";

export class PlayScreen extends Stage {
Expand All @@ -14,6 +15,11 @@ export class PlayScreen extends Stage {
// load a level
level.load("map1");

// add a minimap camera (reuse if already present)
if (!this.cameras.has("minimap")) {
this.cameras.set("minimap", new MinimapCamera());
}

// reset the score
gameState.data.score = 0;

Expand Down
3 changes: 3 additions & 0 deletions packages/melonjs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## [18.2.0] (melonJS 2)

### Added
- Camera2d: added proper multi-camera support
- Platformer example: minimap camera showing a zoomed-out view of the full level with viewport highlight and player marker

### Changed
- TypeScript: convert leaf modules to TypeScript — plugin, camera, particles emitter, state, audio
Expand All @@ -15,6 +17,7 @@
- UITextButton: fix `bindKey` settings type from `string` to `string | number`
- Application: fix constructor `options` parameter not being optional
- Application: fix `getUriFragment()` unsafe cast by making `url` parameter optional
- CanvasRenderer: `setProjection()` now properly applies the projection matrix as a canvas 2D transform

### Performance

Expand Down
Loading
Loading