关于 pixijs-scene-core-concepts
pixijs-scene-core-concepts 为 PixiJS v8 开发者梳理场景图的共同概念,并指向各对象的深入资料。它说明容器与叶节点的区别,以及变换、渲染顺序、剔除和遮罩等机制。
主要功能
- 01场景图与节点角色
- 02变换与坐标空间
- 03渲染顺序与图层
- 04渲染组与剔除
- 05遮罩、可见性与销毁
适用场景
- 01规划显示对象的层级结构
- 02选择分组、渲染层或渲染组
- 03配置离屏剔除与遮罩
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pixijs/ pixijs-skills --skill pixijs-scene-core-concepts安装后在 Claude Code 中输入/pixijs-scene-core-concepts确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/constructor-options.mdcontainer-hierarchy.mdlayers.mdmasking.mdrender-groups.mdscene-management.mdtransforms.md详细介绍
此技能是所有 pixijs-scene-* 子技能共同引用的思维模型。它介绍 PixiJS v8 中的场景图、Container 与叶节点的区别,以及各个概念所在的位置。它不会深入讲解任何单个 API,而是概述各个组成部分,并指向相应的技能或参考文件。
快速开始
const world = new Container({ isRenderGroup: true });
app.stage.addChild(world);
const hero = new Container({ label: "hero" });
hero.addChild(new Sprite(bodyTexture));
hero.addChild(new Sprite(faceTexture));
world.addChild(hero);
const mask = new Graphics().rect(0, 0, 800, 600).fill(0xffffff);
world.mask = mask;
world.addChild(mask);
hero.position.set(world.width / 2, world.height / 2);
相关技能: pixijs-scene-container(详细介绍 Container API)、叶节点技能(pixijs-scene-sprite、pixijs-scene-graphics、pixijs-scene-text、pixijs-scene-mesh、pixijs-scene-particle-container、pixijs-scene-dom-container、pixijs-scene-gif)、pixijs-events(命中测试会遍历场景图)、pixijs-performance(缓存、剔除、渲染组)、pixijs-math(Matrix、toGlobal/toLocal 详解)。
核心概念
什么是场景图
PixiJS 场景图是一棵以 app.stage 为根节点的显示对象树。每个节点都有一个父节点、一个相对于父节点的变换(位置、缩放、旋转、轴心、倾斜),以及可选的视觉状态(alpha、色调、混合模式、可见性)。每一帧,渲染器都会遍历这棵树,将变换和视觉状态逐级合成到世界空间,剔除屏幕外的内容,并发出绘制调用。场景图既是布局模型,也是渲染顺序:靠前的同级节点会绘制在靠后的同级节点后面。
在 v8 中,每个显示对象都是 Container 的子类。早期版本中的 DisplayObject 已被移除。
Container 与叶节点(重要)
树中的节点分为两种:
- 容器: 可包含子节点的节点。对于任何用于分组、定位或变换其他节点的节点,都应使用
Container(或RenderLayer)。 - 叶节点: 绘制内容且不包含子节点的节点。应将
Sprite、Graphics、Text、Mesh、ParticleContainer的Particle、DOMContainer或GifSprite用作叶节点。
在 PixiJS v8 中,叶节点不得包含子节点。向 Sprite / Graphics / Text / Mesh 添加子节点会记录弃用警告,且计划在后续版本中改为直接报错。规则是:任何需要包含子节点的节点都应使用 Container;不要在叶场景对象中嵌套子节点。 如果需要将一个叶节点与其他叶节点组合,请将它们放入一个 Container 中。
这一区别正是 pixijs-scene-* 技能采用这种划分方式的原因:pixijs-scene-container 介绍分组节点,每种叶节点则有各自专注于绘制行为的技能。
变换与坐标空间
每个容器都会根据其 position、scale、rotation、pivot 和 skew 合成 localTransform(一个 Matrix)。渲染器会将父节点的局部变换相乘,生成 worldTransform(如果变换链中有渲染组,还会生成 groupTransform),将局部坐标映射到场景根节点空间。使用 toGlobal(point) 和 toLocal(point, from?) 在不同空间之间转换,并使用 getGlobalPosition() 获取此对象的世界位置。Matrix 的完整介绍见 pixijs-math;变换设置器以及 toLocal/toGlobal 见 pixijs-scene-container。
渲染顺序与显式 z 顺序
子节点按数组顺序渲染:索引为 0 的最先绘制,索引最大的最后绘制。要在单个容器中显式设置 z 顺序,请设置 sortableChildren = true,并为子节点赋予 zIndex 值。要让渲染顺序脱离逻辑层级(例如,角色的父节点属于游戏世界,但角色实际绘制在 UI 层),请使用 RenderLayer。包括何时优先选择可排序子节点而非 RenderLayer 在内的详细内容,见 references/scene-management.md。
渲染组
将容器标记为 isRenderGroup: true(或调用 container.enableRenderGroup()),即可让 PixiJS 在 GPU 上将其变换作为单个矩阵应用,而不是每帧都在 CPU 上重新计算其所有后代的世界变换。大型且稳定的子树(例如游戏世界、UI 层或视差条带)适合使用渲染组。详细内容见 references/scene-management.md。
剔除
cullable = true + cullArea: Rectangle 会告知 CullerPlugin(或任何剔除流程)跳过渲染可见区域之外的对象。对于子对象始终位于屏幕上的子树,cullableChildren = false 会停止递归剔除。剔除属于性能相关主题;pixijs-performance 和 references/scene-management.md 介绍了其中的权衡。
遮罩
将 container.mask 设置为另一个显示对象,即可裁剪其渲染内容。PixiJS 会自动选择遮罩类型:Graphics 或 Container 遮罩使用模板缓冲区,Sprite 遮罩使用 Alpha 滤镜,数字则会选择 ColorMask。references/masking.md 涵盖了全部四种遮罩类型(AlphaMask、StencilMask、ScissorMask、ColorMask)。
可见性、透明度、色调和混合模式
visible = false 会跳过渲染和变换更新;renderable = false 会跳过渲染,但仍会更新变换(当命中测试或边界查询需要保持实时更新时使用)。alpha 和 tint 会沿子树逐级相乘;blendMode 控制此容器的绘制指令如何与目标上已有内容合成。完整的混合模式列表请参阅 pixijs-blend-modes,节点级状态请参阅 pixijs-scene-container。
销毁语义
container.destroy() 会解除一个节点的链接。container.destroy({ children: true }) 会递归销毁整个子树;销毁某个分支时务必使用此方法。texture: true 和 textureSource: true 还会销毁叶节点拥有的 GPU 资源。如果启用了 cacheAsTexture,请在销毁前将其禁用。完整签名请参阅 pixijs-scene-container。
生命周期事件
容器会在层级和可见性发生变化时触发事件:父对象上的 childAdded / childRemoved、子对象上的 added / removed,以及容器自身的 visibleChanged 和 destroyed。这些事件可用于连接响应式 UI 更新或资源记账。完整详情请参阅 references/container-hierarchy.md。
叶节点对比:各技能涵盖哪些对象
| 叶节点 | 主要用途 | 技能 |
|---|---|---|
Sprite | 在指定位置绘制单个纹理(包含用于可调整大小的 UI 面板的变体 NineSliceSprite,以及用于重复背景的变体 TilingSprite)。 | pixijs-scene-sprite |
Text / BitmapText / HTMLText / SplitText / SplitBitmapText | 渲染文本。基于 Canvas 的 Text 适用于一般用途,BitmapText 适用于大量且开销低的文本,HTMLText 适用于富 HTML/CSS 布局,分割变体适用于逐字符动画。 | pixijs-scene-text |
Graphics | 矢量绘图:形状、线条、路径、填充和描边。由 GraphicsContext 提供支持。 | pixijs-scene-graphics |
Mesh / MeshSimple / MeshPlane / MeshRope / PerspectiveMesh | 使用着色器或纹理创建自定义几何体。使用 MeshRope 创建沿路径延伸的纹理带,使用 PerspectiveMesh 实现 2D 透视效果。 | pixijs-scene-mesh |
ParticleContainer + Particle | 使用受限的变换集绘制数千个轻量级精灵,适用于高吞吐量粒子效果。 | pixijs-scene-particle-container |
DOMContainer | 在场景图中指定位置渲染 HTML 元素(适用于输入框、iframe、无障碍覆盖层)。 | pixijs-scene-dom-container |
GifSprite | 将动画 GIF 作为显示对象播放。需要 pixi.js/gif。 | pixijs-scene-gif |
Container 本身由 pixijs-scene-container 介绍,它是所有叶节点所在的节点。
如何选择(快速判断)
- “我想对一些显示对象进行分组和变换” →
Container,参见pixijs-scene-container。 - “我想绘制纹理” →
Sprite,参见pixijs-scene-sprite。 - “我想绘制矢量形状或路径” →
Graphics,参见pixijs-scene-graphics。 - “我想绘制文本” →
Text/BitmapText/HTMLText,参见pixijs-scene-text。 - “我想绘制数千个开销低的精灵” →
ParticleContainer,参见pixijs-scene-particle-container。 - “我想创建自定义几何网格或变形精灵” →
Mesh或其某个变体,参见pixijs-scene-mesh。 - “我想裁剪子树” → 设置
.mask,参见references/masking.md。 - “我想让渲染顺序解耦” →
RenderLayer,参见references/scene-management.md。 - “我想为大型稳定子树使用 GPU 级变换” →
isRenderGroup: true,参见references/scene-management.md。 - “我想跳过屏幕外的渲染” →
cullable = true+CullerPlugin,参见pixijs-performance。
参考资料
- references/constructor-options.md:每个派生自
Container的节点都会继承的约 30 个字段(变换、显示、层级、排序、布局、效果、回调),包括默认值、类型,以及何时适合逐行赋值。所有叶子技能共用的参考资料。 - references/container-hierarchy.md:添加/移除/交换子级、保留变换的重新挂接、标签导航、销毁子树。
- references/transforms.md:位置、缩放、旋转、枢轴、原点、倾斜、toGlobal/toLocal、三个矩阵(局部/组/世界)、边界。
- references/masking.md:AlphaMask、StencilMask、ScissorMask、ColorMask、反向遮罩、开销比较。
- references/layers.md:
RenderLayer、挂接/解除挂接、排序图层、图层与逻辑父级的拆分。 - references/render-groups.md:
isRenderGroup、GPU 级变换、适用场景、渲染组与cacheAsTexture的比较。 - references/scene-management.md:综合概览;渲染组、
RenderLayer、剔除、zIndex 排序、boundsArea。
常见错误
[CRITICAL] 向叶子显示对象添加子级
错误:
const sprite = new Sprite(texture);
sprite.addChild(new Graphics().rect(0, 0, 10, 10).fill(0xff0000));
正确:
const group = new Container();
group.addChild(new Sprite(texture));
group.addChild(new Graphics().rect(0, 0, 10, 10).fill(0xff0000));
在 v8 中,叶子节点(Sprite、Graphics、Text、Mesh、ParticleContainer、DOMContainer、GifSprite)虽然在技术上继承自 Container,但不应包含子级。向叶子节点添加子级会导致未定义的渲染行为。需要分组时,请将叶子节点包装在 Container 中。
[CRITICAL] 引用 DisplayObject
错误:
import { DisplayObject } from "pixi.js"; // no such export in v8
function moveNode(node: DisplayObject) {
node.x += 1;
}
正确:
import { Container } from "pixi.js";
function moveNode(node: Container) {
node.x += 1;
}
DisplayObject 已在 v8 中移除。现在所有显示对象(包括 Sprite、Graphics、Text、Mesh)都是 Container 的子类。请将 Container 用作基类型。
[HIGH] 忘记在大型静态子树上设置 isRenderGroup
错误:
const world = new Container();
for (let i = 0; i < 5000; i++) {
world.addChild(new Sprite(texture));
}
app.stage.addChild(world);
正确:
const world = new Container({ isRenderGroup: true });
for (let i = 0; i < 5000; i++) {
world.addChild(new Sprite(texture));
}
app.stage.addChild(world);
如果没有设置 isRenderGroup: true,渲染器每帧都会重新组合每个子级相对于其父级的变换。将子树标记为渲染组后,变换和绘制状态会被缓存,直到子级发生变化;这对大型或大部分静态的树结构至关重要。
[HIGH] 将 child.x 视为世界坐标
错误:
const enemy = new Container();
enemy.x = 500;
world.addChild(enemy);
world.x = 200;
console.log(enemy.x); // 500 (local), not 700 (world)
正确:
const worldPos = enemy.toGlobal({ x: 0, y: 0 });
console.log(worldPos.x); // 700
Container.x/y/scale/rotation 是相对于父级的局部属性。使用 toGlobal(point) 计算世界坐标,或使用 getGlobalPosition() 获取容器原点的世界坐标。世界变换不会以简单的 x/y 对形式公开。
[MEDIUM] 设置 sortableChildren,却未设置 zIndex
错误:
const layer = new Container();
layer.sortableChildren = true;
layer.addChild(bg); // no zIndex
layer.addChild(mid); // no zIndex
layer.addChild(fg); // no zIndex
// order is unchanged — all zIndex default to 0
正确:
const layer = new Container();
layer.sortableChildren = true;
bg.zIndex = 0;
mid.zIndex = 10;
fg.zIndex = 20;
layer.addChild(bg, mid, fg);
sortableChildren 会在渲染前按 zIndex 重新排序子级,但只有在子级具有不同的 zIndex 值时才会生效。只设置父级标志不会产生可见效果。
工具
PixiJS Devtools Chrome 扩展程序可让你实时检查和操控正在运行的场景图。调试任何较复杂的布局或渲染顺序问题时,都应安装此扩展程序。