Live2Dを初めて導入した話

f:id:sumzap_engineer_blog:20200706151207p:plain

はじめに

2020年2月にリリースした「この素晴らしい世界に祝福を!ファンタスティックデイズ」(略称:このファン)にて、弊社としては初めて Live2D を導入しました。
本記事では、Live2D を Unity でどのように扱っていったかやこだわった点などについて紹介していきたいと思います。

Live2D とは

Live2D とは、Live2D 社が開発した、パーツごとに分けられた 1 枚の画像から動く 2D モデルを作る為のツールです。

Live2D を Unity へ組み込むまでの大まかな制作の流れは次のようになります。

  1. 原画を動かしたいパーツごとに分けて、psd データなどにする
  2. Cubism Editor で、各パーツにポリゴンを割り当て、ポリゴンの動きをパラメータ化する(=モデルデータの作成)
  3. Cubism Editor で、2. で定義した複数のパラメータを時間変化させて、アニメーションを作成する(=モーションデータの作成)
  4. Cubism Editor で、Cubism SDK for Unity が扱える形式でモデルデータとモーションデータを出力する
  5. Unity プロジェクト内の適当なフォルダに4. で出力したデータを D&D などで取り込む
  6. Cubism SDK のインポーターが走り、モデルのプレハブとモーションの AnimationClip が自動生成される

以下のセクションでは、6. で生成されるモデルのプレハブとモーションの AnimationClip をどのように扱っていったかについて、お伝えできればと思います。

モーション周りのデータ構造

本プロジェクトでは、AnimationClip の再生制御を Mecanim で行っていきました。
モーションデータには、全身のパーツが可動対象である『ベースモーション』と顔のパーツだけが可動対象の『表情』の 2 種類があり、 それらを AnimatorController のそれぞれ別のレイヤーに配置して、『ベースモーション』の上から『表情』を被せて再生しています。これにより、計算上ではベースモーションの数 × (表情の数 + 1)通りのモーションを再生することができます。 f:id:sumzap_engineer_blog:20200615165411p:plain

モーションの遷移

モーションの遷移は、基本的に Animator クラスの CrossFadeInFixedTime メソッドで行いました。
このメソッドでは、状態遷移にかける時間を規格化された値ではなく実時間(秒)で指定します。

public void CrossFadeInFixedTime (string stateName, float fixedTransitionDuration, int layer= -1, float fixedTimeOffset= 0.0f);

引数名 内容
stateName ステート名
fixedTransitionDuration 状態遷移にかかる時間(秒)
layer クロスフェード対象のレイヤー
fixedTimeOffset モーションのどの地点からクロスフェードを開始するか(秒)

前のセクションで述べたように『ベースモーション』と『表情』は別のレイヤーにあるため、第 3 引数でレイヤーを指定してレイヤーごとに CrossFadeInFixedTime メソッドを実行しています。
第 4 引数は、モーションが終了した時点のポーズでキャラクターを表示させる時に使用しており、モーションの再生時間長を指定しています。

はまった点としては、遷移先のモーションに表情が指定されてない場合はアニメーションのキーが何も打たれてない空のステートに遷移させるのですが、その際に fixedTransitionDuration で指定した値が反映されず、瞬間的に表情が外れるという挙動になってしまいました。これについては、CrossFadeInFixedTime メソッドの代わりに表情のレイヤーの Weight を Animator クラスの SetLayerWeight メソッドで時間的に 0 まで変化させていくことで回避しました。

パラメータの制御

Cubism SDK には、Live2D モデルに付けられているパラメータをランタイムで更新する口が用意されています。具体的には、CubismParameter クラスの Value というフィールドに値を設定できるようになっています。目のパーツを切り替えたり目を閉じ続けさせたりするといった特定のパーツに対して簡単な制御をしたい時に使うと良さそうです。
本プロジェクトでは、次に示すパラメータをランタイムで制御するようにしました。

  • 呼吸パラメータ
  • 目の開閉パラメータ
  • 口の開閉パラメータ
  • 顔影パラメータ
  • 目のパーツの切り替えパラメータ

1つ注意することとしては、Cubism SDK のバージョンが 2.1 か 3 以降かによってパラメータを更新する処理に違いがあるので、バージョンに合わせた実装をする必要があります。
本プロジェクトで採用した Cubism SDK 3 系では、パラメータの更新処理はアニメーションの値が反映される後の LateUpdate で行うようにします。Update でパラメータの更新を行うとアニメーションで定義されている値に上書きされてしまいます。

参考:https://docs.live2d.com/cubism-sdk-tutorials/about-parameterupdating-of-model/

こだわった表現

1.ランダムな要素を持たせた呼吸とまばたき

クロスフェードで再生する一連のモーションの再生が終わったら、キャラクターの動きはランタイムでの呼吸とまばたき制御に任されます。 呼吸とまばたきパラメータの値の周期的な時間変化は CubismAutoEyeBlinkInput クラスの実装に倣い、Sin 波で表現しました。 その際に、固定の Sin 波で変化させると規則性が見えてしまい機械的な動きになってしまったので、1 周期ごとに時間変化に関するパラメータをある値の範囲内でランダムに更新するようにしました。具体的には、以下のパラメータにランダム性を持たせました。

  • 呼吸パラメータを変化させる Sin 波の振幅
  • 呼吸パラメータを変化させる Sin 波の周期時間
  • まばたきのインターバル時間

f:id:sumzap_engineer_blog:20200702210201g:plain:w400
f:id:sumzap_engineer_blog:20200702210357g:plain:w400

呼吸とまばたきのランダム性有り無しの比較(左図: ランダム性無し 右図: ランダム性有り)

2.環境光の表現

キャラクターを表示した際に、背景によってはキャラクターが変に浮きだって見えてしまうことがあったため、背景の色合いに応じて Live2D モデルのテクスチャに指定した色を乗算できるようにしました。
テクスチャに乗算する色を指定するには、CubismRenderController クラスが持つ CubismRenderer の配列プロパティの各要素に対して、CubismRederer クラスの Color プロパティに乗算する色を設定します。
本プロジェクトでは、ストーリーパートにてキャラクターを半透明状態で表示するという場面もあったことから、Live2D キャラの描画に RenderTexture を使っており、乗算カラーの指定は RenderTexture に対して行いました。

f:id:sumzap_engineer_blog:20200701222042p:plainf:id:sumzap_engineer_blog:20200701222102p:plain

環境光の有り無しの比較(左図: 環境光無し 右図: 環境光有り)

CPU 負荷対策

Cubism SDK のバージョン 3 系では、Live2D モデルの描画を CubismModel クラスの OnRenderObject メソッド内で行っており、シーン内にカメラが複数ある場合、Live2D キャラ1体につきカメラの数分の描画処理が呼ばれていました。
そこで、Camera.current で取得されるカメラが Live2D キャラを写しているカメラ以外の時は、return で処理を抜けることで Live2D の描画処理が行われないようにし、CPU 負荷を抑えました。

private void OnRenderObject()
{
    // Live2Dを写しているカメラでなければ、後続のLive2Dの描画の更新処理は行わない。
    if (!Camera.current.CompareTag(LIVE2D_CAMERA_TAG))
    {
        return;
    }

    // 以下、Live2Dの描画の更新処理(略)
}

まとめ

Live2D を使うのは初めての試みでしたが、Live2D のデータを Unity に取り込んでしまえば実際に扱っていくのは AnimationClip のような Unity 純正のアセットでしたので、比較的容易に導入することができました。
Unity標準の機能の範囲内ならば、ちょっと凝ったことをしたいという時にも安心して触っていけるのではないかと思います。



©2019 暁なつめ・三嶋くろね/KADOKAWA/映画このすば製作委員会 ©Sumzap, Inc.