melonJSを使ったゲーム制作で、目的に合う専門ガイドを選ぶための入口です。初期化から描画、入力、物理演算、音声、2D・3D機能まで案内します。
- melonJSで作る内容に合うガイドを探したいとき
- 新しいゲームの初期化手順を知りたいとき
- 3Dやモデル読み込みの参照先を探したいとき
melonJSのイベント購読と解除、フレームや状態変更のイベント名、Renderableのライフサイクルを整理し、重複登録や解除漏れを防ぎます。
Use this skill for the melonJS event bus and lifecycle hooks — subscribing to engine events, the per-frame and per-state event names, renderable lifecycle callbacks, and how to unsubscribe without leaking. Triggers on: event.on, event.off, event.once, event.emit, GAME_UPDATE, GAME_BEFORE_DRAW, LEVEL_LOADED, LOADER_COMPLETE, STATE_CHANGE, CANVAS_ONRESIZE, POINTERMOVE, WORLD_STEP, onActivateEvent, onDeactivateEvent, onDestroyEvent, lifecycle, subscribe, listener.
実行スコア 未検証
インストール方法を見るmelonJSのイベントバスでエンジンイベントを購読し、event.offやevent.onceで管理する方法を扱います。フレーム更新、描画、物理ステップ、状態変更、レベル読み込み、画面サイズ変更など、用途に応じたイベントを確認できます。ハンドラーとcontextの同一性が解除時に必要なことや、RenderableのonActivateEvent()・onDeactivateEvent()を使った登録と解除の組み合わせも学べます。
ゲームの更新やアセット読み込み完了に合わせて処理したいときに役立ちます。プールで再利用するオブジェクトにイベントを登録する場合や、状態切り替え中のコールバック、衝突処理中のオブジェクト削除で問題が起きる場合にも、原因と扱い方を確認できます。
TypeScriptではエンジンのイベント一覧にない独自イベント名が型エラーになります。旧loader.onProgressなどのプロパティは削除済みで、LOADER_PROGRESSなどのイベントを使います。
この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。
インストールする前に、エージェントに与えられる指示の中身を確認できます。
import { event } from "melonjs";
const onUpdate = () => { /* … */ };
const unsubscribe = event.on(event.GAME_UPDATE, onUpdate); // on() returns its own off()
event.once(event.LEVEL_LOADED, () => { /* … */ });
// two ways to undo it — the same reference, or the returned function
event.off(event.GAME_UPDATE, onUpdate);
unsubscribe();
event.on/once/off/has all take an optional third context argument,
invoked as the handler's this. off matches on the pair — handler identity
AND context identity. on(EVT, fn, this) followed by off(EVT, fn) removes
nothing, silently.
Store the handler reference, or use the context argument. An inline arrow or
a fresh .bind(this) in the on call can never be removed — that is the
standard way to leak listeners in melonJS.
// ✗ unremovable
event.on(event.GAME_UPDATE, this.tick.bind(this));
// ✓ keep the bound reference
this._tick = this.tick.bind(this);
event.on(event.GAME_UPDATE, this._tick);
// later: event.off(event.GAME_UPDATE, this._tick);
// ✓ or skip the bind entirely — the context is part of the identity
event.on(event.GAME_UPDATE, this.tick, this);
// later: event.off(event.GAME_UPDATE, this.tick, this);
Custom event names work at runtime (event.emit("my.custom.event", payload)),
but on/emit are typed against a closed map of the engine's own events, so in
TypeScript a name outside that map is a compile error. Use your own emitter for
game events in a .ts project.
Per frame
| event | when |
|---|---|
GAME_BEFORE_UPDATE / GAME_UPDATE / GAME_AFTER_UPDATE | around the world update — GAME_UPDATE is not emitted while paused |
GAME_BEFORE_DRAW / GAME_AFTER_DRAW | around the draw pass |
WORLD_STEP | after the physics step |
TICK | the raw engine tick |
Lifecycle
| event | when |
|---|---|
BOOT, GAME_INIT, VIDEO_INIT | engine start-up |
STATE_CHANGE, STATE_PAUSE, STATE_RESUME, STATE_STOP | state machine |
STAGE_RESET | a stage was reset |
LEVEL_LOADED | a Tiled level finished loading |
LOADER_PROGRESS, LOADER_COMPLETE, LOADER_ERROR | asset loading |
BLUR / FOCUS | the page lost or regained focus |
CANVAS_ONRESIZE, VIEWPORT_ONRESIZE | relayout hooks |
POINTERMOVE | global pointer movement |
Use LOADER_PROGRESS / LOADER_COMPLETE. The loader.onProgress /
loader.onload / loader.onError properties were deprecated in 18.2.0 and
removed in 20.3: they were module-level let bindings on an ES module
namespace, so loader.onProgress = fn threw and the documented migration path
never worked. For completion, loader.preload(assets, onloadcb) also takes the
callback directly.
Prefer these over bus subscriptions where they fit — the engine calls them at the right times and there is nothing to unsubscribe:
| callback | when |
|---|---|
onActivateEvent() | addChild-ed into a container that is already attached to the world root (a Container forwards it to its children) |
onDeactivateEvent() | removed from its container — including the automatic world clear on a state change |
onDestroyEvent() | destroy() ran; called with no arguments from the container-removal path |
onCollisionStart / onCollisionActive / onCollisionEnd | contacts |
Note the activate gate: adding a child to a detached container fires nothing.
The callback arrives later, when that container itself is added to the world.
And an isPersistent renderable survives the world clear between stages, so it
gets no fresh onActivateEvent on the next state.
Pair subscriptions with activate/deactivate, not the constructor. This is the rule that keeps pooled objects working:
onActivateEvent() {
this._tick = this.update.bind(this);
event.on(event.GAME_UPDATE, this._tick);
input.registerPointerEvent("pointerdown", this, this._onDown);
}
onDeactivateEvent() {
event.off(event.GAME_UPDATE, this._tick);
input.releasePointerEvent("pointerdown", this);
}
Pool-recycled objects never fire onDestroyEvent on removal — removeChildNow
first tries to return the child to the pool that built it, and only calls
destroy() when nothing owns it. So anything taken from a createPool goes
back to that pool instead. Anything you clean up there will not run. destroy() is also what calls
releaseAllPointerEvents(this), which is why a pooled object must release its
own pointer registrations in onDeactivateEvent.
Events can arrive while a stage is being dismantled, when the current state is already something else:
if (!state.isCurrent(state.PLAY)) return;
Without this, a "children emptied" handler can reset the wrong stage.
Contact callbacks fire inline inside the physics step, mid-iteration over the
detector's pair list. container.removeChild() is safe there — it defers the
actual removal to the next tick, which is exactly why it defers. The immediate
forms are the dangerous ones: removeChildNow() tears the object down under
the iteration, via the internal destroy() it calls, which nulls pos and
body. (destroy() is engine teardown and is not in the published types, so
removeChildNow is the call a game actually makes.) The builtin
detector guards the pair it is currently resolving, but nothing guarantees that
for the rest of the step or for another adapter.
Use removeChild(), or flag the work and drain it from Stage.update.
| symptom | cause |
|---|---|
| handler still fires after removing the object | event.off given a different function reference, or a different context than on got |
| handlers accumulate on pooled objects | subscribed in the constructor rather than onActivateEvent |
| cleanup never runs on a pooled object | pooled removal does not fire onDestroyEvent |
| crash during a stage switch | callback ran mid-teardown — guard with state.isCurrent |
| crash after a collision | removeChildNow() inside a contact callback — use removeChild() |
| progress callbacks never fire | assigned loader.onProgress (removed in 20.3) instead of subscribing to LOADER_PROGRESS |
onActivateEvent never runs | the child was added to a container not yet attached to the world |
melonjs-scenes-and-state — the state machine and stage lifecyclemelonjs-performance — pooling, and why activate/deactivate pairing mattersまだレビューはありません。使ってみた感想をお寄せください。
ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。
まだコメントはありません。最初のコメントを書いてみませんか。
melonJSを使ったゲーム制作で、目的に合う専門ガイドを選ぶための入口です。初期化から描画、入力、物理演算、音声、2D・3D機能まで案内します。
melonJS 19.x以前の知識を20.x向けに更新し、初期化や描画効果、廃止・非推奨APIの変更点を確認して移行時の不具合を調べられます。
melonJSの3D・2.5Dゲーム向けに、カメラ、メッシュ、ライト、奥行き順、透明表現やフォグの設定と座標系の注意点を確認できます。
melonJSでglTF・GLBやOBJ・MTLの3Dモデルを読み込み、マテリアルやライト、アニメーション、影、インスタンスの扱い方を確認できます。
melonJSで効果音やBGMを読み込み、再生・停止・音量調整するほか、位置に応じた音や音声合成、ミックス全体へのエフェクト設定を扱うスキルです。
melonJSのカメラ追従や画面効果、座標変換、図形描画を実装する際の要点を確認できます。描画のクリッピングやCanvasへの焼き込み、ピクセルの読み出しも扱います。