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

melonjs-3d-assets

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

原文原文の説明を見る

Use this skill when loading 3D models into melonJS — glTF and GLB scenes, OBJ/MTL models, materials, imported lights, node animation, ground shadows and GPU instancing. Covers level.load options including the async flag, the rightHanded conversion, and exactly what the loader does and does not support. Triggers on: glTF, gltf, glb, OBJ, MTL, 3D model, getGLTF, getOBJ, getMTL, GLTFModel, GLTFScene, level.load glb, rightHanded, lightIntensityScale, castGroundShadow, shadowGroundY, EXT_mesh_gpu_instancing, KHR_lights_punctual, skinning, Blender export, 3D asset.

melonjs/melonJSスター 6,429(GitHub 6,429 + cccskills 0)
連携ツールBlender

実行スコア 未検証

インストール方法を見る

こんなときに便利

  • glTF・GLBのシーンを読み込みたいとき
  • OBJモデルとMTL素材を使いたいとき
  • モデルの向きや大きさを調整したいとき
  • 影やアニメーションの制約を調べたいとき

日本語での紹介

できること

melonJSで3Dアセットを読み込む手順と、読み込み後に利用できる機能や制約を確認できます。glTF・GLBはシーンとして読み込み、OBJ・MTLは主に単体の静的モデルとして扱います。level.loadのスケール、読み込み完了の待ち方、座標系の変換、ライトや地面の影の設定も説明します。glTFのノードアニメーションやGPUインスタンス、MTLのテクスチャ指定など、形式ごとの扱いも分かります。

こんなときに便利

3Dモデルが左右反転する、サイズが合わない、プレビューより質感が平坦になるといった問題の原因を調べたいときに役立ちます。アニメーション付きモデルや多数の同じオブジェクトをシーンに配置する場合にも使えます。

使い方の例

  • 「このGLBシーンをmelonJSに読み込む設定を教えて」
  • 「OBJモデルにMTLのテクスチャを適用する方法を確認して」
  • 「読み込んだキャラクターが変形しない理由を調べて」

注意点

Draco圧縮された形状は読み込みに失敗します。頂点スキニング、モーフターゲットなど、読み込まれない機能があります。地面の影にはGPUバックエンドとCamera3dが必要です。

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

含まれるファイル(1)

  • SKILL.md25.8 KB

SKILL.md(原文)

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

Loading 3D assets

glTF/GLB is the primary path for 3D content, and it goes through the level director rather than the world directly: level.load("scene"), the same call a Tiled map uses. OBJ/MTL is the simpler path for a single static model.

Loading a glTF/GLB scene

await loader.preload([
    { name: "diorama", type: "glb", src: "data/scenes/diorama.glb" },
]);

level.load("diorama", { scale: 50 });

type: "gltf" (JSON plus external buffers) and type: "glb" (single binary) are both loader types. A self-contained GLB is the better shipping format — one request, nothing to resolve relatively.

The scene loads under whatever camera the stage has; view it under a Camera3d or it renders flat. See melonjs-3d for the camera.

level.load options for glTF/GLB

optiondefaultwhat it does
scale1pixels per glTF unit, applied to the whole scene. Blender's metre-scale export usually needs 20–100.
containergame.worldwhere the nodes are added
onLoadedgame.onLevelLoadedcalled with the level id, not the scene
asyncfalsereturn a promise that settles once the scene is in the world, instead of a boolean
rightHandedtruesee below
lightstrueinstantiate authored KHR_lights_punctual lights as Light3d world children
lightIntensityScale—keep authored intensity ratios instead of normalising every light to 1

Those game. defaults are the engine's, not a recommendation. game is whichever Application finished init() last, so pass container explicitly from a Stage, where the right one is in your hand:

level.load("sceneId", { container: this.app.world, scale: 40 });

| castGroundShadow | inherits the app setting (on) | blob shadows for this scene's meshes | | shadowGroundY | each object's own base | world Y of the floor the blobs land on | | shadowOpacity | 0.45 | how dark the blobs are | | shadowScale | 1 | blob footprint multiplier; 0 or less hides them | | shadowOffset | 0 | slide them along the light, in blob radii; needs shadowGroundY, which is the only way a scene load can name its floor | | shadowStretch | 1 | lengthen them along it, clamped to 3 | | shadowDirectionX / shadowDirectionZ | 0 | the ground direction they are cast along | | shadowGroundNormal | world up | up normal of the floor, for a scene that is not level | | shadowLight | — | a Light3d to take the direction from, re-read every draw |

shadowGroundPoint is not on that list, and a scene load drops it without a warning. One floor POINT shared by every part of a scene would stack the whole thing's blobs on a single spot, which is nonsense where one floor HEIGHT is perfectly sensible. Ground that is not flat is a per-object answer: set it on the parts you want once the load has finished.

onLoaded receives the level id — it is a "done" signal, not a handle on the scene. You need it, or async: with the game loop running, level.load stops the loop and defers the actual load to a timer, so by default it returns before anything is in the world.

await level.load("diorama", { scale: 50, async: true });
// the scene is in the world here

Note await level.load("diorama") without the flag does not await the load — the call returns a boolean, and await true resolves immediately. To get at what was loaded, load into a container you own, or look the nodes up by their authored names:

level.load("diorama", { scale: 50, onLoaded: () => {
    const sun = app.world.getChildByName("Sun")[0];   // note: returns an array
    sun.intensity = 2;
}});

The same options from a Tiled Trigger

A Trigger authored in Tiled forwards these to level.load(), so a trigger that walks the player into a glTF level carries the scene's settings as Tiled properties:

world.addChild(new Trigger(x, y, {
    shapes: [new Rect(0, 0, 100, 100)],
    to: "diorama",              // the level id — `to`, not `level`
    scale: 50,
    rightHanded: false,
    castGroundShadow: false,
}));

Every level.load option above travels, including the whole ground-shadow set, and so do container, onLoaded, flatten and setViewportBounds. The list is derived from level.loadOptions rather than restated, so an option added to a level format arrives here for free. It used to be hand-written and had fallen three releases behind the glTF path (#1649).

async is deliberately excluded. A trigger with a color + duration sets it itself, because it needs the promise to sequence its fade and reveal; a trigger without one loads fire-and-forget and never sets it. Either way an authored async property is ignored rather than honoured.

rightHanded — and the default that differs between the two APIs

glTF is authored Y-up, right-handed; melonJS is Y-down, +Z forward. There are two ways to bridge that, and they are not equivalent:

  • a rotation — preserves chirality, matches the authoring tool
  • a mirror (negate Y only) — flips the scene left/right

level.load defaults rightHanded: true, so a scene loaded through the level director comes in correctly. The Mesh constructor defaults it to false, so glTF or OBJ geometry you feed to a Mesh yourself comes in mirrored unless you pass rightHanded: true. A model that is subtly wrong — text backwards, a character's gear on the wrong side — is this and nothing else.

Do not add a flip of your own on top; two corrections cancel back to wrong.

Materials — what actually arrives

The importer covers a deliberate subset:

  • pbrMetallicRoughness base colour texture and factor
  • metallic/roughness factors converted to the engine's specular model
  • emissiveFactor, including KHR_materials_emissive_strength
  • KHR_materials_unlit — honoured, for baked/stylised workflows
  • alphaMode: "MASK" — the cutout threshold is applied
  • doubleSided

Not imported: normal maps, metallic/roughness textures, occlusion maps, morph targets, KHR_texture_transform. Nothing errors — the material simply arrives without them, which reads as "flatter than the preview in my authoring tool". Bake what you need into the base colour.

Draco compression is the exception that does error. Its geometry sits in accessors with no buffer view, which the parser rejects outright, so the whole asset fails to load. Export uncompressed.

textureFilter on the Application settings ("nearest" / "linear" / "auto") drives sampling; glTF materials that declare their own sampler carry it through. Pixel-art models want "nearest".

baseColorFactor is LINEAR; a tint is sRGB

Pass the factor as a setting and the encoding is done for you. Mesh takes baseColorFactor — [r, g, b] in 0..1, linear, which is the form the loader hands back — so a mesh built from a node passes the node's own field straight through:

const node = loader.getGLTF("palm").nodes[0];
const mesh = new Mesh(x, y, {
    vertices: node.vertices, normals: node.normals,
    uvs: node.uvs, indices: node.indices, normalize: false,
    baseColorFactor: node.baseColorFactor,   // encoded, not scaled
});

The setting's type admits undefined, because the loader declares the field optional and that line has to compile under exactOptionalPropertyTypes. Sprite3d forwards it too, and mesh.setBaseColorFactor(f) applies one after construction. For an ordinary sRGB colour use setTint instead — the rest of this section is why the two are different.

The loader handles this for you. It only bites when you build meshes yourself from loader.getGLTF(name).nodes — which is what you do for per-node control, since there is no public per-node Mesh factory — and reach for the obvious * 255:

// WRONG — every untextured material renders far too dark
mesh.tint.setColor(f[0] * 255, f[1] * 255, f[2] * 255, 1);

glTF defines pbrMetallicRoughness.baseColorFactor in linear space (spec 3.9.2), while a melonJS tint is 8-bit sRGB — the same space as a CSS colour or a texel out of a PNG. They are not the same number, and the gap is widest exactly where art tends to live:

linear* 255correct
0.051363
0.2051124
0.42107173
0.72184221
1.00255255

Color.setLinear does the encoding for you — it is the linear-space sibling of setFloat, which takes the same 0..1 range but treats it as already-sRGB:

mesh.tint.setLinear(...node.baseColorFactor);   // encodes; what you want
mesh.tint.setFloat(...node.baseColorFactor);    // does NOT encode; too dark

It clamps, so the slightly out-of-range factors some exporters emit cannot turn into NaN, and it keeps the encoded value as a float rather than rounding through 8 bits.

Measured on a scene whose every material went through * 255: mean frame luma 85.5 against 147.8 once encoded, with each channel sitting 60 to 73 counts low.

Fix it early. It is nearly invisible as a bug, because the scene still looks coherent — just moody — so lighting and bloom thresholds get tuned against the wrong values. Correcting the encoding afterwards blows the frame out and forces a lighting retune, which is a much bigger job than the encode.

This applies only to factors coming out of glTF. Colours you author for the screen, a HUD tint or a glow, are already sRGB: * 255 is right for those.

Lights

Authored KHR_lights_punctual lights — sun, point, spot — become Light3d world children, plus a soft ambient fill, and each keeps its authored name. A scene carrying any of them renders through the lit path automatically.

glTF stores physical units — lux for suns, candela for lamps — and a Blender daylight sun is 1000+, which blows out the engine's stylised shading. So every light is normalised to intensity 1 by default. To keep the ratios the artist set, scale instead of normalising:

// a 1000-lux sun lands at 1, a 500-lux fill lands at 0.5
level.load("diorama", { scale: 50, lightIntensityScale: 0.001 });

Pass lights: false to manage lighting yourself.

Animation

Node-TRS animation is supported: translation, rotation and scale channels, the rigid hierarchical animation used by blocky/low-poly character packs. An animated asset instantiates as a single GLTFModel keeping the node hierarchy intact, with an API deliberately mirroring Sprite. The model carries the asset name, which is how you get hold of it after loading:

const model = app.world.getChildByName("hero")[0];   // the GLTFModel

model.getAnimationNames();                           // ["idle", "walk", ...]
model.setCurrentAnimation("walk", { loop: true, speed: 1, onComplete });
model.setCurrentAnimation("jump", { next: "idle" }); // chain
model.isCurrentAnimation("walk");
model.play();
model.pause();

speed is a playback multiplier (1 = authored speed), not a frame delay. Only an explicit loop: false stops looping. setCurrentAnimation throws on a clip name the asset does not define — check getAnimationNames() first.

Placing and moving a model

A GLTFModel is placed like any other renderable — pos, depth, rotate and scale move the whole rig, and compose with whatever the active clip is doing, so a walk cycle plays wherever the character stands:

const boat = new me.GLTFModel(me.loader.getGLTF("boat"), { scale: 40, lit: false });
boat.setCurrentAnimation("paddle", { loop: true });
app.world.addChild(boat);

boat.pos.set(steerX, waterLevel);
boat.depth = travelled;
boat.rotate(lean - lastLean, AXIS_Z);   // `rotate` is RELATIVE — feed it the delta
boat.animationspeed = 0.85 + 0.9 * pace; // one authored tempo, many run speeds

Constructing one directly from loader.getGLTF(name) is fine and is the way to go when the model is a game object rather than a scene — level.load is for loading a whole scene into the world.

Vertex skinning is out of scope — JOINTS_0 / WEIGHTS_0 are not read, and neither are morph targets. A smoothly-deforming character will not deform. The options are: rig it hierarchically instead (separate meshes parented into a node tree), use Sprite3d billboards, or use the Spine plugin for 2D skeletal work.

Instancing

A node using EXT_mesh_gpu_instancing loads as an InstancedMesh — one draw call for every copy, which is the difference between a hundred trees and a hundred thousand. Export with instancing enabled where your tool supports it. Sparse accessors on the instancing attributes are rejected with a clear error rather than silently misplacing every instance.

Ground shadows

level.load("diorama", { scale: 50, castGroundShadow: true, shadowGroundY: 0 });

castGroundShadow is tri-state: set it and it overrides the application setting for this scene in both directions; omit it and the scene inherits — and that setting is true by default, so shadows are already on. As a scene-wide opt-in it skips nodes with no vertical extent — a ground plane is exactly that, and shadowing it with itself smears a blob across the whole floor. Blob shadows need a GPU backend and a Camera3d; nothing is drawn on the 2D-camera or Canvas paths.

Omit shadowGroundY and each blob sits at its own object's base at full strength, which is right for props already resting on the ground. Set it when things jump or fly, so the shadow stays on the floor and shrinks with height.

A blob sits on its caster's x/z by default, so a wide flat-bottomed prop hides its own shadow under itself. The knob for that is shadowScale, which widens the blob until its edge shows past the prop, or shadowOffset plus a direction, which slides it clear. Not shadowGroundY: raising that floats the blob up over the object as a dark ring instead of sliding it out, because it is for things that leave the ground, not a visibility control.

For a light that is not overhead, name one: shadowLight takes the direction from a Light3d and is re-read every draw, so a day or night cycle carries every shadow in the scene with it. Nothing is inferred — a scene with no named light and no shadowDirectionX/Z gets no direction, because a scene with several lights has no non-arbitrary answer. shadowStretch lengthens the blob along that direction and fades it as it pulls; shadowGroundNormal lays it on a floor that is not level. All of these apply to an InstancedMesh too, as one value for the whole set.

OBJ/MTL

A different shape entirely: OBJ produces raw geometry you hand to a Mesh, not a scene. Preload the two files and name them in the Mesh settings — the model and material names are independent, the same name for both is just a convention:

await loader.preload([
    { name: "fox", type: "obj", src: "models/fox.obj" },
    { name: "fox", type: "mtl", src: "models/fox.mtl" },
]);

const mesh = new Mesh(400, 300, {
    model: "fox",
    material: "fox",        // texture resolved from the material's map_Kd
    width: 200,
    height: 200,
    rightHanded: true,      // the Mesh default is false — see above
});
world.addChild(mesh);

The MTL loader fetches the textures it references itself — map_Kd, map_d and map_bump are resolved relative to the .mtl and preloaded for you, so there is no image entry in the manifest above. Passing texture: explicitly overrides that and pins one binding over the whole model, which on a multi-material model suppresses the per-material texture split; leave it out unless you mean that.

MTL contributes Kd (diffuse tint), d / Tr (opacity), Ke (emissive), Ks + Ns (specular highlight), map_Kd (diffuse texture), map_d (per-texel opacity) and map_bump / bump / norm (tangent-space normal map — needs lit: true, and the tangent frame is derived per fragment, so the model needs no tangent attribute). All three normal-map spellings are read as normal maps even though the format specifies two of them as height maps, which is what every exporter actually writes. Specular maps (map_Ks) and the PBR extension's map_Pr / map_Pm are not supported and warn — the Pr / Pm scalars are read and approximated onto the specular terms, the maps would need a real PBR model.

Ka (ambient) and illum (illumination model) are read onto the material and readable from loader.getMTL(name), but take no part in shading. That is deliberate rather than pending: Ka predates scene-wide ambient light and would fight Stage.ambientLightingColor, and illum enumerates fixed-function behaviours this renderer has no equivalent for. In particular an illum of 0 or 1 nominally means "no specular", and the engine does not honour that — Ns stays the only gate, so a model that renders correctly today is not restyled by a field its exporter wrote inconsistently.

loader.getOBJ(name) and loader.getMTL(name) return the parsed data if you want it directly. OBJ has no scene graph, no lights, no animation — use glTF for anything beyond a single static model.

Authoring assets for the engine

Notes from modelling props and a rigged character against this loader. They are Blender-specific in the API details, general in the traps.

One clip means one action

glTF names an animation after the action it came from, so three objects each carrying their own action export as three separate clips — and a game can only play one clip at a time, so the other two never move. Put every animated object in one action, using slots (Blender 4.4+):

shared = bpy.data.actions.new("paddle")
for ob in (pivot, shoulder_l, shoulder_r, body):
    ad = ob.animation_data_create()
    ad.action = shared
    ad.action_slot = shared.slots.new('OBJECT', ob.name)

Exporting with the scene/whole-timeline mode does not merge per-object actions into one clip — it still emits one per action. Slots are the fix.

In Blender 5.x, action.fcurves no longer exists; curves live under action.layers[].strips[].channelbags[].fcurves, and a channelbag identifies its slot as cb.slot.identifier (there is no slot_identifier).

Never re-parent or join a rig that is mid-pose

Both parenting and joining bake the objects' current world transforms. Parenting a child while the parent sits in an animated pose writes the inverse of that pose into matrix_parent_inverse, which silently cancels the animation — the part renders rigid while the curves still exist. Detach the action, zero the rig, do the surgery, then reattach:

saved = [(o, o.animation_data.action_slot) for o in animated]
for ob, _ in saved:
    ob.animation_data.action = None
    ob.rotation_euler = (0, 0, 0)
# ...re-parent / join here...
for ob, slot in saved:
    ob.animation_data.action = act
    ob.animation_data.action_slot = slot

A joined mesh also inherits the active object's scale, so joining into a box that was scaled to (0.8, 2.0, 0.1) leaves a node with that non-uniform scale and geometry divided back through it. It renders in the right place but skews flat-shaded normals — apply transforms (rotation=True, scale=True) after joining. Watch for a related trap: setting ob.scale.x on a mesh whose size was already applied multiplies rather than replaces it.

A model only needs to stay split where something animates it

Every glTF primitive is a draw call. Merge everything static into one mesh and keep separate nodes only for the parts a clip actually drives — a boat with a rabbit and a paddle went from 28 primitives to 5 that way, with no visible change.

One palette strip beats one material per colour

An InstancedMesh is one geometry and one material, so a prop that wants five colours cannot use five materials. Give the whole model a single material whose image is an N×1 strip, one pixel per shade, and point each face's UVs at a cell centre (u = (cell + 0.5) / N, v = 0.5) with the sampler set to nearest. A whole scene's palette then costs one tiny texture, a merged mesh can be twenty colours, and there is nothing to bleed between cells.

Colour-space trap when generating that strip programmatically: Blender's image.pixels are scene-linear floats, but this export path writes them out verbatim, so pre-converting sRGB→linear darkens every colour. White is the tell — it is the one value identical in both spaces, so if white survives and everything else came out dark, that is the bug. Verify by decoding the PNG back out of the GLB and comparing bytes to the source palette.

Feeding an authored mesh into an InstancedMesh

EXT_mesh_gpu_instancing covers instances authored in the file. When the game places them itself, take the geometry off the parsed descriptor instead:

const node = me.loader.getGLTF("palm").nodes[0];
const trees = new me.InstancedMesh(0, groundY, {
    vertices: node.vertices, uvs: node.uvs,
    normals: node.normals, indices: node.indices,
    texture: palette, textureFilter: "nearest", scale: 40,
    instanceCount: count,
});

nodes is one entry per primitive, each in its own local space with a separate world matrix — so this only works cleanly when the asset is a single merged primitive exported with an identity node transform. Model it at the origin, base on the floor, and apply transforms before exporting.

One geometry stamped out hundreds of times reads as one object copied hundreds of times, so vary each instance in its transform. Rotate after translating, or the instance swings around the group origin instead of turning where it stands:

placement.identity().translate(x / s, -y / s, z / s)
         .rotate(math.randomFloat(0, Math.PI * 2), AXIS_Y);
const j = math.randomFloat(0.82, 1.18);
placement.scale(j, j, j);

Sizing every prop against one scale constant, rather than giving each its own, keeps "this looks wrong" a modelling question instead of a scaling one.

Packaging

A self-contained GLB is one file with nothing to resolve — the safest thing to ship. External references still work in both formats: .bin buffers and image uris are resolved relative to the asset's own URL (a GLB with an external texture loads as-shipped). But an external file that 404s rejects the parse, so the whole asset fails to load rather than arriving partially — no untextured fallback. Note that loader.setBaseURL("*", …) deliberately skips gltf, glb, obj and mtl for this reason; set those types individually if you need a prefix.

Symptom → cause

symptomcause
model mirrored — text backwards, gear on the wrong sideMesh defaults rightHanded to false; pass true
model upside downa manual axis flip on top of the loader's conversion
scene is enormous or invisibly smallscale left at 1 against a metre-scale export
the asset fails to load entirelyan external buffer/image uri that 404s, or Draco-compressed geometry
everything is blown out whiteauthored lux/candela intensities; use lightIntensityScale
materials flatter than the authoring-tool previewno normal or metallic/roughness textures are imported
a character does not deformvertex skinning is out of scope; rig hierarchically or billboard
animation names come back emptythe asset has no node-TRS channels (skin-only rig)
a hundred copies tank the frame rateexported without EXT_mesh_gpu_instancing
a whole scatter looks like one object repeatedevery instance shares the group transform — vary yaw/scale per instance, rotating after the translate
three clips where you authored oneone action per object; use a single action with a slot each
one part of a rig renders rigid while its curves existit was re-parented mid-pose, baking the inverse into matrix_parent_inverse
a merged mesh lights wrong along one axisit inherited the active object's non-uniform scale on join — apply transforms
an authored palette comes out uniformly dark, but white is correctlinear values written to a strip that is saved verbatim
every untextured glTF material renders too dark, white unaffectedbaseColorFactor is linear and a tint is sRGB — tint.setLinear(...), not * 255 or setFloat
getGLTF(name).nodes[0] geometry lands in the wrong placenodes is per primitive, each with its own world; merge to one primitive and export at the origin
a prop casts no visible shadowwide and flat-bottomed — the blob is under it; shadowGroundY haloes it rather than revealing it
shadows only show on casters near the camerashadowGroundY is on the wrong side — Y-down means the floor is a greater y, so a pos.y - lift puts the blob inside the caster and the depth test leaves only a hairline ring
a shadow smeared across the whole floora ground plane cast its own blob — use the scene-wide opt-in
onLoaded gets a string, not the sceneit is called with the level id; load into your own container instead
scene renders flat and unlitno Camera3d — the 2D-camera path is CPU-projected and unlit (with a Camera3d on Canvas you get a black canvas instead)

Related skills

  • melonjs-3d — conventions, Camera3d, meshes, Light3d, instancing, and camera.setFog (an outdoor scene almost always wants it; it is set on the camera, so a loaded scene needs no per-node work)
  • melonjs-lighting — how Light3d behaves once imported
  • melonjs-loading-assets — the loader, asset types and base URLs
  • melonjs-plugins — the Spine plugin, for skeletal characters

レビュー

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

コメント

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

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

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。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-audio

無料概要あり

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

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

melonjs-camera-and-drawing

無料概要あり

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

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

melonjs-deployment

無料概要あり

melonJSゲームのWeb向けビルドやネイティブアプリ化、ミニゲーム配信に向けた準備を進め、ブラウザー対応や容量の注意点も確認できます。

  • 配布用のゲームプロジェクトを作りたいとき
  • iOSやAndroid向けにアプリ化したいとき
  • 古いブラウザーへの対応を検討したいとき
melonjs/melonJS6,4292026年10月29日 更新

melonjs のスキルをすべて見る

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