mcpField
P
pixijs-rendering

pixijs-rendering

github.com/gamedev-skills开发5.4k6.0k 次安装今天更新
代码仓库

关于 pixijs-rendering

面向 PixiJS 8.21 开发者的渲染技能,涵盖异步应用初始化、资源加载、场景图、动画循环和指针交互,并说明 v8 API 的用法。

主要功能

  1. 01异步初始化 Application
  2. 02使用 Assets 加载资源
  3. 03组织 Container 场景图
  4. 04通过 ticker 驱动动画
  5. 05配置指针事件与渲染组

适用场景

  1. 01启动 PixiJS v8 应用并显示画布
  2. 02加载纹理并构建场景对象
  3. 03实现帧率无关的动画
  4. 04处理鼠标、触控或拖动交互

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill pixijs-rendering

安装后在 Claude Code 中输入/pixijs-rendering确认已加载。

附带文件references/assets-and-display.md

详细介绍

PixiJS 8.21 渲染

搭建并组织 PixiJS 8.21 应用:异步 Application、通过 Assets 加载资源、Container/Sprite 场景图、ticker 循环、指针事件和渲染组。固定使用 8.21 API(异步 init、统一的 Assets、eventMode)。

适用场景

  • 开始开发 PixiJS v8 项目、修复空白画布、组织显示列表、加载纹理、使用 ticker 制作动画或处理指针输入时使用。
  • 当 package.json 依赖 pixi.js(v8),且代码使用 import { Application } from 'pixi.js' 时使用。

不适用场景: Phaser 的场景/加载器模型 → phaser-core。3D 场景 → threejs-scene-setup。PixiJS v7 及更早版本的代码(同步 new Application({...})、Loader、beginFill/endFill)需要先迁移到 v8;本技能仅针对 v8。(v8 中 interactive = true 仍可作为 eventMode = 'static' 的别名使用,但建议显式设置 eventMode。)

核心工作流

  1. 创建并 await Application。 在 v8 中,new Application() 是空实例;需要通过 await app.init({...}) 完成配置。将 app.canvas(而非 app.view)添加到 DOM。使用打包工具时,请将顶层 await 放入异步函数中。
  2. 使用 Assets 加载资源。 await Assets.load(url) 会返回一个 Texture。要加载多个资源,请注册清单/资源包并按名称加载。v7 的 Loader 已不存在。
  3. 构建场景图。 所有内容都从 app.stage(一个 Container)向下派生。将相关对象分组到 Container 中;子对象的变换相对于父对象。绘制顺序 = 插入顺序(后插入的显示在上层)。
  4. 使用 ticker 制作动画。 app.ticker.add((ticker) => {...})。通过 ticker.deltaTime(帧数,在 60fps 下约为 1)或 ticker.deltaMS(毫秒)缩放运动,从而使速度不受帧率影响。
  5. 为对象启用事件,方法是设置 eventMode = 'static'(或 'dynamic'),然后调用 obj.on('pointerdown', ...)。联合指针事件支持鼠标、触摸和触控笔。
  6. 将较大的静态子树提升为渲染组(isRenderGroup: true),以便 GPU 缓存其变换。前后进行性能分析;确认屏幕上的像素显示正常。

常用模式

1. 异步启动 Application(v8 入口点)
import { Application, Assets, Sprite } from 'pixi.js';

(async () => {
  // v8: construct empty, then await init(). Config does NOT go in the constructor.
  const app = new Application();
  await app.init({
    background: '#1099bb',
    resizeTo: window,        // track the window size
    antialias: true,
    // preference: 'webgpu',  // hint only; default order tries 'webgl' first.
    //                        // Pixi falls back if the backend is unavailable —
    //                        // branch on app.renderer.name, don't assume it took.
  });

  document.body.appendChild(app.canvas); // v8 uses app.canvas, not app.view

  const texture = await Assets.load('https://pixijs.com/assets/bunny.png');
  const bunny = new Sprite(texture);
  bunny.anchor.set(0.5);
  bunny.position.set(app.screen.width / 2, app.screen.height / 2);
  app.stage.addChild(bunny);
})();
2. 使用 Container 构建相对变换场景图
import { Container, Sprite } from 'pixi.js';

const world = new Container();
app.stage.addChild(world);

// Children are positioned relative to `world`; move/scale/rotate the whole group
// by transforming the parent.
for (let i = 0; i < 10; i++) {
  const coin = new Sprite(coinTexture);
  coin.x = i * 40;
  world.addChild(coin);
}
world.position.set(100, 100);
world.scale.set(2);            // every coin scales with the container
3. Ticker 循环(帧率无关)
let elapsed = 0;
app.ticker.add((ticker) => {
  // deltaTime ≈ 1 at 60fps; deltaMS is milliseconds since last frame.
  elapsed += ticker.deltaMS;
  bunny.rotation += 0.05 * ticker.deltaTime;          // smooth at any frame rate
  bunny.y = app.screen.height / 2 + Math.sin(elapsed / 500) * 50;
});
4. 指针事件(联合事件)
bunny.eventMode = 'static';   // 'static' = interactive, doesn't move on its own
bunny.cursor = 'pointer';     // hover cursor; `buttonMode` was removed in v8
bunny.on('pointerdown', (event) => {
  bunny.tint = 0xff0000;
  // event.global is the pointer position in stage space.
});
bunny.on('pointerover', () => bunny.scale.set(1.1));
bunny.on('pointerout',  () => bunny.scale.set(1.0));

拖动需要使用 globalpointermove,而不是 pointermove。在 v8 中,pointermove 仅在指针位于对象上方时触发,因此拖动时如果指针移出对象边缘,更新就会停止。globalpointermove 会在每次移动时触发:

let dragging = false;
bunny.on('pointerdown', () => { dragging = true; });
bunny.on('pointerup', () => { dragging = false; });
bunny.on('pointerupoutside', () => { dragging = false; }); // release off the object
bunny.on('globalpointermove', (event) => {
  if (dragging) bunny.position.copyFrom(event.global);
});
5. 按名称加载多个资源(资源包)
import { Assets } from 'pixi.js';

await Assets.init({
  manifest: {
    bundles: [{
      name: 'level-1',
      assets: [
        { alias: 'hero',  src: 'assets/hero.png' },
        { alias: 'tiles', src: 'assets/tiles.png' },
      ],
    }],
  },
});

const bundle = await Assets.loadBundle('level-1'); // { hero: Texture, tiles: Texture }
const hero = new Sprite(bundle.hero);
6. 为大型静态图层使用渲染组
// A big, rarely-changing background subtree: let the GPU cache its transforms.
const background = new Container({ isRenderGroup: true });
app.stage.addChild(background);
// Add hundreds of static tiles to `background`. Moving `background` itself stays
// cheap; constantly re-adding/removing children negates the benefit.

常见问题

  • 画布为空 / “app.stage is undefined” → 你没有 await app.init(),或者在构造函数中进行了配置。在 v8 中,构造函数为空;所有选项都应传给 init()。在 init() Promise 兑现之前,app.renderer/app.canvas/app.screen 都是 undefined。
  • app.view is undefined → v8 将其重命名为 app.canvas。
  • 迁移 v7 代码 → Loader/loader.add → Assets.load(Loader 类已移除);同步的 new Application({...}) → 空构造函数 + 异步 init()(构造函数中的选项会被忽略,并显示弃用警告,但不会报错);beginFill()/endFill() → 先定义形状,再调用 .rect(...).fill(...)。interactive = true 和 app.view 仍可作为已弃用的别名使用。
  • Ticker 回调参数是 Ticker,而不是 delta 数值 → app.ticker.add((dt) => { obj.rotation += dt; }) 可以通过编译,但 dt 是整个 Ticker 对象,因此计算结果为 NaN,不会产生动画。应从参数中读取 ticker.deltaTime。
  • 顶层 await 导致构建错误(Vite ≤6.0.6) → 用 (async () => { ... })() 包装启动代码。
  • 速度会随帧率变化 → 将移动量乘以 ticker.deltaTime(在 60fps 时约为 1),或按 ticker.deltaMS 缩放;不要假定帧率为 60fps。deltaTime 是无量纲乘数,不是毫秒。
  • 点击没有反应 → 对象的 eventMode 仍为 'passive'(v8 的默认值:对象本身不可交互,但子对象仍可交互);将其设为 'static'(对于在静止光标下移动的对象,则设为 'dynamic')。
  • 拖动在对象边缘处停止 → 在 v8 中,只有指针位于对象上时才会触发 pointermove;拖动或全局跟踪请使用 globalpointermove。
  • Texture.from(url) 返回空白或未定义的纹理 → 在 v8 中,它只会读取 Assets 缓存;请先执行 await Assets.load(url),然后使用返回的 Texture。
  • WebGPU 功能报错 → preference 只是提示。如果后端不可用,Pixi 会回退(先尝试 WebGL,再尝试 Canvas);使用特定后端的代码前,请根据 app.renderer.name 进行分支处理。
  • 像素艺术的纹理看起来模糊 → 设置 texture.source.scaleMode = 'nearest'(或在加载时传入此选项)。
  • 内存持续增长 → removeChild 不会释放 GPU 内存;对于不再需要的资源,请调用 sprite.destroy() 和 Assets.unload(url)。
  • 在同一标签页中销毁并重新创建应用后出现闪烁或损坏 → 使用 app.destroy({ releaseGlobalResources: true }) 销毁;否则,旧应用中池化的批次和纹理会泄漏到新应用中。

参考资料

  • 关于纹理/资源管线(精灵表/图集、Assets.add、后台加载、卸载),以及 Graphics/Text/TilingSprite/ParticleContainer 和滤镜,请阅读 references/assets-and-display.md。

相关技能

  • phaser-core — 功能齐全的 2D 框架(场景、物理、输入)。
  • threejs-scene-setup — 使用 three.js 在浏览器中构建 3D 场景。
  • prototype-fast — 快速构建可玩的灰盒原型(经常引用 PixiJS)。

评论

支持 Markdown