/** * 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 } }