🅿️

PixiJS v8のマイグレーションガイドを読んでみる

2024/04/12に公開

この記事は

PixiJSの8系がリリースされた。WebGPU対応でなんだかメチャ速らしい。
かんたんなものだけどPixiJSでゲームを作ってるので移行できるなら移行したい。
公式がマイグレーションガイド v8 Migration Guide | PixiJS を出してくれているので今日はこれを読む。
あくまで個人的な備忘録であり翻訳ではない。正確性も保証しない。

※2024/04/12 時点のガイド これは公式リポジトリのWikiに移動して更新される可能性がある。

1.イントロ

WebGPUに対応したりしてメチャ速くなりました。

2.破壊的な変更

破壊的な変更やまだ対応完了していないPixiプラグインもあるので過去バージョンから移行する場合には検討が必要です。

移行が難しいかもしれない人

  • 数万個単位のパーティクルを描画するためにParticleContainerを使っている人

  • 独自バッチレンダラーを使っている人(v8には独自バッチを組み込む機能はまだない でもロードマップにはある)

  • v8で未対応のPixiライブラリを使っている人
    • 統合済み
      • Filters
      • Sound
      • Gif
      • Storybook
      • UI
      • Open Games??(しらない)
    • 近々対応予定
      • React
      • Spine
    • そのうち…
      • Pixi layers(プラグインとしてではなく基本機能としてv8に組み込み予定)

新パッケージストラクチャ

パッケージを小分けにしすぎたので単一パッケージに戻しますとのこと。

Old:

import { Application } from '@pixi/app';
import { Sprite } from '@pixi/sprite';

New:

import { Application, Sprite } from 'pixi.js';

カスタムビルド

今までextensions扱いだったものがデフォルトでインポートされるようになったけど、それらをインポートしたくない場合は手動でimport書いてねってことみたい?

非同期イニシャライズ

WebGPUの導入によりApplicationを生成後に非同期のinit()待ちが必要になったらしいです。
ApplicationOptionsオブジェクトはコンストラクタでなくinit()に渡せとのこと。

Old:

import { Application } from 'pixi.js';

const app = new Application();

// do pixi things

New:

import { Application } from 'pixi.js'

const app = new Application();

(async () => {
    await app.init({
        // application options
    });

    // do pixi things
})()

コメント
クラスのコンストラクタ内でApplicationを初期化してる場合とか非同期builder作らなきゃいけなくなってめんどくさそう

Texture の調整

ちょっと何言ってるかよくわからないセクション。

Textureがリソースを管理する必要はなく、これらは事前にアセットマネージャーが行う仕事。
BaseTextureは廃止された。
代わりにTextureSourceが追加された。これはテクスチャの設定とアップロード,使用する方法をまとめたもの。(??)
テクスチャソースにはいろいろある

  • TextureSource
  • ImageSource
  • CanvasSource
  • VideoSource
  • BufferSource
  • CompressedSource

基本的にはAssetsTextureを返すとのこと。

コメント
Texture側から自身がロード済みかどうか気にする必要がなくなったってこと…?v7の時点でそんな感じじゃなかったっけ…
BaseTexureがなくなって色んなタイプのTextureSourceが追加されたよ!っていうのがメインなのかな… Sprite生成くらいにしかTextureをつかってないのでよくわからん…

Graphics APIの改修

Graphicsを生成するときの記述の順序が変わった。
beginFill()→ 形状描画 → endFill()
から
形状描画 → 塗りつぶし,ストロークになった。

Old:

// red rect
const graphics = new Graphics()
  .beginFill(0xFF0000)
  .drawRect(50, 50, 100, 100)
  .endFill();

// blur rect with stroke
const graphics2 = new Graphics()
  .lineStyle(2, 'white')
  .beginFill('blue')
  .circle(530, 50, 140, 100)
  .endFill();

New:

// red rect
const graphics = new Graphics()
  .rect(50, 50, 100, 100)
  .fill(0xFF0000);


// blur rect with stroke
const graphics2 = new Graphics()
  .rect(50, 50, 100, 100)
  .fill('blue')
  .stroke({width:2, color:'white'});

そのほかGraphics関連変更

詳しくは原文で

  • 形状描画関連のメソッド名も変わった。引数は変わらない。(drawCirecle()circle() など)

  • 塗りつぶし系のメソッドは引数が(色, アルファ値)的な形から({color:色, alpha:アルファ値})的な形でそれぞれの型のオブジェクトを渡すようになった。

  • holecutになって使いやすくなった
  • GraphicsGeometryGrahpicsContextに置き換え

コメント
全部書き直すのめんどくせ~~~~~~~

シェーダーの変更

シェーダー周りは知識不足で嘘を書く可能性があるので原文ままで引用します。

As we now need to accommodate both WebGL and WebGPU shaders, the wey they are constructed has been tweaked to take this into account. The main differences you will notice (this is for shaders in general) is that Textures are no longer considered uniforms (as in they cannot be included in a uniform group). Instead we have the concept of resources. A resource can be a few things including:

TextureSource - A source texture myTexture.source
TextureStyle - A texture style myTexture.style
UniformGroup - A collection of number based uniforms myUniforms = new UniformGroup({})
BufferResource - A buffer that is treated as a uniform group (advanced)

creating a webgl only shader now looks like this:

old

const shader = PIXI.Shader.from(vertex, fragment, uniforms);

new

just WebGL

const shader = Shader.from({
    gl: { vertex, fragment },
    resources, // resource used from above including uniform groups
});

WebGL and WebGPU

const shader = Shader.from({
    gl: { vertex, fragment },
    gpu: {
        vertex: {
            entryPoint: 'mainVert',
            source,
        },
        fragment: {
            entryPoint: 'mainFrag',
            source,
        },
    },
    resources, // resource used from above including uniform groups
});

Uniforms are also constructed in a slightly different way. When creating them, you now provide the type of variable you want it to be.

old

const uniformGroup = new UniformGroup({
  uTime:1,
});

uniformGroup.uniforms.uTime = 100;

new

const uniformGroup = new UniformGroup({
  uTime:{value:1, type:'f32',
});

uniformGroup.uniforms.uTime = 100; // still accessed the same!

The best way to play and fully and get to know this new setup please check out the Mesh and Shader examples:
old: v7 example new: v8 example

フィルター

フィルターの動きはほぼ変わらないけどカスタムフィルターを作る場合は前セクションのシェーダーの変更を考慮してねとのこと。

old

  const filter = new Filter(vertex, fragment, {
      uTime: 0.0,
  });

new

    const filter = new Filter({
        glProgram: GlProgram.from({
            fragment,
            vertex,
        }),
        resources: {
            timeUniforms: {
                uTime: { value: 0.0, type: 'f32' },
            },
        },
    });

そのほかの破壊的な変更

  • DisplayObject廃止ContaierがPixiオブジェクトの基底クラスになります。 ※マジか…

  • updateTransform()を廃止。フレームごとに何か処理したい場合にはonRender()を用意したのでこっちを使ってね。

  • ミップマップ生成の変更(?? 原文見てください "Mipmap generation changes" の項)

  • テクスチャのUVが変更された際にスプライトが通知を受け取れなくなった。基本的にテクスチャのUVは変更しない方がいい。
  • UVを変更してアニメーションするような場合スプライトの更新は自身でやらないとダメ。ソースデータ(ビデオテクスチャなど)の変更は即座に反映される。
const texture = await Assets.load('bunny.png');
const sprite = new Sprite(texture);

texture.frame.width = texture.frame.width/2;
texture.update();

// guarantees the texture changes will be reflected on the sprite
sprite.onViewUpdate();


// alternatively you can hooke into the sprites event
texture.on('update', ()=>{sprite.onViewUpdate});


  • Containerのカリング動作を変更。カリングはレンダリングループ内で自動で行われていたが、これは廃止し、ユーザー自身で制御させるようにした。
    関連していくつかのプロパティを追加。
    • cullable - コンテナがカリング可能か否か
    • cullArea - カリング領域をコンテナ全体としたくない場合にこれでカリング領域を指定する
    • cullableChildren - 子要素をカリング可能とするか否か
const container = new Container();
const view = new Rectangle(0, 0, 800, 600);

container.cullable = true;
container.cullArea = new Rectangle(0,0,400,400);
container.cullableChildren = false;

Culler.shared.cull(myContainer, view);
renderer.render(myContainer);

以前と同様の動作をさせたい場合は、CullerPluginが使える。これは毎フレームCuller.shared.cullを実行する。

import {extensions, CullerPlugin} from 'pixi.js'
extensions.add(CullerPlugin)


  • いくつかのメッシュクラスをリネーム
    • SimpleMesh -> MeshSimple
    • SimplePlane -> MeshPlane
    • SimpleRope -> MeshRope

  • Assetsのdeprecatedな使い方を廃止 ※地味にめんどい…

Old:

import { Assets } from 'pixi.js';

Assets.add('bunny', 'bunny.png');

New:

import { Assets } from 'pixi.js';

Assets.add({ alias: 'bunny', src: 'bunny.png' });


  • PIXI.settingsの廃止

Old:

import { settings, BrowserAdapter } from 'pixi.js';

settings.RESOLUTION = 1;
settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
settings.ADAPTER = BrowserAdapter;

New:

import { AbstractRenderer, DOMAdapter, BrowserAdapter } from 'pixi.js';

// Can also be passed into the renderer directly e.g `autoDetectRenderer({resolution: 1})`
AbstractRenderer.defaultOptions.resolution = 1;

// Can also be passed into the renderer directly e.g `autoDetectRenderer({failIfMajorPerformanceCaveat: false})`
AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat = false;

// See below for more information about changes to the adapter
DOMAdapter.set(BrowserAdapter);

SCALE_MODEなんかは存在がなくなってた

  • アダプターとWebWorkerの変更

    • settings.ADAPTERは廃止。DOMAdapterで置き換え
    • 詳しくは原文参照("Adapter and Web Worker Changes" の項)

  • Application生成時にレンダラーのタイプを明示できるようになった?

Old:

const app = new Application<HTMLCanvasElement>();
```js
New:
``` js
// WebGL or WebGPU renderer
const app = new Application<Renderer<HTMLCanvasElement>>();
// WebGL specific renderer
const app = new Application<WebGLRenderer<HTMLCanvasElement>>();
// WebGPU specific renderer
const app = new Application<WebGPURenderer<HTMLCanvasElement>>(); 


  • Texture.from()にURLを渡してテクスチャをロードすることはできなくなった。URLからテクスチャ生成したい場合は一度await Assets.load(url)してからTexture.from(url)する必要がある。

  • Tickerを使ったコールバックにはデルタタイムでなくTickerインスタンスを渡すよう変更

Old:

Ticker.shared.add((dt)=> {
    bunny.rotation += dt
});

New:

Ticker.shared.add((ticker)=> {
    bunny.rotation += ticker.deltaTime;
});

※大量に使ってたりするとめんどくさそう…

  • テキストパーサーのリネーム

    • TextFormat -> bitmapFontTextParser
    • XMLStringFormat -> bitmapFontXMLStringParser
    • XMLFormat -> bitmapFontXMLParser

  • デフォルトのeventModeautoからpassiveへ ※v6からあるinteractiveの拡張らしいけど使ったことない

  • utils廃止。utilsにあったものは本体をインポートすれば使える。

Old:

import { utils } from 'pixi.js';

utils.isMobile.any();

New:

import { isMobile } from 'pixi.js';

isMobile.any();


  • container.getBounds()RectangleでなくBoundsを返すようになった。今まで通りに使いたい場合は、container.getBounds().rectangleRectangleにアクセスできる。

  • ParticleContainerの廃止。代わりに通常のContainerを使ってほしい。WebGPU対応でパフォーマンスが上がったからもうParticleContainerを使う必要ないよ。

3.非推奨となった仕様

deprecatedとなった仕様たち。
v7の一部仕様もv8で廃止されたそうです。

  • 葉ノードは子要素を持てなくなりました
    • 子要素を持てるのはContainerのみ。SpriteMeshGraphicsはだめ。
    • 今まで通りに動作させたいなら親Containerを作って子ともどもぶちこんでね。

Old:

const sprite = new Sprite();
const spriteChild = new Sprite();
sprite.addChild(spriteChild);

New:

const container = new Container();
const sprite = new Sprite();
const spriteChild = new Sprite();

container.addChild(sprite);
container.addChild(spriteChild);


  • Application.viewApplication.canvasに置き換わりました

Old:

const app = new Application({ view: document.createElement('canvas') });
document.body.appendChild(app.view);

New:

const app = new Application();
await app.init({ view: document.createElement('canvas') });
document.body.appendChild(app.canvas);


  • NineSlicePlaneNineSliceSprite にリネーム ※Planeてなんじゃいと思ってはいた

  • SCALE_MODES定数をScaleMode文字列で置き換え

    • SCALE_MODES.NEAREST -> 'nearest'
    • CALE_MODES.LINEAR -> 'linear'
  • WRAP_MODES,DRAW_MODESも同様

  • いずれもsettingsからは消えている

  • 複数の引数を渡していたコンストラクタは、代わりにオブジェクトを渡すよう変更


Old:

const blurFilter = new BlurFilter(8, 4, 1, 5);
const displacementFilter = new DisplacementFilter(sprite, 5);
const meshGeometry = new MeshGeometry(vertices, uvs, index);
const mesh = new Mesh(geometry, shader, state, drawMode);
const plane = new PlaneGeometry(width, height, segWidth, segHeight);
const nineSlicePlane = new NineSlicePlane(texture, leftWidth, topHeight, rightWidth, bottomHeight);
const tileSprite = new TileSprite(texture, width, height);
const text = new Text('Hello World', style);
const bitmapText = new BitmapText('Hello World', style);
const htmlText = new HTMLText('Hello World', style);

New:

const blurFilter = new BlurFilter({
    blur: 8,
    quality: 4,
    resolution: 1,
    kernelSize: 5,
});
const displacementFilter = new DisplacementFilter({
    sprite,
    scale: 5,
});
const meshGeometry = new MeshGeometry({
    positions: vertices,
    uvs,
    indices: index,
    topology: 'triangle-list';
    shrinkBuffersToFit: boolean;
});
const mesh = new Mesh({
    geometry
    shader
    texture
});
const plane = new PlaneGeometry({
    width,
    height,
    verticesX: segWidth,
    verticesY: segHeight,
});
const nineSliceSprite = new NineSliceSprite({
    texture,
    leftWidth,
    topHeight,
    rightWidth,
    bottomHeight,
});
const tileSprite = new TileSprite({
    texture,
    width,
    height,
});
const text = new Text({
    text: 'Hello World',
    style,
});
const bitmapText = new BitmapText({
    text:'Hello World',
    style,
});
const htmlText = new HTMLText({
    text:'Hello World',
    style,
});

※Text系直すのめんどくせ~~~~~~~~~~~~~~~~


  • container.namecontainer.labelにリネーム

4.資料

感想

変更が多い…!
がっつり使ってない人でもぼちぼちコード修正を強いられるかと思います。
爆速レンダリングをてにいれるためだ仕方ない…

おわり

Discussion