pixijs-scene-container について
PixiJS 開発者向けに、Containerを使ったv8シーングラフの構成を解説するスキルです。子要素の管理、変換、ソート、境界、イベント、ライフサイクル操作を扱います。
主な機能
- 01シーンオブジェクトのグループ化とサブツリー変換
- 02子要素の順序、追加、再親化の管理
- 03変換、境界、座標変換の解説
- 04ソートとレンダーグループの説明
- 05イベント、描画コールバック、破棄の解説
活用シーン
- 01スプライトなどのリーフオブジェクトのグループ化
- 02シーン階層と描画順の整理
- 03ローカル座標とグローバル座標間の変換
- 04シーンのサブツリーの削除または破棄
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pixijs/ pixijs-skills --skill pixijs-scene-containerインストール後、Claude Code で/pixijs-scene-containerを入力して読み込まれたか確認します。
詳細
Containerは、PixiJS v8のシーングラフにおける汎用ノードです。子を保持し、サブツリー全体に変換、アルファ、色調、ブレンドモードを適用します。作成するすべての表示オブジェクトは、分岐を構築するContainerか、その中にネストするリーフ(Sprite、Graphics、Text、Mesh)のいずれかです。
pixijs-scene-core-conceptsの内容を理解していることを前提とします。
クイックスタート
const group = new Container({
label: "hero-group",
x: 200,
y: 150,
sortableChildren: true,
});
const body = new Sprite(await Assets.load("body.png"));
const head = new Sprite(await Assets.load("head.png"));
head.position.set(0, -40);
head.zIndex = 1;
group.addChild(body, head);
group.pivot.set(group.width / 2, group.height / 2);
group.scale.set(1.5);
app.stage.addChild(group);
関連スキル: pixijs-scene-core-concepts(シーングラフの基本概念、マスキング、レイヤー、レンダーグループ)、pixijs-scene-sprite / pixijs-scene-graphics / pixijs-scene-text / pixijs-scene-mesh(コンテナー内に入れるリーフオブジェクト)、pixijs-events(eventMode、ヒットテスト)、pixijs-math(Matrix、toGlobal/toLocalの詳細)、pixijs-performance(cacheAsTexture、カリング、レンダーグループ)。
基本パターン
コンストラクターオプション
const container = new Container({
label: "world",
x: 100,
y: 50,
scale: 2,
rotation: Math.PI / 4,
alpha: 0.8,
visible: true,
tint: 0xffaa00,
blendMode: "add",
sortableChildren: true,
isRenderGroup: true,
origin: { x: 0, y: 0 },
boundsArea: new Rectangle(0, 0, 1920, 1080),
});
Containerのすべてのオプション(position、scale、tint、label、filters、zIndexなど)はここでも有効です。詳しくはskills/pixijs-scene-core-concepts/references/constructor-options.mdを参照してください。
ContainerコンストラクターはassignWithIgnoreを使用して、オプションオブジェクト内のすべてのフィールドをインスタンスに一括コピーします。ただし、children、parent、effectsは除きます。Containerの公開プロパティはどれもコンストラクターオプションとして指定できます。cullable、cullArea、mask、filterArea、eventMode、hitAreaなどが該当します。上記のオプション一覧では、よく使われるものをまとめています。全項目については、上記の共通リファレンスを参照してください。
isRenderGroup: trueを指定すると、コンテナーが独自のレンダーグループに昇格し、変換が子ごとにCPU上で再計算されるのではなく、GPU上で適用されます。大規模な静的ワールドやUIレイヤーなど、安定したサブツリーで使用してください。多用は避けてください。ほとんどのシーンではレンダーグループを明示的に設定する必要はなく、多すぎるとパフォーマンスが低下します。昇格する前にプロファイリングしてください。詳しくはpixijs-scene-core-concepts/references/scene-management.mdを参照してください。
sortableChildren: trueを指定すると、次のレンダリング時に子がzIndex順に並べ替えられます。詳しくは以下のzIndexを参照してください。
originは、v8で正式に導入された変換ヘルパーです。コンテナーを移動させずに回転や拡大縮小の中心として機能するObservablePointです。pivotは親空間におけるローカル原点の投影位置をずらすため、変更するとオブジェクトが移動します。一方、originは位置をそのまま保ち、指定したローカル座標を中心に回転や拡大縮小を行います。PointData、単一の数値(両軸に適用)、またはcontainer.origin.set(x, y)による実行時の設定を受け付けます。同じコンテナーにpivotとoriginの両方を設定すると効果が重なり、推奨されません。どちらか一方を選んでください。
boundsAreaを設定すると、getBounds()は子を再帰的に計測する代わりに、固定の矩形を返します。安価で予測可能な子を何百個も持つコンテナーでは、パフォーマンスの向上につながります。
cullableとcullAreaは有効なコンストラクターオプションです(assignWithIgnoreの処理でほかのフィールドと同様にコピーされます)。ただし、カリングの設定はシーングラフではなくパフォーマンスに関する内容のため、pixijs-performanceで説明しています。
リーフとコンテナー
const parent = new Container();
const sprite = new Sprite(texture);
parent.addChild(sprite);
子を持つのは、Containerと、子を保持する目的のサブクラス(RenderLayerなど)だけにしてください。PixiJS v8では、慣例としてSprite、Graphics、Text、Mesh、ParticleContainerのパーティクル、DOMContainerのコンテンツはリーフです。オブジェクトをグループ化する必要がある場合は、常にContainerで囲んでください。レイアウトロジックはコンテナーに持たせ、リーフは純粋な視覚データとして保ちます。リーフに子を追加すると非推奨の警告がログに記録され、将来的にはエラーになる予定です。
子の追加と削除
const parent = new Container();
parent.addChild(a, b, c);
parent.addChildAt(d, 0);
parent.swapChildren(a, b);
parent.setChildIndex(c, 0);
parent.removeChild(b);
parent.removeChildAt(0);
parent.removeChildren();
parent.removeChildren(0, 2);
addChild は任意の数の子を受け取り、最初の子を返します。子は配列の順序で描画されます。インデックス 0 が最初(背面)に描画され、最後のインデックスが最後(前面)に描画されます。addChildAt は指定したインデックスに挿入し、setChildIndex は既存の子を移動し、swapChildren は 2 つの子の位置を入れ替えます。
removeChildren(beginIndex?, endIndex?) は範囲を削除し、削除した配列を返します。
すでに同じコンテナーに属している子を指定して addChildAt を呼び出すと、その子は新しいインデックスに移動します。親子関係は変わらないため、added / childAdded / removed / childRemoved イベントは発生しません。イベントが発生するのは、子が別の親(または親なし)から移動してくる場合のみです。
ワールド変換を維持したまま再親化し(子が視覚的に飛び跳ねないようにし)たい場合は、reparentChild / reparentChildAt を使用します。子を同じ位置で入れ替え、古い子のローカル変換を引き継ぐには、replaceChild を使用します。
変換プロパティ
const obj = new Container();
obj.x = 100;
obj.y = 200;
obj.position.set(100, 200);
obj.scale.set(2);
obj.scale = 2;
obj.rotation = Math.PI / 4;
obj.angle = 45;
obj.pivot.set(50, 50);
obj.skew.set(0.1, 0.2);
obj.alpha = 0.5;
obj.tint = 0xff0000;
obj.visible = false;
obj.renderable = false;
position、scale、pivot、skewはObservablePointです。scale = 2のように設定すると、両方の軸に値が設定されます。rotationはラジアン、angleは度単位です。これらは同じ値を指すエイリアスで、常に同期します。pivotは、親空間のpositionに対応するローカル空間の点を設定します。これを変更すると、コンテナーの位置と回転の両方が変わります。alphaとtintは子孫にも乗算されます。blendModeはこのコンテナーの描画命令に適用されます。visible = falseにすると、レンダリングと変換の更新がスキップされます。renderable = falseにすると、レンダリングはスキップされますが、変換は更新されます(描画せずにgetBounds()やヒットテストを行う場合に使用します)。
zIndex と sortableChildren
const world = new Container({ sortableChildren: true });
const ground = new Sprite(groundTexture);
ground.zIndex = 0;
const player = new Sprite(playerTexture);
player.zIndex = 10;
const ui = new Sprite(uiTexture);
ui.zIndex = 100;
world.addChild(ground, player, ui);
sortableChildren が true の場合、次のレンダリング前にコンテナーの子が zIndex の順に並べ替えられます。子の zIndex を変更すると、親は自動的に並べ替えが必要な状態に設定されます。必要なものだけを並べ替えてください。sortableChildren を無効のままにしておくほうが効率的です。完全に手動で制御したい場合は、zIndex の値を変更したあとに container.sortChildren() を呼び出します。
階層構造から切り離して描画順を制御する場合(子は変換用の論理的な親を維持しつつ、別の z で描画する場合)は、RenderLayer を使用します。pixijs-scene-core-concepts/references/scene-management.md を参照してください。
境界と座標変換
const bounds = container.getBounds();
console.log(bounds.x, bounds.y, bounds.width, bounds.height);
const rect = container.getBounds().rectangle;
const local = new Point(10, 20);
const world = container.toGlobal(local);
const backToLocal = container.toLocal(world);
const selfPos = container.getGlobalPosition();
getBounds() は Bounds オブジェクト(Rectangle ではありません)を返します。このオブジェクトは x、y、width、height と、実際の Rectangle を必要とする API 向けの .rectangle ゲッターを公開します。シグネチャーは getBounds(skipUpdate?: boolean, bounds?: Bounds) です。強制的な変換の更新をスキップするには、最初の引数に true を渡します。新しいオブジェクトの割り当てを避けるには、2 番目の引数に任意の Bounds インスタンスを渡します。
toGlobal(point) は、このコンテナーのローカル空間内の点をシーンルート空間に変換します。toLocal(point, from?) は、別のコンテナーのローカル空間から(from を省略した場合はグローバル空間から)変換します。getGlobalPosition() は parent.toGlobal(this._position) の省略形です。
サイズ設定
const sprite = new Sprite(texture);
sprite.setSize(200, 100);
const { width, height } = sprite.getSize();
setSize は、1 回の操作でコンテナーの境界が指定されたピクセルサイズに収まるように scale を調整します。.width と .height を個別に設定することもできますが、代入のたびに境界が再計算されます。両方の軸を変更する場合は、setSize を使用してください。
コンテナーイベント
const parent = new Container();
parent.on("childAdded", (child, container, index) => {
console.log("added at", index, child.label);
});
parent.on("childRemoved", (child, container, index) => {
console.log("removed from", index);
});
const child = new Container();
child.on("added", (newParent) => console.log("entered", newParent.label));
child.on("removed", (oldParent) => console.log("left", oldParent.label));
child.on("visibleChanged", (visible) => console.log("visible:", visible));
child.on("destroyed", (destroyed) => console.log("gone", destroyed.label));
parent.addChild(child);
| イベント | 発生する対象 | 引数 |
|---|---|---|
childAdded | 子を受け取る親 | (child, container, index) |
childRemoved | 子を失う親 | (child, container, index) |
added | 追加された子 | (parent) |
removed | 切り離された子 | (parent) |
destroyed | 破棄されたコンテナー | (container) |
visibleChanged | visible の値が変わったコンテナー | (visible) |
これらは Container の EventEmitter 側で発行されます。pixijs-events のポインターイベントと混同しないでください。
destroyed は内部クリーンアップの後、リスナーが削除される前に発生します。そのため、ハンドラーの実行時には、position、scale、pivot、origin、skew、parent はすでに null に設定され、children は空になっています(長さは 0 ですが、配列への参照自体は null になりません)。必要なデータは、ハンドラー内ではなく、destroy() を呼び出す前にコンテナーから取得してください。
onRender によるフレームごとの更新
const container = new Container();
container.onRender = (renderer) => {
container.rotation += 0.01;
};
container.onRender = null;
onRender はコンテナーのレンダリング中、毎フレーム実行され、アクティブな Renderer を受け取ります。特定のコンテナーに関連付けられた軽量なアニメーションやフレームごとの更新に使用してください。v7 では updateTransform をオーバーライドするこのパターンが使われていましたが、v8 では毎フレーム実行されなくなりました。コールバックを解除するには、onRender = null を設定してください。
親からの検索と削除
const player = world.getChildByLabel("player");
const enemies = world.getChildrenByLabel(/enemy-\d+/, true);
const bounds = hud.getLocalBounds();
oldSprite.removeFromParent();
getChildByLabel(label, deep?)— 文字列またはRegExpに最初に一致するものを取得します。再帰検索するにはtrueを渡します。getChildrenByLabel(label, deep?, out?)— 一致するものをすべて取得します。再利用可能な出力配列を任意で指定できます。getLocalBounds()— 親の変換を無視した、このコンテナー独自の座標空間での境界です。自己完結型のレイアウト計算ではgetBounds()より低コストです。removeFromParent()— 現在の親からthisを切り離します(すでに親がない場合は何もしません)。
破棄
container.destroy();
container.destroy({
children: true,
texture: true,
textureSource: true,
});
console.log(container.destroyed);
デフォルトでは、destroy() はこのコンテナーを親から切り離し、自身の状態を破棄します。{ children: true } を渡すと、すべての子孫を再帰的に破棄します。サブツリー全体を破棄する場合は、通常この呼び出しを使います。texture: true と textureSource: true を指定すると、さらに葉の子が参照する GPU リソースも破棄されます(その子専用に読み込んだテクスチャを持つスプライトで便利です)。cacheAsTexture が有効な場合は、破棄する前に container.cacheAsTexture(false) で無効にしてください。
よくある間違い
[CRITICAL] 葉となるシーンオブジェクトへの子の追加
誤り:
const sprite = new Sprite(texture);
const overlay = new Sprite(overlayTexture);
sprite.addChild(overlay);
正しい例:
const group = new Container();
const sprite = new Sprite(texture);
const overlay = new Sprite(overlayTexture);
group.addChild(sprite, overlay);
Sprite、Graphics、Text、Mesh は葉です。これらに子を追加すると、現在は非推奨の警告がログに出力され、将来のバージョンではエラーになります。グループ化が必要な場合は、必ず Container でラップしてください。
[HIGH] getBounds() が Rectangle を返すと想定する
誤り:
const rect = container.getBounds();
rect.contains(x, y); // TypeError: contains is not a function
正しい例:
const rect = container.getBounds().rectangle;
rect.contains(x, y);
const bounds = container.getBounds();
console.log(bounds.width, bounds.height);
v8 では、getBounds() は Bounds インスタンスを返します。基本的なゲッター(x、y、width、height)は使用できますが、.contains() などの Rectangle 固有のメソッドを使う場合や、Rectangle を必要とする API に渡す場合は、.rectangle プロパティを参照してください。
[HIGH] cacheAsTexture の代わりに cacheAsBitmap を使用する
誤り:
container.cacheAsBitmap = true;
正しい例:
container.cacheAsTexture(true);
cacheAsBitmap(v7 のプロパティ)は、v8 で cacheAsTexture()(メソッド)に名前が変更されました。destroy() を呼び出す前に、必ず cacheAsTexture(false) で無効にしてください。
[MEDIUM] container.label の代わりに container.name を使用する
v8 では、name は label に名前が変更されました。古いプロパティも非推奨のエイリアスとして引き続き機能します。子を名前で検索するには、v8 では getChildByLabel を使います。
[MEDIUM] 同じコンテナーに pivot と origin の両方を設定する
pivot は親空間におけるローカル原点の投影位置をずらします(回転の中心を変更することで、オブジェクトの位置も副作用として変化します)。origin は移動させずに、回転/スケールの中心を変更します。同じコンテナーに両方を設定すると、予期しない相乗効果が生じるため、どちらか一方を選んでください。