Skip to content

Commit 6472226

Browse files
authored
Merge pull request #1348 from melonjs/refactor/container-no-game
Remove game import from Container, default dimensions to Infinity
2 parents ec7e28d + a361299 commit 6472226

36 files changed

Lines changed: 590 additions & 274 deletions

README.md

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -163,30 +163,29 @@ Browse all examples [here](https://melonjs.github.io/melonJS/examples/)
163163
### Basic Hello World Example
164164

165165
```JavaScript
166-
import * as me from "https://cdn.jsdelivr.net/npm/melonjs/+esm";
167-
168-
me.device.onReady(function () {
169-
// initialize the display canvas once the device/browser is ready
170-
if (!me.video.init(1218, 562, {parent : "screen", scale : "auto"})) {
171-
alert("Your browser does not support HTML5 canvas.");
172-
return;
173-
}
174-
175-
// set a gray background color
176-
me.game.world.backgroundColor.parseCSS("#202020");
177-
178-
// add a font text display object
179-
me.game.world.addChild(new me.Text(609, 281, {
180-
font: "Arial",
181-
size: 160,
182-
fillStyle: "#FFFFFF",
183-
textBaseline : "middle",
184-
textAlign : "center",
185-
text : "Hello World !"
186-
}));
166+
import { Application, Text } from "https://cdn.jsdelivr.net/npm/melonjs/+esm";
167+
168+
// create a new melonJS application
169+
const app = new Application(1218, 562, {
170+
parent: "screen",
171+
scale: "auto",
172+
backgroundColor: "#202020",
187173
});
174+
175+
// set a gray background color
176+
app.world.backgroundColor.parseCSS("#202020");
177+
178+
// add a font text display object
179+
app.world.addChild(new Text(609, 281, {
180+
font: "Arial",
181+
size: 160,
182+
fillStyle: "#FFFFFF",
183+
textBaseline: "middle",
184+
textAlign: "center",
185+
text: "Hello World !",
186+
}));
188187
```
189-
> Simple hello world using melonJS 2
188+
> Simple hello world using melonJS
190189
191190
Documentation
192191
-------------------------------------------------------------------------------
Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DebugPanelPlugin } from "@melonjs/debug-plugin";
22
import {
3+
Application,
34
audio,
45
device,
56
event,
@@ -9,7 +10,6 @@ import {
910
pool,
1011
state,
1112
TextureAtlas,
12-
video,
1313
} from "melonjs";
1414
import { CoinEntity } from "./entities/coin.js";
1515
import { FlyEnemyEntity, SlimeEnemyEntity } from "./entities/enemies.js";
@@ -19,63 +19,52 @@ import { PlayScreen } from "./play.js";
1919
import { resources } from "./resources.js";
2020

2121
export const createGame = () => {
22-
// init the video
23-
if (
24-
!video.init(800, 600, {
25-
parent: "screen",
26-
scaleMethod: "flex-width",
27-
renderer: video.AUTO,
28-
preferWebGL1: false,
29-
depthTest: "z-buffer",
30-
subPixel: false,
31-
})
32-
) {
33-
alert("Your browser does not support HTML5 canvas.");
34-
return;
35-
}
22+
// create a new melonJS Application
23+
const app = new Application(800, 600, {
24+
parent: "screen",
25+
scaleMethod: "flex-width",
26+
renderer: 2, // AUTO
27+
preferWebGL1: false,
28+
depthTest: "z-buffer",
29+
subPixel: false,
30+
});
3631

3732
// register the debug plugin
3833
plugin.register(DebugPanelPlugin, "debugPanel");
3934

40-
// initialize the "sound engine"
35+
// initialize the sound engine
4136
audio.init("mp3,ogg");
4237

4338
// allow cross-origin for image/texture loading
4439
loader.setOptions({ crossOrigin: "anonymous" });
4540

46-
// set all ressources to be loaded
41+
// preload all resources
4742
loader.preload(resources, () => {
48-
// set the "Play/Ingame" Screen Object
43+
// set the Play screen
4944
state.set(state.PLAY, new PlayScreen());
5045

5146
// set the fade transition effect
5247
state.transition("fade", "#FFFFFF", 250);
5348

54-
// register our objects entity in the object pool
49+
// register entity classes in the object pool
5550
pool.register("mainPlayer", PlayerEntity);
5651
pool.register("SlimeEntity", SlimeEnemyEntity);
5752
pool.register("FlyEntity", FlyEnemyEntity);
5853
pool.register("CoinEntity", CoinEntity, true);
5954

60-
// load the texture atlas file
61-
// this will be used by renderable object later
55+
// load the texture atlas
6256
gameState.texture = new TextureAtlas(
6357
loader.getJSON("texture"),
6458
loader.getImage("texture"),
6559
);
6660

67-
// add some keyboard shortcuts
68-
event.on(event.KEYDOWN, (_action, keyCode /*, edge */) => {
69-
// change global volume setting
61+
// keyboard shortcuts for volume and fullscreen
62+
event.on(event.KEYDOWN, (_action, keyCode) => {
7063
if (keyCode === input.KEY.PLUS) {
71-
// increase volume
7264
audio.setVolume(audio.getVolume() + 0.1);
7365
} else if (keyCode === input.KEY.MINUS) {
74-
// decrease volume
7566
audio.setVolume(audio.getVolume() - 0.1);
7667
}
77-
78-
// toggle fullscreen on/off
7968
if (keyCode === input.KEY.F) {
8069
if (!device.isFullscreen()) {
8170
device.requestFullscreen();
@@ -85,7 +74,7 @@ export const createGame = () => {
8574
}
8675
});
8776

88-
// switch to PLAY state
77+
// switch to the Play state
8978
state.change(state.PLAY);
9079
});
9180
};

packages/examples/src/examples/platformer/entities/player.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
audio,
33
Body,
44
collision,
5-
game,
65
input,
76
level,
87
Rect,
@@ -61,9 +60,6 @@ export class PlayerEntity extends Sprite {
6160

6261
this.multipleJump = 1;
6362

64-
// set the viewport to follow this renderable on both axis, and enable damping
65-
game.viewport.follow(this, game.viewport.AXIS.BOTH, 0.1);
66-
6763
// enable keyboard
6864
input.bindKey(input.KEY.LEFT, "left");
6965
input.bindKey(input.KEY.RIGHT, "right");
@@ -150,7 +146,16 @@ export class PlayerEntity extends Sprite {
150146
}
151147

152148
/**
153-
** update the force applied
149+
* called when added to the game world
150+
*/
151+
onActivateEvent() {
152+
const app = this.parentApp;
153+
// set the viewport to follow this renderable on both axis, and enable damping
154+
app.viewport.follow(this, app.viewport.AXIS.BOTH, 0.1);
155+
}
156+
157+
/**
158+
* update the force applied
154159
*/
155160
update(dt) {
156161
if (input.isKeyPressed("left")) {
@@ -192,12 +197,13 @@ export class PlayerEntity extends Sprite {
192197

193198
// check if we fell into a hole
194199
if (!this.inViewport && this.getBounds().top > video.renderer.height) {
200+
const app = this.parentApp;
195201
// if yes reset the game
196-
game.world.removeChild(this);
197-
game.viewport.fadeIn("#fff", 150, () => {
202+
app.world.removeChild(this);
203+
app.viewport.fadeIn("#fff", 150, () => {
198204
audio.play("die", false);
199205
level.reload();
200-
game.viewport.fadeOut("#fff", 150);
206+
app.viewport.fadeOut("#fff", 150);
201207
});
202208
return true;
203209
}
@@ -211,7 +217,7 @@ export class PlayerEntity extends Sprite {
211217
}
212218

213219
/**
214-
* colision handler
220+
* collision handler
215221
*/
216222
onCollision(response, other) {
217223
switch (other.body.collisionType) {
@@ -228,7 +234,7 @@ export class PlayerEntity extends Sprite {
228234
) {
229235
// Disable collision on the x axis
230236
response.overlapV.x = 0;
231-
// Repond to the platform (it is solid)
237+
// Respond to the platform (it is solid)
232238
return true;
233239
}
234240
// Do not respond to the platform (pass through)
@@ -286,7 +292,7 @@ export class PlayerEntity extends Sprite {
286292
});
287293

288294
// flash the screen
289-
game.viewport.fadeIn("#FFFFFF", 75);
295+
this.parentApp.viewport.fadeIn("#FFFFFF", 75);
290296
audio.play("die", false);
291297
}
292298
}

packages/examples/src/examples/platformer/play.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { audio, device, game, level, plugin, Stage } from "melonjs";
1+
import { type Application, audio, device, level, plugin, Stage } from "melonjs";
22
import { VirtualJoypad } from "./entities/controls";
33
import UIContainer from "./entities/HUD";
44
import { MinimapCamera } from "./entities/minimap";
@@ -11,7 +11,7 @@ export class PlayScreen extends Stage {
1111
/**
1212
* action to perform on state change
1313
*/
14-
override onResetEvent() {
14+
override onResetEvent(app: Application) {
1515
// load a level
1616
level.load("map1");
1717

@@ -27,14 +27,14 @@ export class PlayScreen extends Stage {
2727
if (typeof this.HUD === "undefined") {
2828
this.HUD = new UIContainer();
2929
}
30-
game.world.addChild(this.HUD);
30+
app.world.addChild(this.HUD);
3131

3232
// display if debugPanel is enabled or on mobile
3333
if (plugin.cache.debugPanel?.panel.visible || device.touch) {
3434
if (typeof this.virtualJoypad === "undefined") {
3535
this.virtualJoypad = new VirtualJoypad();
3636
}
37-
game.world.addChild(this.virtualJoypad);
37+
app.world.addChild(this.virtualJoypad);
3838
}
3939

4040
// play some music
@@ -44,15 +44,15 @@ export class PlayScreen extends Stage {
4444
/**
4545
* action to perform on state change
4646
*/
47-
override onDestroyEvent() {
47+
override onDestroyEvent(app: Application) {
4848
// remove the HUD from the game world
4949
if (this.HUD) {
50-
game.world.removeChild(this.HUD);
50+
app.world.removeChild(this.HUD);
5151
}
5252

5353
// remove the joypad if initially added
54-
if (this.virtualJoypad && game.world.hasChild(this.virtualJoypad)) {
55-
game.world.removeChild(this.virtualJoypad);
54+
if (this.virtualJoypad && app.world.hasChild(this.virtualJoypad)) {
55+
app.world.removeChild(this.virtualJoypad);
5656
}
5757

5858
// stop some music

packages/examples/src/examples/ui/ExampleUI.tsx

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
2+
Application as App,
3+
type Application,
24
ColorLayer,
3-
game,
45
loader,
56
Stage,
67
state,
@@ -41,7 +42,6 @@ class ButtonUI extends UISpriteElement {
4142
fillStyle: "black",
4243
textAlign: "center",
4344
textBaseline: "middle",
44-
offScreenCanvas: video.renderer.WebGLVersion >= 1,
4545
});
4646
}
4747

@@ -123,7 +123,6 @@ class CheckBoxUI extends UISpriteElement {
123123
textAlign: "left",
124124
textBaseline: "middle",
125125
text: offLabel,
126-
offScreenCanvas: true,
127126
});
128127

129128
this.getBounds().width += this.font.measureText().width;
@@ -204,8 +203,8 @@ class UIContainer extends UIBaseElement {
204203
}
205204

206205
class PlayScreen extends Stage {
207-
override onResetEvent() {
208-
game.world.addChild(
206+
override onResetEvent(app: Application) {
207+
app.world.addChild(
209208
new ColorLayer("background", "rgba(248, 194, 40, 1.0)"),
210209
0,
211210
);
@@ -243,21 +242,16 @@ class PlayScreen extends Stage {
243242
panel.addChild(new ButtonUI(30, 250, "green", "Accept"));
244243
panel.addChild(new ButtonUI(230, 250, "yellow", "Cancel"));
245244

246-
game.world.addChild(panel, 1);
245+
app.world.addChild(panel, 1);
247246
}
248247
}
249248

250249
const createGame = () => {
251-
if (
252-
!video.init(800, 600, {
253-
parent: "screen",
254-
scale: "auto",
255-
scaleMethod: "flex-width",
256-
})
257-
) {
258-
alert("Your browser does not support HTML5 canvas.");
259-
return;
260-
}
250+
const app = new App(800, 600, {
251+
parent: "screen",
252+
scale: "auto",
253+
scaleMethod: "flex-width",
254+
});
261255

262256
const resources = [
263257
{ name: "UI_Assets-0", type: "image", src: `${base}img/UI_Assets-0.png` },

packages/melonjs/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
- Application: new `canvas` getter, `resize()`, and `destroy()` convenience methods
1515
- Application: `GAME_INIT` event now passes the Application instance as parameter
1616
- Stage: `onResetEvent(app, ...args)` now receives the Application instance as first parameter, followed by any extra arguments from `state.change()`
17+
- Stage: `onDestroyEvent(app)` now receives the Application instance as parameter
18+
- Container: default dimensions are now `Infinity` (no intrinsic size, no clipping) — removes dependency on `game.viewport`. `anchorPoint` is always `(0, 0)` as containers act as grouping/transform nodes
19+
- ImageLayer: decoupled from `game` singleton — uses `parentApp` for viewport and renderer access; `resize`, `createPattern` and event listeners deferred to `onActivateEvent`
20+
- TMXTileMap: `addTo()` resolves viewport from the container tree via `getRootAncestor().app` instead of `game.viewport`
21+
- Input: pointer and pointerevent modules now receive the Application instance via `GAME_INIT` event instead of importing the `game` singleton
22+
- video: `video.renderer` and `video.init()` are now deprecated — use `new Application(width, height, options)` and `app.renderer` instead. `video.renderer` is kept in sync via `VIDEO_INIT` for backward compatibility
1723
- EventEmitter: native context parameter support — `addListener(event, fn, context)` and `addListenerOnce(event, fn, context)` now accept an optional context, eliminating `.bind()` closure overhead and enabling proper `removeListener()` by original function reference
1824
- EventEmitter: `event.on()` and `event.once()` no longer create `.bind()` closures when a context is provided
1925

@@ -25,6 +31,8 @@
2531
- Application: prevent white flash on load by setting a black background on the parent element when no background is defined
2632
- WebGLRenderer: `setBlendMode()` now tracks the `premultipliedAlpha` flag — previously only the mode name was checked, causing incorrect GL blend function when mixing PMA and non-PMA textures with the same blend mode
2733
- TMX: fix crash in `getObjects(false)` when a map contains an empty object group (Container.children lazily initialized)
34+
- Container: fix `updateBounds()` producing NaN when container has Infinity dimensions (skip parent bounds computation for non-finite containers, derive bounds from children only)
35+
- Container: fix circular import in `BitmapTextData` pool registration (`pool.ts``bitmaptextdata.ts`)
2836
- EventEmitter: `removeAllListeners()` now correctly clears once-listeners (previously only cleared regular listeners)
2937
- Loader: fix undefined `crossOrigin` variable in script parser, unsafe regex match in video parser, missing error parameter in video/fontface error callbacks, `fetchData` Promise constructor antipattern and silent error swallowing
3038

0 commit comments

Comments
 (0)