MotionFade

最終更新: 2019年3月28日

概要

Unity上でViewer for OWと同じモーションフェード、パラメータフェードを再現するには、MotionFadeを使用します。

Cubismのモーションフェード、パラメータフェードについて詳しくはこちらをご覧ください。

Viewer for OWでは、モーションが切り替わる際は、それぞれがイージングしながらフェードイン/フェードアウトします。
しかし、Unityではアニメーションが切り替わる際は、イージングせずリニアでのフェードのみとなっており、Viewer for OWと同じフェード表現するためにはこの機能を使用します。

MotionFadeは以下の処理を行います。

  1. [.fade.asset]を作成
  2. [.fadeMotionList]を作成し、[.fade.asset]を追加
  3. AnimationClipのinstanceIDを保存
  4. 再生モーションリストを作成
  5. フェードの計算と適用

[.fade.asset]を作成

[.fade.asset]は[motion3.json]からコンバートし、フェードに使用するブレンド用のカーブを含んでいるScriptableObjectのアセットです。
[.fade.asset]は[motion3.json]をインポートしたときにAnimationClipとともに生成されます。
[.fade.asset]を変更した場合、正常な動作は保証しません。

[exp.json]を[.fade.asset]にコンバートするには以下の処理を行います。

  1. [motion3.json]をパース
  2. CubismFadeMotionData作成
  3. [.fade.asset]作成

[motion3.json]をパース

[motion3.json]のパースは CubismMotion3Json.LoadFrom(string motion3Json) または CubismMotion3Json.LoadFrom(TextAsset motion3JsonAsset) を使います。

  • string motion3Json[motion3.json]の文字列。
  • TextAsset motion3JsonAsset[motion3.json]のテキストファイルアセット。

CubismFadeMotionData作成

CubismFadeMotionDataはフェード用の情報を記録するクラスで、以下の情報を保持します。

フィールド説明
MotionNamestringモーションの名前。
FadeInTimefloatモーションがフェードインするまで時間。値の単位は秒。
FadeOutTimefloatモーションがフェードアウトするまで時間。値の単位は秒。
ParameterIdsstring[]モーションが使用するパラメータID。
ParameterCurvesAnimationCurve[]モーションがパラメータに適用するカーブ。
ParameterFadeInTimesfloat[]パラメータフェードによってそのパラメータがフェードインするまでの時間。値の単位は秒。負の値が設定された場合、FadeInTimeが適用される。
ParameterFadeOutTimesfloat[]パラメータフェードによってそのパラメータがフェードアウトするまでの時間。値の単位は秒。負の値が設定された場合、FadeOutTimeが適用される。
MotionLengthfloatそのモーションの長さ。値の単位は秒。

[.fade.asset]を新規作成、上書き保存するにはCreateInstanceを使用します。
第一引数に、既に生成されたCubismFadeMotionDataのインスタンスを渡した場合、そのインスタンスに上書きして処理します。

CubismFadeMotionData.CreateInstance(CubismMotion3Json motion3Json, string motionName, float motionLength, bool shouldImportAsOriginalWorkflow = false, bool isCallFormModelJson = false);
CubismFadeMotionData.CreateInstance(CubismFadeMotionData fadeMotion, CubismMotion3Json motion3Json, string motionName, float motionLength, bool shouldImportAsOriginalWorkflow = false, bool isCallFormModelJson = false);
引数説明
fadeMotionCubismFadeMotionData上書き対象となるCubismFadeMotionData。
motion3JsonCubismMotion3Json.motion3.jsonをパースしたデータ。
motionNamestringモーションの名前。
motionLengthfloatモーションの長さ。値の単位は秒。
shouldImportAsOriginalWorkflowboolOriginalWorkflowとしてインポートするかどうか。
isCallFormModelJsonboolmodel3.jsonのインポート処理から呼び出されているか。

[.fade.asset]作成

[.fade.asset]は、AssetDatabase.CreateAsset()を用いて作成します。

// 新規作成する場合
fadeMotion = CubismFadeMotionData.CreateInstance(
    importer.Motion3Json,
    importer.AssetPath.Replace(directoryPath, ""),
    animationClip.length,
    CubismUnityEditorMenu.ShouldImportAsOriginalWorkflow,
    CubismUnityEditorMenu.ShouldClearAnimationCurves);

AssetDatabase.CreateAsset(fadeMotion, importer.AssetPath.Replace(".motion3.json", ".fade.asset"));

// 既に作成されたアセットを更新の場合
fadeMotion = CubismFadeMotionData.CreateInstance(
    oldFadeMotion,
    importer.Motion3Json,
    importer.AssetPath.Replace(directoryPath, ""),
    animationClip.length,
    CubismUnityEditorMenu.ShouldImportAsOriginalWorkflow,
    CubismUnityEditorMenu.ShouldClearAnimationCurves);

EditorUtility.CopySerialized(fadeMotion, oldFadeMotion);

// Unityのデータベースを更新
EditorUtility.SetDirty(fadeMotion);

この処理は CubismFadeMotionImporter.OnFadeMotionImport() で行っています。

[.fadeMotionList]を作成し、[.fade.asset]を追加

[.fadeMotionList.asset]は[.fade.asset]と対になるAnimationClipのインスタンスIDを一組にして保持しているアセットです。
[.fade.asset]リストの順番は[motion3.json]のインポート順で追加されます。
[.fadeMotionList.asset]を変更した場合、正常な動作は保証しません。

[.fadeMotionList]を作成

[.fadeMotionList.asset]は、モデルのプレハブと同じ階層に作成します。

// fadeMotionListを作成する
fadeMotions = ScriptableObject.CreateInstance<CubismFadeMotionList>();
fadeMotions.MotionInstanceIds = new int[0];
fadeMotions.CubismFadeMotionObjects = new CubismFadeMotionData[0];
AssetDatabase.CreateAsset(fadeMotions, fadeMotionListPath);

この処理は CubismFadeMotionImporter.OnFadeMotionImport() で行っています。

[.fade.asset]を追加

[.fadeMotionList.asset]に[.fade.asset]を追加するには、以下のように行います。

// 既存のデータを更新する時
fadeMotions.CubismFadeMotionObjects[motionIndex] = fadeMotion;

// 新規データを追加する時
motionIndex = fadeMotions.MotionInstanceIds.Length;

Array.Resize(ref fadeMotions.MotionInstanceIds, motionIndex+1);
fadeMotions.MotionInstanceIds[motionIndex] = instanceId;

Array.Resize(ref fadeMotions.CubismFadeMotionObjects, motionIndex+1);
fadeMotions.CubismFadeMotionObjects[motionIndex] = fadeMotion;

// Unityのデータベースを更新
EditorUtility.SetDirty(fadeMotions);

この処理は CubismFadeMotionImporter.OnFadeMotionImport() で行っています。

AnimationClipのinstanceIDを保存

AnimationClipのインスタンスIDは、そのAnimationClipにAnimationEventとして保存します。
※ 実行する環境によって AnimationClip.GetInstanceID() で取得したインスタンスIDがUnity上とランタイムとで異なる場合があるため、AnimationClipの判別ができなくなることがあります。

// インスタンスID保存用のイベントを取得
var sourceAnimEvents = AnimationUtility.GetAnimationEvents(animationClip);
AnimationEvent instanceIdEvent= null;
for(var i = 0; i < sourceAnimEvents.Length; ++i)
{
    if(sourceAnimEvents[i].functionName != "InstanceId")
    {
        continue;
    }

    instanceIdEvent= sourceAnimEvents[i];
    break;
}

// インスタンスID保存用のイベントがない場合、イベントを追加
if(instanceIdEvent== null)
{
    instanceIdEvent= new AnimationEvent();
    Array.Resize(ref sourceAnimEvents, sourceAnimEvents.Length + 1);
    sourceAnimEvents[sourceAnimEvents.Length - 1] = instanceIdEvent;
}

// インスタンスIDを設定
instanceIdEvent.time = 0;
instanceIdEvent.functionName = "InstanceId";
instanceIdEvent.intParameter = instanceId;
instanceIdEvent.messageOptions = SendMessageOptions.DontRequireReceiver;

// AnimationClipにイベントを設定
AnimationUtility.SetAnimationEvents(animationClip, sourceAnimEvents);

この処理は CubismFadeMotionImporter.OnFadeMotionImport() で行っています。

再生モーションリストを作成

再生モーションリストは、フェードの計算をする際に参照するリストとなっており、 CubismFadePlayingMotion を使用して再生中のモーションの情報もまとめて保持しています。

CubismFadePlayingMotionについて

CubismFadePlayingMotionは以下のモーションの再生情報とフェード情報を持つクラスです。

  • StartTime:モーションの再生開始時間。値の単位は秒。
  • EndTime:モーションの終了時間。値の単位は秒。
  • FadeInStartTime:モーションのフェードイン開始時間。値の単位は秒。
  • Speed:モーションの再生速度。速度の範囲は0以上。1がCubism Editorで設定した速度と同じ速度。
  • Motion:モーションのフェード用情報を持つCubismFadeMotionData。

ICubismFadeStateについて

ICubismFadeStateは、フェード計算で必要な情報を取得するためのインターフェースです。

ICubismFadeStateのAPI:

  • List<CubismFadePlayingMotion> GetPlayingMotions()
    • 再生モーションリストの取得。
  • bool IsDefaultState()
    • デフォルトステートかどうか。
  • float GetLayerWeight()
    • レイヤーのウェイトを取得。
  • bool GetStateTransitionFinished()
    • トランジションが終了してるかどうか。
  • void SetStateTransitionFinished(bool isFinished)
    • トランジションの終了を設定。
  • void StopAnimation(int index)
    • インデックスで指定したアニメーションの再生を停止。

CubismFadeStateObserverについて

CubismFadeStateObserverはICubismFadeStateを継承し、Animatorのステートを監視して再生モーションリストを作成するクラスです。

再生モーションリストはアニメーション再生時に呼ばれる OnStateEnter() で作成します。

// 再生するAnimationClipsを取得
var animatorClipInfo = controller.GetNextAnimatorClipInfo(layerIndex);

// 再生中のアニメーションの終了時間を設定
for (var i = 0; i < _playingMotions.Count; ++i)
{
    var motion = _playingMotions[i];

    if (motion.Motion == null)
    {
        continue;
    }

    var newEndTime = Time.time + motion.Motion.FadeOutTime;

    if (motion.EndTime < 0.0f || newEndTime < motion.EndTime)
    {
        motion.EndTime = newEndTime;
    }
}

// 再生アニメーション作成と追加
for (var i = 0; i < animatorClipInfo.Length; ++i)
{
    CubismFadePlayingMotion playingMotion;

    /*省略*/

    playingMotion.Motion = (motionIndex == -1)
        ? null
        : _cubismFadeMotionList.CubismFadeMotionObjects[motionIndex];

    playingMotion.Speed = 1.0f;
    playingMotion.StartTime = Time.time;
    playingMotion.FadeInStartTime = Time.time;
    playingMotion.EndTime = (playingMotion.Motion.MotionLength <= 0)
        ? -1
        : playingMotion.StartTime + playingMotion.Motion.MotionLength + playingMotion.Motion.FadeOutTime;

    _playingMotions.Add(playingMotion);
}

この処理は CubismFadeMotionImporter.OnFadeMotionImport() で行っています。

フェードの計算と適用

フェードの計算と適用は CubismFadeController.UpdateFade() で行っています。

MotionFadeの適用はアニメーション処理の後に行う必要があります。

この記事はお役に立ちましたか?
はいいいえ
この記事に関するご意見・
ご要望をお聞かせください。