// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using Animancer.Units; using System; using UnityEngine; namespace Animancer.Samples.AnimatorControllers.GameKit { /// /// A which keeps the character standing still and occasionally plays alternate /// animations if it remains active for long enough. /// /// /// /// Sample: /// /// 3D Game Kit/Idle /// /// /// https://kybernetik.com.au/animancer/api/Animancer.Samples.AnimatorControllers.GameKit/IdleState /// [AddComponentMenu(Strings.SamplesMenuPrefix + "Game Kit - Idle State")] [AnimancerHelpUrl(typeof(IdleState))] public class IdleState : CharacterState { /************************************************************************************************************************/ [SerializeField] private ClipTransition _MainAnimation; [SerializeField, Seconds] private float _FirstRandomizeDelay = 5; [SerializeField, Seconds] private float _MinRandomizeInterval = 0; [SerializeField, Seconds] private float _MaxRandomizeInterval = 20; [SerializeField] private ClipTransition[] _RandomAnimations; private float _RandomizeTime; // _RandomizeDelay was originally handled by the PlayerController (Idle Timeout). // The min and max interval were handled by the RandomStateSMB on the Idle state in IdleSM. /************************************************************************************************************************/ protected virtual void Awake() { Action onEnd = PlayMainAnimation; for (int i = 0; i < _RandomAnimations.Length; i++) { _RandomAnimations[i].Events.OnEnd = onEnd; // We could just do `...OnEnd = PlayMainAnimation` instead of declaring the delegate first, but that // assignment is actually shorthand for `new Action(PlayMainAnimation)` which would create a new // delegate object for each animation. This way all animations just share the same object. } } /************************************************************************************************************************/ public override bool CanEnterState => Character.Movement.IsGrounded; /************************************************************************************************************************/ protected virtual void OnEnable() { PlayMainAnimation(); _RandomizeTime += _FirstRandomizeDelay; } private void PlayMainAnimation() { _RandomizeTime = UnityEngine.Random.Range(_MinRandomizeInterval, _MaxRandomizeInterval); Character.Animancer.Play(_MainAnimation); } /************************************************************************************************************************/ protected virtual void FixedUpdate() { if (Character.CheckMotionState()) return; Character.Movement.UpdateSpeedControl(); // We use time where Mecanim used normalized time because choosing a number of seconds is much simpler than // finding out how long the animation is and working with multiples of that value. AnimancerState state = Character.Animancer.States.Current; if (state == _MainAnimation.State && state.Time >= _RandomizeTime) { PlayRandomAnimation(); } } /************************************************************************************************************************/ private void PlayRandomAnimation() { int index = UnityEngine.Random.Range(0, _RandomAnimations.Length); ClipTransition animation = _RandomAnimations[index]; AnimancerState state = Character.Animancer.Play(animation); state.FadeGroup.SetEasing(Easing.Sine.InOut); } /************************************************************************************************************************/ } }