/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
using Live2D.Cubism.Core;
using Live2D.Cubism.Framework.MotionFade;
using System;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
namespace Live2D.Cubism.Framework.Motion
{
///
/// Cubism motion controller.
///
[RequireComponent(typeof(CubismFadeController))]
public class CubismMotionController : MonoBehaviour
{
#region Action
///
/// Action animation end handler.
///
[SerializeField]
public Action AnimationEndHandler;
///
/// Action OnAnimationEnd.
///
private void OnAnimationEnd(int layerIndex, float instanceId)
{
_motionPriorities[layerIndex] = CubismMotionPriority.PriorityNone;
if (AnimationEndHandler != null)
{
AnimationEndHandler(instanceId);
}
}
#endregion
#region Variable
///
/// Layer count.
///
public int LayerCount = 1;
///
/// List of cubism fade motion.
///
private CubismFadeMotionList _cubismFadeMotionList;
///
/// Motion controller is active.
///
private bool _isActive = false;
///
/// Playable graph controller.
///
private PlayableGraph _playableGrap;
///
/// Playable output.
///
private AnimationPlayableOutput _playableOutput;
///
/// Animation layer mixer.
///
private AnimationLayerMixerPlayable _layerMixer;
///
/// Cubism motion layers.
///
private CubismMotionLayer[] _motionLayers;
///
/// Cubism motion priorities.
///
private int[] _motionPriorities;
#endregion Variable
#region Function
///
/// Play animations.
///
/// Animator clip.
/// layer index.
/// Animation priority
/// Animation is loop.
/// Animation speed.
public void PlayAnimation(AnimationClip clip, int layerIndex = 0, int priority = CubismMotionPriority.PriorityNormal, bool isLoop = true, float speed = 1.0f)
{
// Fail silently...
if(!enabled || !_isActive || _cubismFadeMotionList == null || clip == null
|| layerIndex < 0 || layerIndex >= LayerCount ||
((_motionPriorities[layerIndex] >= priority) && (priority != CubismMotionPriority.PriorityForce)))
{
Debug.Log("can't start motion.");
return;
}
_motionPriorities[layerIndex] = priority;
_motionLayers[layerIndex].PlayAnimation(clip, isLoop, speed);
// Play Playable Graph
if(!_playableGrap.IsPlaying())
{
_playableGrap.Play();
}
}
///
/// Stop animation.
///
/// Animator index.
/// layer index.
public void StopAnimation(int animationIndex, int layerIndex = 0)
{
// Fail silently...
if(layerIndex < 0 || layerIndex >= LayerCount)
{
return;
}
_motionLayers[layerIndex].StopAnimationClip();
}
///
/// Stop all animation.
///
public void StopAllAnimation()
{
for(var i = 0; i < LayerCount; ++i)
{
_motionLayers[i].StopAnimationClip();
}
}
///
/// Is playing animation.
///
/// True if the animation is playing.
public bool IsPlayingAnimation(int layerIndex = 0)
{
// Fail silently...
if(layerIndex < 0 || layerIndex >= LayerCount)
{
return false;
}
return !_motionLayers[layerIndex].IsFinished;
}
///
/// Set layer weight.
///
/// layer index.
/// Layer weight.
public void SetLayerWeight(int layerIndex, float weight)
{
// Fail silently...
if(layerIndex <= 0 || layerIndex >= LayerCount)
{
return;
}
_motionLayers[layerIndex].SetLayerWeight(weight);
_layerMixer.SetInputWeight(layerIndex, weight);
}
///
/// Set layer blend type is additive.
///
/// layer index.
/// Blend type is additive.
public void SetLayerAdditive(int layerIndex, bool isAdditive)
{
// Fail silently...
if(layerIndex <= 0 || layerIndex >= LayerCount)
{
return;
}
_layerMixer.SetLayerAdditive((uint)layerIndex, isAdditive);
}
///
/// Set animation speed.
///
/// layer index.
/// index of playing motion list.
/// Animation speed.
public void SetAnimationSpeed(int layerIndex, int index, float speed)
{
// Fail silently...
if(layerIndex < 0 || layerIndex >= LayerCount)
{
return;
}
_motionLayers[layerIndex].SetStateSpeed(index, speed);
}
///
/// Set animation is loop.
///
/// layer index.
/// Index of playing motion list.
/// State is loop.
public void SetAnimationIsLoop(int layerIndex, int index, bool isLoop)
{
// Fail silently...
if(layerIndex < 0 || layerIndex >= LayerCount)
{
return;
}
_motionLayers[layerIndex].SetStateIsLoop(index, isLoop);
}
///
/// Get cubism fade states.
///
public ICubismFadeState[] GetFadeStates()
{
if(_motionLayers == null)
{
LayerCount = (LayerCount < 1) ? 1 : LayerCount;
_motionLayers = new CubismMotionLayer[LayerCount];
_motionPriorities = new int[LayerCount];
}
return _motionLayers;
}
#endregion Function
#region Unity Events Handling
///
/// Called by Unity.
///
private void OnEnable()
{
_cubismFadeMotionList = GetComponent().CubismFadeMotionList;
// Fail silently...
if(_cubismFadeMotionList == null)
{
Debug.LogError("CubismMotionController : CubismFadeMotionList doesn't set in CubismFadeController.");
return;
}
// Get Animator.
var animator = GetComponent();
if (animator.runtimeAnimatorController != null)
{
Debug.LogWarning("Animator Controller was set in Animator component.");
return;
}
_isActive = true;
// Disable animator's playablegrap.
var graph = animator.playableGraph;
if(graph.IsValid())
{
graph.GetOutput(0).SetWeight(0);
}
// Create Playable Graph.
#if UNITY_2018_1_OR_NEWER
_playableGrap = PlayableGraph.Create("Playable Graph : " + this.FindCubismModel().name);
#else
_playableGrap = PlayableGraph.Create();
#endif
_playableGrap.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
// Create Playable Output.
_playableOutput = AnimationPlayableOutput.Create(_playableGrap, "Animation", animator);
_playableOutput.SetWeight(1);
// Create animation layer mixer.
_layerMixer = AnimationLayerMixerPlayable.Create(_playableGrap, LayerCount);
// Create cubism motion layers.
if(_motionLayers == null)
{
LayerCount = (LayerCount < 1) ? 1 : LayerCount;
_motionLayers = new CubismMotionLayer[LayerCount];
_motionPriorities = new int[LayerCount];
}
for(var i = 0; i < LayerCount; ++i)
{
_motionLayers[i] = CubismMotionLayer.CreateCubismMotionLayer(_playableGrap, _cubismFadeMotionList, i);
_motionLayers[i].AnimationEndHandler += OnAnimationEnd;
_layerMixer.ConnectInput(i, _motionLayers[i].PlayableOutput, 0);
_layerMixer.SetInputWeight(i, 1.0f);
}
// Set Playable Output.
_playableOutput.SetSourcePlayable(_layerMixer);
}
///
/// Called by Unity.
///
private void OnDisable()
{
// Destroy _playableGrap.
if(_playableGrap.IsValid())
{
_playableGrap.Destroy();
}
}
///
/// Called by Unity.
///
private void Update()
{
// Fail silently...
if(!_isActive)
{
return;
}
for( var i = 0; i < _motionLayers.Length; ++i)
{
_motionLayers[i].Update();
if (_motionLayers[i].IsFinished)
{
_motionPriorities[i] = 0;
}
}
}
#endregion Unity Events Handling
}
}