MotionFade

Updated: 03/28/2019

Summary

To reproduce the same motion and parameter fades as Viewer for OW in Unity, use MotionFade.

For more information on motion fades and parameter fades in Cubism, click here.

In Viewer for OW, when the motion switches, each fades in and out with blending.
However, in Unity, when the animation switches, there is only a linear fade without blending, and this feature is used to express the same fade as in the Viewer for OW.

MotionFade performs the following processes.

  1. Create [.fade.asset]
  2. Create [.fadeMotionList] and add [.fade.asset]
  3. Save the instanceID of the AnimationClip
  4. Create playback motion list
  5. Calculate and apply fades

Create [.fade.asset]

[.fade.asset] is a ScriptableObject asset that converts from [motion3.json] and contains curves for blending used for fades.
The [.fade.asset] is generated with the AnimationClip when importing [motion3.json].
Normal operation is not guaranteed if [.fade.asset] is changed.

To convert [exp.json] to [.fade.asset], do the following.

  1. Parse [motion3.json]
  2. Create CubismFadeMotionData
  3. Create [.fade.asset]

Parse [motion3.json]

Use CubismMotion3Json.LoadFrom(string motion3Json) or CubismMotion3Json.LoadFrom(TextAsset motion3JsonAsset) to parse [motion3.json].

  • The string of string motion3Json[motion3.json]
  • The text file asset of TextAsset motion3JsonAsset[motion3.json].

Create CubismFadeMotionData

CubismFadeMotionData is a class that records information for fades and holds the following information.

FieldTypeDescription
MotionNamestringThe name of the motion.
FadeInTimefloatTime until motion fades in. The unit of value is seconds.
FadeOutTimefloatTime until motion fades out. The unit of value is seconds.
ParameterIdsstring[]Parameter ID used by the motion.
ParameterCurvesAnimationCurve[]Curves that motion applies to parameters.
ParameterFadeInTimesfloat[]The time it takes for the parameter to fade in. The unit of value is seconds. If a negative value is set, FadeInTime is applied.
ParameterFadeOutTimesfloat[]The time it takes for the parameter to fade out. The unit of value is seconds. If a negative value is set, FadeOutTime is applied.
MotionLengthfloatThe length of the motion. The unit of value is seconds.

Use CreateInstance to create a new [.fade.asset] and overwrite it.
If a previously generated instance of CubismFadeMotionData is passed as the first argument, it will be overwritten and processed.

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);
ArgumentTypeDescription
fadeMotionCubismFadeMotionDataCubismFadeMotionData to be overwritten.
motion3JsonCubismMotion3JsonData parsed from .motion3.json.
motionNamestringThe name of the motion.
motionLengthfloatThe length of the motion. The unit of value is seconds.
shouldImportAsOriginalWorkflowboolDetermine whether to import as OriginalWorkflow.
isCallFormModelJsonboolDetermine whether to be called from the model3.json import process.

Create [.fade.asset]

The [.fade.asset] is created using AssetDatabase.CreateAsset().

// When creating a new asset
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"));

// When updating a previously created asset
fadeMotion = CubismFadeMotionData.CreateInstance(
    oldFadeMotion,
    importer.Motion3Json,
    importer.AssetPath.Replace(directoryPath, ""),
    animationClip.length,
    CubismUnityEditorMenu.ShouldImportAsOriginalWorkflow,
    CubismUnityEditorMenu.ShouldClearAnimationCurves);

EditorUtility.CopySerialized(fadeMotion, oldFadeMotion);

// Update Unity database.
EditorUtility.SetDirty(fadeMotion);

This is done by CubismFadeMotionImporter.OnFadeMotionImport().

Create [.fadeMotionList] and add [.fade.asset]

[.fadeMotionList.asset] is an asset that holds a set of AnimationClip instance IDs that are paired with [.fade.asset].
The order of the [.fade.asset] list is added in the import order of [motion3.json].
Normal operation is not guaranteed if [.fadeMotionList.asset] is changed.

Creating [.fadeMotionList]

The [.fadeMotionList.asset] is created using the same hierarchy as the model prefab.

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

This is done by CubismFadeMotionImporter.OnFadeMotionImport().

Adding [.fade.asset]

To add [.fade.asset] to [.fadeMotionList.asset], do the following.

// When updating existing data
fadeMotions.CubismFadeMotionObjects[motionIndex] = fadeMotion;

// When adding new data.
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; 

// Update Unity database. 
EditorUtility.SetDirty(fadeMotions);

This is done by CubismFadeMotionImporter.OnFadeMotionImport().

Save the instanceID of the AnimationClip

The instance ID of an AnimationClip is stored in that AnimationClip as an AnimationEvent.
Note: Depending on the execution environment, the instance ID obtained from AnimationClip.GetInstanceID() may be different in Unity and at runtime, making it impossible to determine the AnimationClip.

// Get event for instance ID storage
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;
}

// If there is no event for instance ID storage, add an event.
if(instanceIdEvent== null)
{
    instanceIdEvent= new AnimationEvent();
    Array.Resize(ref sourceAnimEvents, sourceAnimEvents.Length + 1);
    sourceAnimEvents[sourceAnimEvents.Length - 1] = instanceIdEvent;
}

// Set instance ID 
instanceIdEvent.time = 0;
instanceIdEvent.functionName = "InstanceId";
instanceIdEvent.intParameter = instanceId;
instanceIdEvent.messageOptions = SendMessageOptions.DontRequireReceiver;

// Set events in AnimationClip 
AnimationUtility.SetAnimationEvents(animationClip, sourceAnimEvents);

This is done by CubismFadeMotionImporter.OnFadeMotionImport().

Create playback motion list

The playback motion list is a list that is referenced when calculating fades, and it also holds information about motions that are being played back using CubismFadePlayingMotion.

CubismFadePlayingMotion

CubismFadePlayingMotion is a class with playback and fade information for the following motions.

  • StartTime: Start time of the motion playback. The unit of value is seconds.
  • EndTime: End time of the motion. The unit of value is seconds.
  • FadeInStartTime: Fade-in start time for motion. The unit of value is seconds.
  • Speed: Motion playback speed. The range of speed is greater than or equal to 0. 1 is the same speed as set in the Cubism Editor.
  • Motion: CubismFadeMotionData contains information for motion fades.

ICubismFadeState

ICubismFadeState is an interface for obtaining the information needed in fade calculations.

API for ICubismFadeState:

  • List GetPlayingMotions()
    • Get playback motion list.
  • bool IsDefaultState()
    • Determine whether or not it is the default state.
  • float GetLayerWeight()
    • Get the weight of the layer.
  • bool GetStateTransitionFinished()
    • Determine whether transitions have been completed.
  • void SetStateTransitionFinished(bool isFinished)
    • Set the end of the transition.
  • void StopAnimation(int index)
    • Stop playback of the animation specified by the index.

CubismFadeStateObserver

CubismFadeStateObserver is a class that inherits from ICubismFadeState and monitors the Animator’s state to create a playback motion list.

The playback motion list is created by OnStateEnter(), which is called during animation playback.

// Get AnimationClips to play
var animatorClipInfo = controller.GetNextAnimatorClipInfo(layerIndex);

// Set the end time of the animation currently playing
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;
    }
}

// Create and add playback animation
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);
}

This is done by CubismFadeMotionImporter.OnFadeMotionImport().

Calculate and apply fades

The fade is calculated and applied with CubismFadeController.UpdateFade().

MotionFade must be applied after the animation process.

Was this article helpful?
YesNo
Please let us know what you think about this article.