// Animancer // Copyright 2020 Kybernetik // #pragma warning disable CS0618 // Type or member is obsolete (for ControllerStates in Animancer Lite). #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using System; using System.Reflection; using UnityEngine; using UnityEngine.Events; namespace Animancer.Examples.AnimatorControllers.GameKit { /// /// A which moves the creature according to their /// . /// [AddComponentMenu(Strings.MenuPrefix + "Examples/Game Kit - Locomotion State")] [HelpURL(Strings.APIDocumentationURL + ".Examples.AnimatorControllers.GameKit/LocomotionState")] public sealed class LocomotionState : CreatureState { /************************************************************************************************************************/ [SerializeField] private Float2ControllerState.Transition _LocomotionBlendTree; [SerializeField] private ClipState.Transition _QuickTurnLeft; [SerializeField] private ClipState.Transition _QuickTurnRight; [SerializeField] private float _QuickTurnMoveSpeed = 2; [SerializeField] private float _QuickTurnAngle = 145; /************************************************************************************************************************/ private void Awake() { _QuickTurnLeft.Events.Sequence.OnEnd = _QuickTurnRight.Events.Sequence.OnEnd = () => Creature.Animancer.Play(_LocomotionBlendTree); } /************************************************************************************************************************/ public override bool CanEnterState(CreatureState previousState) { return Creature.CharacterController.isGrounded; } /************************************************************************************************************************/ private void OnEnable() { Creature.Animancer.Play(_LocomotionBlendTree); } /************************************************************************************************************************/ private void FixedUpdate() { if (Creature.CheckMotionState()) return; Creature.UpdateSpeedControl(); _LocomotionBlendTree.State.ParameterX = Creature.ForwardSpeed; // Or we could set the parameter manually: //_LocomotionBlendTree.State.Playable.SetFloat("Speed", Creature.ForwardSpeed); UpdateRotation(); UpdateAudio(); } /************************************************************************************************************************/ private void UpdateRotation() { // If the default locomotion state is not active we must be performing a quick turn. // Those animations use root motion to perform the turn so we don't want any scripted rotation during them. if (!_LocomotionBlendTree.State.IsActive) return; float currentAngle, targetAngle; if (!Creature.GetTurnAngles(Creature.Brain.Movement, out currentAngle, out targetAngle)) return; // Check if we should play a quick turn animation: // If we are moving fast enough. if (Creature.ForwardSpeed > _QuickTurnMoveSpeed) { // And turning sharp enough. var deltaAngle = Mathf.DeltaAngle(currentAngle, targetAngle); if (Mathf.Abs(deltaAngle) > _QuickTurnAngle) { // Determine which way we are turning. var turn = deltaAngle < 0 ? _QuickTurnLeft : _QuickTurnRight; // Make sure the desired turn is not already active so we don't keep using it repeatedly. if (turn.State == null || turn.State.Weight == 0) { Creature.Animancer.Play(turn); // Now that we are quick turning, we don't want to apply the scripted turning below. return; } } } Creature.TurnTowards(currentAngle, targetAngle, Creature.CurrentTurnSpeed); } /************************************************************************************************************************/ [SerializeField] private UnityEvent _PlayFootstepAudio;// See the Read Me. private bool _CanPlayAudio; private bool _IsPlayingAudio; /// /// This is the same logic used for locomotion audio in the original PlayerController. /// private void UpdateAudio() { var footFallCurve = _LocomotionBlendTree.State.ParameterY; if (footFallCurve > 0.01f && !_IsPlayingAudio && _CanPlayAudio) { _IsPlayingAudio = true; _CanPlayAudio = false; // The full 3D Game Kit has different footstep sounds depending on the ground material and your speed // so it calls RandomAudioPlayer.PlayRandomClip with those parameters: //_FootstepAudio.PlayRandomClip(Creature.GroundMaterial, Creature.ForwardSpeed < 4 ? 0 : 1); // Unfortunately UnityEvents cannot call methods with multiple parameters (UltEvents can), but it does // not realy matter because the 3D Game Kit Lite only has one set of footstep sounds anyway. _PlayFootstepAudio.Invoke(); } else if (_IsPlayingAudio) { _IsPlayingAudio = false; } else if (footFallCurve < 0.01f && !_CanPlayAudio) { _CanPlayAudio = true; } } /************************************************************************************************************************/ } }