🎥

PixiJS x Spineでキャラクターアニメーションを実装してみる【ウユニ塩湖】

2023/11/12に公開

かなりご無沙汰の Spine 記事です。
今回は PixiJS / PixiJS Filters の ReflectionFilter / pixi-spine を利用して、キャラクターアニメーションを実装してみました。

成果物

画面キャプチャ

成果物のURLはコチラです。
> pixijs spine uyuni-lake page

ウユニ塩湖をイメージしたアニメーションです。
(SP最適化は未実装のため、PCにて確認してください。)
イラストは Stable Diffusion で生成して、Photoshop + ペンタブで書き足したり、調整しました。

主な npm パッケージとバージョン

パッケージ バージョン
lil-gui ^0.18.2
@pixi/filter-reflection ^5.1.1
pixi-spine ^4.0.4
pixi.js ^7.3.1
typescript ^4.8.4

ソースコードの解説

リポジトリはコチラです。
> t-tonyo-maru/pub_web_pixijs_uyuni-lake

いろいろと格納されていますが、主要なファイルは以下の main.ts のみです。
> /src/ts/main.ts

少々長いので要所だけを解説していきます。

Spine アニメーション

Spine エクスポートファイルは PIXI.Assets を利用して読み込みます。
Replaces Loader with Assets | PixiJS v7 Migration Guide にあるように、PixiJS は v7 から PIXI.Assets.load を利用して、各種アセットファイルを読み込むカタチになりました。

import 'pixi-spine'
import { Spine } from 'pixi-spine'

// Spine エクスポートファイルの読み込み
const spineAnimation = await PIXI.Assets.load(
  `path/to/spine-data/model.json`
)
  .then((resource) => {
    // 読み込み成功時
    // pixi-spine より Spine Animation インスタンスを生成
    const animation = new Spine(resource.spineData)

    // Spine Animation の位置・縮尺率をセット
    animation.x = app.screen.width / 2
    animation.y = app.screen.height
    animation.scale.set(1)

    // 一番はじめのトラックに idle アニメーション追加
    // setAnimation() の第 3 引数に true を指定して、アニメーションをループさせる
    animation.state.setAnimation(0, 'idle', true)

    // Pixi.Container に Spine Animation を追加
    container.addChild(animation as PIXI.DisplayObject)

    // 画面リサイズ時に Spine Animation の位置・縮尺率を調整するため、Spine Animation を返します
    return animation
  })
  .catch((err) => {
    // 読み込み失敗時
    console.log(err)
  })

Reflection filter

画面下部の鏡面は PixiJS の ReflectionFilter を利用しています。
PixiJS の Filter は、インスタンスを生成して PIXI.Containerfilters に追加するだけで適用できます。簡単ですね。

import { ReflectionFilter } from '@pixi/filter-reflection'

// Reflection filter のパラメータ
const reflectionParameter = {
  mirror: true,
  boundary: 0.8,
  amplitude: [0, 20],
  waveLength: [30, 100],
  alpha: [1, 1]
}
// Reflection filter のインスタンスを生成
const reflectionFilter = new ReflectionFilter(reflectionParameter)
// PIXI.Container に Reflection filter を適用
container.filters = [reflectionFilter]

lil-gui

lil-gui は Web ページ上にコントロールパネルを表示して、パラメータを制御できるパッケージです。
画面右上に表示される黒いコントロールパネルが lil-gui によって生成されたモノです。
Three.js のサンプルページなどでよく見かけますね。

lil-gui コントロールパネル

Spine アニメーションの実装に必須ではありませんが、アニメーションの制御や Reflection filter の効果の確認のために導入しています。

import GUI from 'lil-gui'

// lil-gui のインスタンスを生成
const gui = new GUI()
// コントロールパネルで制御するパラメータ
const guiObject = {
  enable: true,
  mirror: reflectionParameter.mirror,
  boundary: reflectionParameter.boundary,
  ['amplitude.start']: 0,
  ['amplitude.end']: 20,
  ['waveLength.start']: 30,
  ['waveLength.end']: 100,
  ['alpha.start']: 0.2,
  ['alpha.end']: 1,
  spine: {
    enable: true,
    laughAlpha: 0
  },
  background: {
    isShowBackground: true
  }
}
// Reflection filter / Spine アニメーションのパラメータをセット
const generalFolder = gui.addFolder('Reflection General')
generalFolder.add(guiObject, 'enable')
const parameterFolder = gui.addFolder('Reflection Main Parameter')
parameterFolder.add(guiObject, 'mirror')
parameterFolder.add(guiObject, 'boundary', 0, 1, 0.01)
parameterFolder.add(guiObject, 'amplitude.start', 0, 50, 0.1)
parameterFolder.add(guiObject, 'amplitude.end', 0, 50, 0.1)
parameterFolder.add(guiObject, 'amplitude.start', 10, 200, 1)
parameterFolder.add(guiObject, 'amplitude.end', 10, 200, 1)
parameterFolder.add(guiObject, 'alpha.start', 0, 1, 0.01)
parameterFolder.add(guiObject, 'alpha.end', 0, 1, 0.01)
const spineFolder = gui.addFolder('Spine Animatoin Parameter')
spineFolder.add(guiObject.spine, 'enable')

まとめ

PixiJS を使うと Spine アニメーションを簡単に表示できますし、Filter 機能との連携も容易です。
次回もまた別のアニメーションを作っていきたいと思います。

では、また!

Discussion