| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 | /** * 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.Motion;using UnityEngine;namespace Live2D.Cubism.Framework.MotionFade{    /// <summary>    /// Cubism fade controller.    /// </summary>    [RequireComponent(typeof(Animator))]    public class CubismFadeController : MonoBehaviour, ICubismUpdatable    {        #region Variable        /// <summary>        /// Cubism fade motion list.        /// </summary>        [SerializeField]        public CubismFadeMotionList CubismFadeMotionList;        /// <summary>        /// Parameters cache.        /// </summary>        private CubismParameter[] DestinationParameters { get; set; }        /// <summary>        /// Parts cache.        /// </summary>        private CubismPart[] DestinationParts { get; set; }        /// <summary>        /// Model has motion controller component.        /// </summary>        private CubismMotionController _motionController;        /// <summary>        /// Model has cubism update controller component.        /// </summary>        [HideInInspector]        public bool HasUpdateController { get; set; }        /// <summary>        /// Fade state machine behavior set in the animator.        /// </summary>        private ICubismFadeState[] _fadeStates;        /// <summary>        /// Model has animator component.        /// </summary>        private Animator _animator;        /// <summary>        /// Restore parameter value.        /// </summary>        private CubismParameterStore _parameterStore;        /// <summary>        /// Fading flags for each layer.        /// </summary>        private bool[] _isFading;        #endregion        #region Function        /// <summary>        /// Refreshes the controller. Call this method after adding and/or removing <see cref="CubismFadeParameter"/>s.        /// </summary>        public void Refresh()        {            _animator = GetComponent<Animator>();            // Fail silently...            if (_animator == null)            {                return;            }            DestinationParameters = this.FindCubismModel().Parameters;            DestinationParts = this.FindCubismModel().Parts;            _motionController = GetComponent<CubismMotionController>();            _parameterStore = GetComponent<CubismParameterStore>();            // Get cubism update controller.            HasUpdateController = (GetComponent<CubismUpdateController>() != null);            _fadeStates = (ICubismFadeState[])_animator.GetBehaviours<CubismFadeStateObserver>();            if ((_fadeStates == null || _fadeStates.Length == 0) && _motionController != null)            {                _fadeStates = _motionController.GetFadeStates();            }            if (_fadeStates == null)            {                return;            }            _isFading = new bool[_fadeStates.Length];        }        /// <summary>        /// Called by cubism update controller. Order to invoke OnLateUpdate.        /// </summary>        public int ExecutionOrder        {            get { return CubismUpdateExecutionOrder.CubismFadeController; }        }        /// <summary>        /// Called by cubism update controller. Needs to invoke OnLateUpdate on Editing.        /// </summary>        public bool NeedsUpdateOnEditing        {            get { return false; }        }        /// <summary>        /// Called by cubism update controller. Updates controller.        /// </summary>        /// <remarks>        /// Make sure this method is called after any animations are evaluated.        /// </remarks>        public void OnLateUpdate()        {            // Fail silently.            if (!enabled || _fadeStates == null || _parameterStore == null               || DestinationParameters == null || DestinationParts == null)            {                return;            }            var time = Time.time;            for (var i = 0; i < _fadeStates.Length; ++i)            {                _isFading[i] = false;                var playingMotions = _fadeStates[i].GetPlayingMotions();                if (playingMotions == null || playingMotions.Count <= 1)                {                    continue;                }                var latestPlayingMotion = playingMotions[playingMotions.Count - 1];                var playingMotionData = latestPlayingMotion.Motion;                var elapsedTime = time - latestPlayingMotion.StartTime;                for (var j = 0; j < playingMotionData.ParameterFadeInTimes.Length; j++)                {                    if ((elapsedTime <= playingMotionData.FadeInTime) ||                        ((0 <= playingMotionData.ParameterFadeInTimes[j]) &&                          (elapsedTime <= playingMotionData.ParameterFadeInTimes[j])))                    {                        _isFading[i] = true;                        break;                    }                }            }            var isFadingAllFinished = true;            for (var i = 0; i < _fadeStates.Length; ++i)            {                var playingMotions = _fadeStates[i].GetPlayingMotions();                var playingMotionCount = playingMotions.Count - 1;                if (_isFading[i])                {                    isFadingAllFinished = false;                    continue;                }                for (var j = playingMotionCount; j >= 0; --j)                {                    if (playingMotions.Count <= 1)                    {                        break;                    }                    var playingMotion = playingMotions[j];                    if (time <= playingMotion.EndTime)                    {                        continue;                    }                    // If fade-in has been completed, delete the motion that has been played back.                    _fadeStates[i].StopAnimation(j);                }            }            if (isFadingAllFinished)            {                return;            }            _parameterStore.RestoreParameters();            // Update sources and destinations.            for (var i = 0; i < _fadeStates.Length; ++i)            {                if (!_isFading[i])                {                    continue;                }                UpdateFade(_fadeStates[i]);            }        }        /// <summary>        /// Update motion fade.        /// </summary>        /// <param name="fadeState">Fade state observer.</param>        private void UpdateFade(ICubismFadeState fadeState)        {            var playingMotions = fadeState.GetPlayingMotions();            if (playingMotions == null)            {                // Do not process if there is only one motion, if it does not switch.                return;            }            // Weight set for the layer being processed.            // (In the case of the layer located at the top, it is forced to 1.)            var layerWeight = fadeState.GetLayerWeight();            var time = Time.time;            // Set playing motions end time.            if ((playingMotions.Count > 0) && (playingMotions[playingMotions.Count - 1].Motion != null) && (playingMotions[playingMotions.Count - 1].IsLooping))            {                var motion = playingMotions[playingMotions.Count - 1];                var newEndTime = time + motion.Motion.FadeOutTime;                motion.EndTime = newEndTime;                while (true)                {                    if ((motion.StartTime + motion.Motion.MotionLength) >= time)                    {                        break;                    }                    motion.StartTime += motion.Motion.MotionLength;                }                playingMotions[playingMotions.Count - 1] = motion;            }            // Calculate MotionFade.            for (var i = 0; i < playingMotions.Count; i++)            {                var playingMotion = playingMotions[i];                var fadeMotion = playingMotion.Motion;                if (fadeMotion == null)                {                    continue;                }                var elapsedTime = time - playingMotion.StartTime;                var endTime = playingMotion.EndTime - elapsedTime;                var fadeInTime = fadeMotion.FadeInTime;                var fadeOutTime = fadeMotion.FadeOutTime;                var fadeInWeight = (fadeInTime <= 0.0f)                    ? 1.0f                    : CubismFadeMath.GetEasingSine(elapsedTime / fadeInTime);                var fadeOutWeight = (fadeOutTime <= 0.0f)                    ? 1.0f                    : CubismFadeMath.GetEasingSine((playingMotion.EndTime - Time.time) / fadeOutTime);                playingMotions[i] = playingMotion;                var motionWeight = (i == 0)                    ? (fadeInWeight * fadeOutWeight)                    : (fadeInWeight * fadeOutWeight * layerWeight);                // Apply to parameter values                for (var j = 0; j < DestinationParameters.Length; ++j)                {                    var index = -1;                    for (var k = 0; k < fadeMotion.ParameterIds.Length; ++k)                    {                        if (fadeMotion.ParameterIds[k] != DestinationParameters[j].Id)                        {                            continue;                        }                        index = k;                        break;                    }                    if (index < 0)                    {                        // There is not target ID curve in motion.                        continue;                    }                    DestinationParameters[j].Value = Evaluate(                            fadeMotion.ParameterCurves[index], elapsedTime, endTime,                            fadeInWeight, fadeOutWeight,                            fadeMotion.ParameterFadeInTimes[index], fadeMotion.ParameterFadeOutTimes[index],                            motionWeight, DestinationParameters[j].Value);                }                // Apply to part opacities                for (var j = 0; j < DestinationParts.Length; ++j)                {                    var index = -1;                    for (var k = 0; k < fadeMotion.ParameterIds.Length; ++k)                    {                        if (fadeMotion.ParameterIds[k] != DestinationParts[j].Id)                        {                            continue;                        }                        index = k;                        break;                    }                    if (index < 0)                    {                        // There is not target ID curve in motion.                        continue;                    }                    DestinationParts[j].Opacity = Evaluate(                            fadeMotion.ParameterCurves[index], elapsedTime, endTime,                            fadeInWeight, fadeOutWeight,                            fadeMotion.ParameterFadeInTimes[index], fadeMotion.ParameterFadeOutTimes[index],                            motionWeight, DestinationParts[j].Opacity);                }            }        }        /// <summary>        /// Evaluate fade curve.        /// </summary>        /// <param name="curve">Curves to be evaluated.</param>        /// <param name="elapsedTime">Elapsed Time.</param>        /// <param name="endTime">Fading end time.</param>        /// <param name="fadeInTime">Fade in time.</param>        /// <param name="fadeOutTime">Fade out time.</param>        /// <param name="parameterFadeInTime">Fade in time parameter.</param>        /// <param name="parameterFadeOutTime">Fade out time parameter.</param>        /// <param name="motionWeight">Motion weight.</param>        /// <param name="currentValue">Current value with weight applied.</param>        public float Evaluate(            AnimationCurve curve, float elapsedTime, float endTime,            float fadeInTime, float fadeOutTime,            float parameterFadeInTime, float parameterFadeOutTime,            float motionWeight, float currentValue)        {            if (curve.length <= 0)            {                return currentValue;            }            // Motion fade.            if (parameterFadeInTime < 0.0f &&                parameterFadeOutTime < 0.0f)            {                return currentValue + (curve.Evaluate(elapsedTime) - currentValue) * motionWeight;            }            // Parameter fade.            float fadeInWeight, fadeOutWeight;            if (parameterFadeInTime < 0.0f)            {                fadeInWeight = fadeInTime;            }            else            {                fadeInWeight = (parameterFadeInTime < float.Epsilon)                    ? 1.0f                    : CubismFadeMath.GetEasingSine(elapsedTime / parameterFadeInTime);            }            if (parameterFadeOutTime < 0.0f)            {                fadeOutWeight = fadeOutTime;            }            else            {                fadeOutWeight = (parameterFadeOutTime < float.Epsilon)                    ? 1.0f                    : CubismFadeMath.GetEasingSine(endTime / parameterFadeOutTime);            }            var parameterWeight = fadeInWeight * fadeOutWeight;            return currentValue + (curve.Evaluate(elapsedTime) - currentValue) * parameterWeight;        }        #endregion        #region Unity Events Handling        /// <summary>        /// Initializes instance.        /// </summary>        private void OnEnable()        {            // Initialize cache.            Refresh();        }        /// <summary>        /// Called by Unity.        /// </summary>        private void LateUpdate()        {            if (!HasUpdateController)            {                OnLateUpdate();            }        }        #endregion    }}
 |