跳到正文
cccskills
免费在 GitHub 公开

melonjs-sprites-and-animation

Create melonJS sprites from images or texture atlas regions, set up frame animations, and adjust tint, transparency, and flips. The guide also covers nine-slice panels and sprite positioning, including how the default centered anchor affects coordinates.

中文简介正在准备中。当前显示英语简介。

原文查看原文说明

Use this skill when creating sprites, playing frame animations, or working with texture atlases in melonJS. Covers Sprite construction from a plain image or an atlas region, addAnimation/setCurrentAnimation, TextureAtlas for TexturePacker/ShoeBox/Aseprite, tint, flip, NineSliceSprite, and the centred anchorPoint that offsets naive positioning. Triggers on: Sprite, new Sprite, addAnimation, setCurrentAnimation, isCurrentAnimation, TextureAtlas, TexturePacker, Aseprite, ShoeBox, atlas, region, framewidth, frameheight, tint, flipX, flipY, NineSliceSprite, animation.

melonjs/melonJS星标 6,429(GitHub 6,429 + cccskills 0)

实测评分 未实测

查看安装方法

适用场景

  • Create a sprite from an image or atlas region
  • Set up idle, walking, or one-shot animations
  • Fix sprite placement using the anchor point
  • Stretch a panel while keeping its corners intact

包含的文件(1)

  • SKILL.md8.1 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Sprites, animation and atlases

Creating sprites from images or atlases, driving frame animation, and the positioning trap that catches almost everyone.

pos is the centre, not the top-left

Renderable.anchorPoint defaults to (0.5, 0.5), so a sprite's pos is its centre. Container forces (0, 0) instead, which is why containers and sprites appear to disagree.

// a 64×64 sprite at (100, 100) covers 68..132, not 100..164
const s = new Sprite(100, 100, { image: "hero", framewidth: 64, frameheight: 64 });

s.anchorPoint.set(0, 0);   // switch to top-left positioning if you prefer

Writing top-left maths against the centred default puts everything half a sprite off, with no error. It is the most common source of "close but subtly wrong" placement.

Constructing a sprite

From a plain image:

const hero = new Sprite(x, y, {
    image: "hero",          // preloaded asset name
    framewidth: 32,
    frameheight: 32,
});

From a texture atlas region:

const atlas = new TextureAtlas(loader.getJSON("sprites"), loader.getImage("sprites"));
const coin = new Sprite(x, y, { image: atlas, region: "coin_01.png" });

framewidth / frameheight define the animation grid on a sheet. Omit them and the whole image is one frame.

Animation

hero.addAnimation("idle", [0, 1, 2, 3], 100);   // frames, ms per frame
hero.addAnimation("walk", [4, 5, 6, 7]);
hero.addAnimation("die",  [8, 9], 150);

hero.setCurrentAnimation("walk");
hero.setCurrentAnimation("die", "idle");                          // chain to idle when it ends
hero.setCurrentAnimation("die", { loop: false });                 // play once, hold the last frame
hero.setCurrentAnimation("die", { loop: false, onComplete: fn }); // …and call fn when it does
hero.setCurrentAnimation("use", { next: "idle", onComplete: fn }); // call fn, then chain to idle
hero.setCurrentAnimation("run", { onFrame: (frame) => {            // each frame playback reaches
    if (frame === 3 || frame === 8) footstep();
} });

if (hero.isCurrentAnimation("walk")) { /* … */ }

Frame indices are into the framewidth/frameheight grid. With an atlas, use atlas.getAnimationSettings([regionNames]) and spread it into the Sprite settings, or atlas.createAnimationFromName([regionNames]), which returns a ready-made Sprite. On a fixed-cell spritesheet only numeric indices are accepted — passing region names throws.

The second argument to setCurrentAnimation is polymorphic. A string chains to that animation when this one ends. An options object takes { loop, next, speed, onComplete, onFrame }, and only an explicit loop: false stops the looping. A bare function is the legacy form: it fires at every cycle end, and only holds the last frame if it returns false. Omit the argument entirely and the animation loops forever.

onFrame(frame) is called with frame 0 when the animation starts, then with each frame playback reaches, so a footstep or a hit lands on its frame without polling getCurrentAnimationFrame(). A long tick reports every frame it goes past. It is not called for a frame set with setAnimationFrame, nor for the frame 0 a play-once animation would wrap to. Keep the callback in a field when you call setCurrentAnimation every frame: re-selecting keeps the first one anyway.

With both next and onComplete, the callback runs first and the chained animation starts with none of its own, so a one-off action (a pickup, an attack) needs no manual switch back to idle.

Re-selecting the animation that is already current is a no-op, so calling setCurrentAnimation every frame is safe, and an isCurrentAnimation check around it adds nothing. An unknown id throws.

Texture atlases

TextureAtlas reads several formats:

sourcehow
TexturePacker / Free Texture Packer JSONnew TextureAtlas(loader.getJSON(name), loader.getImage(name))
Aseprite JSON exportsame as TexturePacker — the format is detected from meta.app
Aseprite .aseprite / .ase binaryloader type "aseprite", which stores the composited image and a JSON sidecar under the one asset name
ShoeBoxJSON export using the melonJS exporter settings file; same call as TexturePacker
fixed-cell spritesheetnew TextureAtlas({ framewidth, frameheight, anchorPoint }, loader.getImage(name))
multi-pagepass an array of atlas JSON objects as the first argument

Helpers: createSpriteFromName(name), createAnimationFromName([names]), getAnimationSettings([names]).

Atlases matter for performance: the GPU batchers bind several textures at once (up to renderer.maxTextures), so a handful of separate images still batch — but once a frame crosses that many distinct textures the batch has to flush. Packing into one atlas keeps everything on a single binding.

Visual properties

sprite.setTint("#ff8000");   // multiply tint
sprite.alpha = 0.5;
sprite.flipX(true);                   // mirror horizontally
sprite.flipY(true);
sprite.blendMode = "additive";        // see the effects skill for the full list
sprite.scale(2, 2);                   // ← multiplicative, see below

scale() is multiplicative, not absolute. Calling it every frame compounds. For absolute scaling reset first:

sprite.currentTransform.identity();
sprite.currentTransform.scale(s, s, 1);

This bites hardest with pooled sprites, which arrive carrying their previous life's transform.

NineSliceSprite

For panels and dialogue boxes that stretch without distorting their corners:

new NineSliceSprite(x, y, {
    image: "panel", width: 300, height: 120,
    insetx: 12, insety: 12,     // lowercase x/y — `insetX` is silently ignored
});

width and height are mandatory and the constructor throws without them. The insets default to a quarter of the frame.

Pooling

Frequently spawned sprites should be pooled:

import { createPool } from "melonjs";

const bulletPool = createPool((x, y) => {    // me.pool is deprecated
    const instance = new Bullet(x, y);
    return { instance, reset: (x, y) => instance.onResetEvent(x, y) };
});
const b = bulletPool.get(x, y);             // new Bullet(x, y), or reset(x, y) on a reused one
world.removeChild(b);                       // returns it to bulletPool automatically

Two consequences:

  • A recyclable class's onResetEvent must reset everything its lifetime mutates — alpha, tint, scale, animation state. Anything you forget carries over to the next use.
  • Pool-recycled objects do not fire onDestroyEvent on removal. Pair event subscriptions with onActivateEvent / onDeactivateEvent instead.

Symptom → cause

symptomcause
everything offset by half a spritetop-left maths against the centred anchorPoint
sprite grows every framescale() is multiplicative — reset the transform
a recycled sprite keeps its old lookonResetEvent does not restore every mutated property
event handlers leak on pooled objectsbound in the constructor rather than onActivateEvent
animation never advancesno framewidth/frameheight, so the sheet is one frame
animation plays once and stops{ loop: false }, or a callback that returned false
sprites do not batchmore distinct textures in one frame than renderer.maxTextures
nine-slice corners stretch anywayinsetX/insetY instead of insetx/insety

Related skills

  • melonjs-renderables — anchors, draw order, custom draw
  • melonjs-getting-started — preloading images and atlases

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

melonjs

免费

Use this entry point to find the melonJS guide for a game development task, from application setup and scenes to sprites, input, physics, audio, and 3D. It also points to companion packages and the API index when the guides do not cover a need.

中文简介正在准备中。当前显示英语简介。

  • Choose a guide for a melonJS game task
  • Find the right setup steps for a new game
  • Locate guidance for a 3D scene or imported model
melonjs/melonJS64292026年10月29日 更新

Update older melonJS code for version 20.x. Review the asynchronous Application setup, renderer changes, post-effect API, and deprecated exports so you can identify outdated patterns when migrating or debugging expected APIs.

中文简介正在准备中。当前显示英语简介。

  • Migrating a game from melonJS 19.x
  • Checking why an older API is missing
  • Replacing deprecated shader assignments
melonjs/melonJS64292026年10月29日 更新

Build 3D or 2.5D scenes in melonJS with cameras, meshes, billboards, lights, shadows, and depth sorting. This guide explains the engine’s Y-down and +Z-forward coordinates, camera setup, transparency, and fog, and identifies features that require a GPU renderer instead of the Canvas fallback.

中文简介正在准备中。当前显示英语简介。

  • Setting up a Camera3d for a game stage
  • Placing meshes, billboards, or repeated instances
  • Fixing depth order or transparency in a 3D scene
melonjs/melonJS64292026年10月29日 更新

Load glTF, GLB, or OBJ/MTL assets into melonJS and understand how their materials, lights, animations, shadows, and instances are handled. The guide explains loader options and common format limitations so you can configure scenes and diagnose missing or incorrectly rendered asset features.

中文简介正在准备中。当前显示英语简介。

  • Loading a glTF or GLB scene into a melonJS level
  • Adding a static OBJ model with its MTL material
  • Checking why imported materials or lights look different
melonjs/melonJS64292026年10月29日 更新

Manage sound in melonJS games: load audio, play effects and looping background tracks, control volume, and place sounds in the game world. It also covers generated tones and noise, whole-mix effects, and common causes of silent playback.

中文简介正在准备中。当前显示英语简介。

  • Loading sound files and choosing supported audio formats
  • Playing sound effects and looping background music
  • Positioning sounds around a player or game object
melonjs/melonJS64292026年10月29日 更新

Use this skill to control melonJS cameras and draw shapes, lines, and gradients. It covers target following, camera bounds and effects, coordinate conversion, clipping, rendering into a canvas target, and reading rendered pixels for images or data.

中文简介正在准备中。当前显示英语简介。

  • Making a camera follow a player within world bounds
  • Converting pointer or 3D positions to screen coordinates
  • Drawing shapes or gradients in a custom draw method
melonjs/melonJS64292026年10月29日 更新

查看 melonjs 的全部技能

报告此技能的问题