本文へ移動
cccskills
無料GitHub で公開日本語紹介

melonjs-loading-assets

melonJSで画像、音声、マップ、フォントなどのゲーム素材を読み込み、ロード画面や進捗、取得、解放、外部URLや認証付き素材の設定を管理するスキルです。

原文原文の説明を見る

Use this skill for loading and managing game assets in melonJS — the resource descriptor format, the full asset type list, preload versus load, retrieving loaded assets, base URLs and cross-origin settings, and the loading screen. Triggers on: loader, loader.preload, loader.load, resources, getImage, getJSON, getTMX, getGLTF, getVideo, getFont, setBaseURL, setOptions, crossOrigin, withCredentials, credentials, cookies, authenticated assets, CORS, asset, preload, LOADER_COMPLETE, loading screen, unload, setParser, custom asset type, generated texture, procedural texture.

melonjs/melonJSスター 6,429(GitHub 6,429 + cccskills 0)

実行スコア 未検証

インストール方法を見る

こんなときに便利

  • ゲーム素材のプリロード一覧を作りたいとき
  • 読み込み進捗を画面に表示したいとき
  • 外部サーバーの素材を読み込みたいとき
  • 独自形式や生成素材を読み込みに組み込みたいとき

日本語での紹介

できること

melonJSのローダーを使い、画像、音声、動画、フォント、JSON、Tiledマップ、3Dモデルなどの素材を読み込みます。素材ごとの記述方法とタイプ名、起動前にまとめて読み込む preload() と個別読み込みの使い分け、読み込み済み素材の取得や解放を扱います。進捗・完了イベントの監視、標準または独自のロード画面、URLの共通部分を設定する方法も確認できます。独自形式や生成画像をプリロード工程に組み込むカスタムアセットタイプ、外部サーバーからの素材や認証情報付きリクエストにも触れます。

こんなときに便利

ゲーム開始時やレベル切り替え時の素材読み込みを整理したいときに役立ちます。素材が見つからない、フォントが反映されない、外部URLやログインが必要な素材の読み込みに失敗するといった問題の原因も確認できます。

使い方の例

  • 「画像、音声、マップをプリロードするリソース一覧を作って」
  • 「読み込み進捗と完了イベントを使って独自のロード画面を表示して」
  • 「ログインが必要な素材を読み込む設定を教えて」

注意点

音声のプリロード前には audio.init() が必要です。外部素材のCORS設定や認証情報は loader.setOptions() で指定します。独自アセットタイプは、素材一覧に加える前に loader.setParser() で登録します。

この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。

含まれるファイル(1)

  • SKILL.md19.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Loading assets

The resource descriptor

Every asset is { name, type, src } plus type-specific extras:

import { loader } from "melonjs";

loader.setOptions({ crossOrigin: "anonymous" });   // needed for remote assets

await loader.preload([
    { name: "tiles",   type: "image",    src: "data/img/tiles.png" },
    { name: "map1",    type: "tmx",      src: "data/map/map1.tmx" },
    { name: "tileset", type: "tsx",      src: "data/map/tileset.tsx" },
    { name: "sfx",     type: "audio",    src: "data/sfx/" },
    { name: "font",    type: "fontface", src: "data/font/PressStart2P.ttf" },
    { name: "atlas",   type: "json",     src: "data/img/atlas.json" },
]);

preload() has been awaitable since 19.8. The callback form still works and takes a third argument to suppress the built-in loading screen:

loader.preload(resources, onLoaded, /* switchToLoadState */ false);

The type list

The type strings are melonJS-specific — this is not a generic loader:

typefor
"image"textures, sprite sheets, normal maps
"json"data, texture-atlas descriptors, collision shapes from a shape editor
"tmx" / "tsx"Tiled maps and external tilesets
"audio"sound — src is a directory, see below
"video"video textures
"binary"raw data, bitmap font .fnt descriptors
"fontface"web fonts (.ttf, .woff)
"aseprite"Aseprite JSON + image
"shader"GLSL/WGSL shader sources — src or inline via data
"obj" / "mtl"Wavefront models and materials
"gltf" / "glb"glTF scenes
"js"scripts
any type you registeryour own formats, and things you generate rather than download — see Custom asset types

What belongs in the manifest — and what cannot

Preloading is not only for things with a URL. Three tiers, and the middle one is the one people miss.

After preloading, switch to a stage of your own. preload() puts you in state.LOADING, and that stage is transitional: build your scene in the callback and stay there, and nothing ever destroys it, so the built-in loading screen's logo and progress bar remain on top of your game. See melonjs-scenes-and-state.

1. Assets with a source. Everything in the type table above. Note "shader" takes inline source as well as a src, via the data field:

import { myRamp } from "./myRamp";   // a GLSL fragment body, in your own module

loader.preload([
    { name: "ramp", type: "shader", data: myRamp },
]);
const fx = loader.getShader("ramp");   // shared, already compiled

This matters more than it looks. A ShaderEffect compiles its program in its constructor, and compileProgram calls getProgramParameter(LINK_STATUS) immediately after linkProgram — a blocking call that forces the driver to finish. So building one during a scene costs that link on the frame it appears, and building three identical ones costs it three times. Preloaded as an asset it is compiled at load time, behind the loading screen, and the loader hands out a shared instance (effect.shared === true), which is what stops one renderable's teardown freeing it out from under the others. clone() does NOT help here — it constructs a new effect, and therefore links a new program.

2. Derived assets — build once, before the first stage. Canvases you bake, NoiseTexture2d, TextureAtlas: none of the built-in types describes these, and they must not be rebuilt per scene either. The renderer's texture cache is keyed by the image object, so a freshly baked canvas is a brand new GPU texture with a new mip chain — rebuild one per stage and a title → play → game-over cycle uploads four copies of the same pixels. Build each once and share it from a module the scenes read from, the way the platformer example builds its TextureAtlas.

Where you build it depends on what it costs. A gradient or a palette strip is well under a millisecond: build it after preload resolves. A bake that takes real time — procedural noise is the usual one: Earth Defender's sky, two fractal simplex fields over a 512² tile, takes about 200ms on a desktop — belongs inside the preload as an asset type of your own (Custom asset types). It then runs behind the loading screen and counts on the progress bar. Built on stage entry instead, it lands on the frame the scene is assembled, and with a transition running that frame is a black screen.

3. The engine's own shader programs — warmed for you, since 20.6. The mesh batcher compiles a program per feature combination (instance colours, instance data, fog — fog joins the key), lazily, on the first draw that needs it. prewarm builds them during preload() instead, behind the loading screen:

const app = new Application(1280, 720, {
    // on by default since 20.6 — named here only to show where it lives
    prewarm: true,
});

You do not call this yourself. preload() calls renderer.prewarm() once the assets are in and before LOADER_COMPLETE fires, so it happens while the progress bar is still up. Every preload() warms, not just the first, so a per-level preload warms whatever that level brought with it.

The one precondition for this part: a game that never calls loader.preload() does not get it. If you load assets another way, call await app.renderer.prewarm() yourself. On WebGL that links the mesh batchers' variants, for the unlit tier and the lit one that inherits from it; on WebGPU it compiles the WGSL module of every shader the loader holds, and the mip-chain blitter when textures are filtered.

4. Everything that depends on the renderable — warmed as you build, since 20.10. The rest cannot be done at preload, because it depends on what draws: on WebGPU a pipeline is keyed by blend mode, culling, transparency and the format of the target as well as by shader, and that compile is the expensive part (tens of milliseconds, up to 140ms measured, per effect). So, behind the same prewarm setting, the engine warms a renderable when it gains an effect (addPostEffect, or assigning shader) and when it is added to the world: its effect pipelines, its own draw, the render targets its chain composites through, the advanced-blend shader if it uses one. Inline effects are covered too; on WebGL that means linking a mesh-hosted effect's spliced program.

That work starts after your code has run and finishes off the frame, so it needs at least a frame between setting a renderable up and drawing it. The pattern that gets it for free is the one you want anyway: build pools (and attach their effects) when the stage starts, then show and hide them during play. Something created and drawn in the same frame still compiles on that frame.

Cost to a game that uses none of it is about 60ms of preload on a desktop GPU. Set it to false only for a target where linking is slow enough that the preload itself would suffer.

What it does not do. It makes the shaders ready earlier. It does not remove the hitch on a scene's first frame, because most of that frame is first-submit work — texture residency, buffer uploads, the driver specializing against real bindings — which compiling a program does not touch. For that, the lever is still to draw the scene once while the loading screen is up and then throw it away; the warmed GPU state stays behind.

await loader.preload(resources);
buildScene(app);                       // same geometry the level uses
await twoFrames();                     // event.GAME_AFTER_DRAW
removeOnlyWhatYouAdded(app);           // keep the programs, drop the scene
state.change(state.PLAY);

It borrows the LOADING stage's world, and that has three teeth, all learned the hard way:

  • Never tear down with app.world.reset(). It clears every child of that world, the loading screen's own included, and the screen goes blank for the rest of the load. Snapshot app.world.children first and remove only what you added.
  • Restore anything the warm-up changes that the loading screen shows — the renderer's backgroundColor above all, or the warm-up flashes its own empty first frame.
  • Under a Camera3d it will hide the built-in loading screen, and there is no clean way around it — see below.

DefaultLoadingScreen lives in the WORLD, not on the screen

The built-in loading screen is written for a Camera2d. Its progress bar and logo are added together, once the logo is decoded (a frame or so after the screen comes up), with app.world.addChild(bar, 1) / addChild(logo, 2), and are floating === false — ordinary world-space renderables. They read as an overlay only because that stage's world is otherwise empty.

So with cameraClass: Camera3d, anything you add to that world is in the scene with them, and a 3D scene simply swallows them: the depth buffer puts terrain in front of a bar sitting at z = 1. What does NOT rescue it, all tried:

  • Raising their z — for a mesh on the Camera3d path pos.z IS world depth, so the scene's own z values are geometry, not layering, and the bar still loses to the depth test.
  • moveToTop() — it early-returns for a child already at index 0, which the loading screen's children usually are, having been added before anything else. It silently does nothing.
  • Flipping them to floating = true for the duration — the scene still covers them.

If you want a warm-up (or any 3D behind a loading screen), write your own loading stage and draw its progress UI as floating renderables over the scene. That is also the nicer result: the player watches the level assemble instead of a logo. Budget it as real work, not a one-liner — and weigh it against what the warm-up actually saves.

One more limit: a warm-up only covers the combinations it actually draws. Renderables the real level has and the warm-up does not will still link on entry.

Two conventions that produce 404s

Audio src is a directory. The loader appends the asset name plus each format from audio.init(), and picks the first that decodes:

audio.init("mp3,ogg");                                  // before preloading audio
{ name: "cling", type: "audio", src: "data/sfx/" }      // → data/sfx/cling.mp3

A full filename here gives you a 404. And audio.init() must run before any audio asset is preloaded.

A Tiled map needs its dependencies listed too — the .tmx, any external .tsx, and the tileset images. Listing only the map produces a blank level.

Retrieving loaded assets

loader.getImage("tiles");
loader.getJSON("atlas");
loader.getTMX("map1");
loader.getGLTF("diorama");
loader.getVideo("intro");
loader.getFont("font");
loader.getBinary("font-fnt");

Most engine classes take the asset name directly, so you often do not need these — new Sprite(x, y, { image: "tiles" }) resolves it for you.

Custom asset types

loader.setParser(type, parserFn) teaches the loader a type of its own. An entry of that type then sits in the preload list like any other: it is one step of the progress bar, and preload() waits for it before LOADER_COMPLETE. Use it for a file format melonJS does not know, and for anything you generate that takes long enough to matter.

import { loader, Sprite } from "melonjs";

const skies = new Map();

// once, before preloading: registering a type twice logs a warning
loader.setParser("sky", (asset, onload, onerror) => {
    bakeSky(asset.name)                 // your generator, returning a Promise
        .then((canvas) => {
            skies.set(asset.name, canvas);
            onload();                   // takes no value, see below
        }, onerror);
    return 1;                           // one asset, settling later
});

await loader.preload([
    { name: "nebula", type: "sky" },    // no src: nothing to download
    // ...the rest of the manifest
]);

// in the stage, later: already baked
const backdrop = new Sprite(0, 0, { image: skies.get("nebula") });

The contract:

  • Call onload() or onerror(error), exactly once, now or later. Return 1 when you will, 0 when there is nothing to load (the loader then moves on without waiting).
  • The loader keeps nothing for a type it does not know. onload takes no value, and loader.getImage and friends will not find your result. Keep it yourself, keyed by asset.name, and read it from there.
  • name and type are a complete entry. src is only for a type that fetches something. data is typed for the built-in uses (a string, or a {glsl, wgsl} pair), so for a generator's settings, keep a table in your own module keyed by name and let the entry carry only the name. That also keeps the preload and the stages from disagreeing on a seed.
  • Base URLs: a src is prefixed only by setBaseURL(yourType, …). The "*" wildcard does not cover custom types.
  • Do not hold the main thread for the whole bake. A parser runs alongside the downloads, and a 200ms synchronous loop freezes the loading screen and stalls every other asset's progress for that long. Bake in slices of a few milliseconds and yield between them, with setTimeout, not requestAnimationFrame, which stops in a background tab and would hold the whole preload with it. Skip the yield while the tab is hidden: a background tab runs timers about once a second, which turns a bake of 25 slices into 25 seconds, and there is no loading screen on show to keep smooth:
const bakeSky = async (name) => {
    const { size, seed } = SKIES[name];         // your recipe table
    const tile = startTile(size, seed);         // set up the canvas and the noise
    for (let y = 0; y < size; ) {
        const start = performance.now();
        while (y < size && performance.now() - start < 8) {
            tile.fillRow(y++);
        }
        if (!document.hidden) {
            await new Promise((resolve) => setTimeout(resolve, 0));
        }
    }
    return tile.finish();
};

The Earth Defender example does exactly this for its two nebula skies (fx/nebula.ts): a "nebula" type, two name-only entries in its manifest, and stages that ask for a sky by name and find it baked.

Base URLs

Set a prefix per asset type rather than repeating it:

loader.setBaseURL("image", "data/img/");
loader.setBaseURL("audio", "data/sfx/");
loader.setBaseURL("*", "https://cdn.example.com/");   // every 2D type at once

The "*" wildcard deliberately skips obj, mtl, gltf and glb: those resolve their own internal references (a map_Kd texture, a glTF .bin) relative to their file, so a global prefix would double-prefix them. Set those types individually.

Progress and completion

Use the events. The old callback properties (loader.onload, onProgress, onError) were deprecated in 18.2 and removed in 20.3 — being let bindings on an ES module namespace they could never be assigned from outside in the first place, so loader.onProgress = fn always threw a TypeError:

import { event } from "melonjs";

event.on(event.LOADER_PROGRESS, (progress) => { /* 0..1 */ });
event.on(event.LOADER_COMPLETE, () => { /* … */ });
event.on(event.LOADER_ERROR, (res) => { /* … */ });

Unloading

loader.unload({ name: "map1", type: "tmx" });
loader.unloadAll();

unloadAll() also releases audio and GPU resources such as shader programs, so it is the right teardown for switching between large levels.

Loading between scenes

The default loading screen appears automatically when preload switches state. For a custom one, register a Stage against state.LOADING:

state.set(state.LOADING, new MyLoadingScreen());

If you preload without a loading stage and then change state immediately, pass forceChange:

state.change(state.PLAY, true);

Cross-origin and authenticated assets

Two settings, both global and both applying to every asset type — there is no per-asset or per-type spelling to remember:

loader.setOptions({
    crossOrigin: "anonymous",   // CORS mode for remote assets
    withCredentials: true,      // send cookies / auth with the request
});

withCredentials is what an asset behind a session cookie or an Authorization-style login needs. Every fetched asset — image, json, binary, tmx, tsx, shader, gltf/glb, obj, mtl, aseprite, video — carries it, and so does buffered audio.

Set them through setOptions. loader.crossOrigin and loader.withCredentials are read-only module bindings; assigning to them throws, the same trap as the removed loader.onload / onProgress.

one streamed clip (stream: true / html5: true / preload: false) ignores withCredentialsit plays through an <audio> element, which needs a crossorigin attribute rather than fetch credentials. Preload it buffered if it is behind auth
credentials silently dropped for audio before 20.4the loader forwarded them under a pre-20.3 name the backend never read — a hung preload rather than an error

Symptom → cause

symptomcause
audio 404ssrc given as a file instead of a directory
audio fails, preload() rejects with "Failed loading resource"audio.init() not called before preloading — the real message is replaced by the loader's
blank level, missing tiles.tsx or tileset image not listed in the resources
cross-origin textures failmissing loader.setOptions({ crossOrigin: "anonymous" })
assets behind a login 401 / audio preload hangsmissing loader.setOptions({ withCredentials: true }) — one switch, all asset types
TypeError assigning loader.withCredentials / crossOriginread-only module bindings; use loader.setOptions({ … })
preload never finishes, blank screen, no errora failing sound with audio.setStopOnAudioError(false) before 20.4 — it disabled audio and failed the preload
text renders in a fallback fontweb font not preloaded as "fontface"
BitmapText renders nothingthe .fnt (as "binary") or its image is missing
TypeError assigning loader.onProgress / onloadremoved in 20.3; they were never writable — use the LOADER_* events
load : unknown or invalid resource typea custom type in the manifest before its loader.setParser call
black or frozen screen when a stage is first entered, the profile shows your own code (noise, a canvas bake)a costly derived asset built on stage entry — bake it inside the preload as a custom asset type
a custom asset preloads but the result is nowhere to be foundonload takes no value and the loader stores nothing for custom types — keep the result in your own module

Related skills

  • melonjs-getting-started — where preloading sits in the bootstrap
  • melonjs-tilemaps — the full Tiled asset set
  • melonjs-audio — formats and the directory convention

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

melonjs

無料概要あり

melonJSを使ったゲーム制作で、目的に合う専門ガイドを選ぶための入口です。初期化から描画、入力、物理演算、音声、2D・3D機能まで案内します。

  • melonJSで作る内容に合うガイドを探したいとき
  • 新しいゲームの初期化手順を知りたいとき
  • 3Dやモデル読み込みの参照先を探したいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs-20-migration

無料概要あり

melonJS 19.x以前の知識を20.x向けに更新し、初期化や描画効果、廃止・非推奨APIの変更点を確認して移行時の不具合を調べられます。

  • 19.x以前のゲームを20.xへ移行したいとき
  • 古いAPIが見つからない原因を調べたいとき
  • 非推奨の描画効果を置き換えたいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs-3d

無料概要あり

melonJSの3D・2.5Dゲーム向けに、カメラ、メッシュ、ライト、奥行き順、透明表現やフォグの設定と座標系の注意点を確認できます。

  • 3Dカメラをステージに設定したいとき
  • メッシュやビルボードを配置したいとき
  • 奥行き順や透明表現を調整したいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs-3d-assets

無料概要あり

melonJSでglTF・GLBやOBJ・MTLの3Dモデルを読み込み、マテリアルやライト、アニメーション、影、インスタンスの扱い方を確認できます。

  • glTF・GLBのシーンを読み込みたいとき
  • OBJモデルとMTL素材を使いたいとき
  • モデルの向きや大きさを調整したいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs-audio

無料概要あり

melonJSで効果音やBGMを読み込み、再生・停止・音量調整するほか、位置に応じた音や音声合成、ミックス全体へのエフェクト設定を扱うスキルです。

  • 効果音とBGMを読み込みたいとき
  • ゲーム内の位置に応じて音を鳴らしたいとき
  • 音声ファイルなしで短い音を作りたいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs-camera-and-drawing

無料概要あり

melonJSのカメラ追従や画面効果、座標変換、図形描画を実装する際の要点を確認できます。描画のクリッピングやCanvasへの焼き込み、ピクセルの読み出しも扱います。

  • プレイヤーを追うカメラの範囲を設定したいとき
  • ワールド座標と画面座標を変換したいとき
  • 独自描画に図形やグラデーションを使いたいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs のスキルをすべて見る

このスキルの問題を報告する