// Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik // using Animancer.Examples.StateMachines; using Animancer.Units; using UnityEngine; namespace Animancer.Examples.AnimatorControllers { /// <summary> /// A <see cref="CharacterState"/> which moves the character according to their /// <see cref="CharacterParameters.MovementDirection"/>. /// </summary> /// /// <remarks> /// This class is very similar to <see cref="MoveState"/>, except that it manages a /// Blend Tree instead of a Mixer. /// </remarks> /// /// <example><see href="https://kybernetik.com.au/animancer/docs/examples/animator-controllers/character">Hybrid Character</see></example> /// /// https://kybernetik.com.au/animancer/api/Animancer.Examples.AnimatorControllers/HybridMoveState /// [AddComponentMenu(Strings.ExamplesMenuPrefix + "Hybrid - Move State")] [HelpURL(Strings.DocsURLs.ExampleAPIDocumentation + nameof(AnimatorControllers) + "/" + nameof(HybridMoveState))] public sealed class HybridMoveState : CharacterState { /************************************************************************************************************************/ [SerializeField, DegreesPerSecond] private float _TurnSpeed = 360; [SerializeField] private float _ParameterFadeSpeed = 2; private float _MoveBlend; /************************************************************************************************************************/ /// <summary> /// Normally the <see cref="Character"/> class would have a reference to the specific type of /// <see cref="AnimancerComponent"/> we want, but for the sake of reusing code from the earlier example, we /// just use a type cast here. /// </summary> private HybridAnimancerComponent HybridAnimancer => (HybridAnimancerComponent)Character.Animancer; /************************************************************************************************************************/ private void OnEnable() { HybridAnimancer.PlayController(); HybridAnimancer.SetBool(Animations.IsMoving, true); _MoveBlend = Character.Parameters.WantsToRun ? 1 : 0; } /************************************************************************************************************************/ private void Update() { UpdateAnimation(); UpdateTurning(); } /************************************************************************************************************************/ /// <summary>This method is similar to <see cref="MoveState.UpdateAnimation"/>.</summary> private void UpdateAnimation() { var target = Character.Parameters.WantsToRun ? 1 : 0; _MoveBlend = Mathf.MoveTowards( _MoveBlend, target, _ParameterFadeSpeed * Time.deltaTime); HybridAnimancer.SetFloat(Animations.MoveBlend, _MoveBlend); } /************************************************************************************************************************/ /// <remarks>This method is identical to <see cref="MoveState.UpdateTurning"/>.</remarks> private void UpdateTurning() { var movement = Character.Parameters.MovementDirection; if (movement == default) return; var targetAngle = Mathf.Atan2(movement.x, movement.z) * Mathf.Rad2Deg; var turnDelta = _TurnSpeed * Time.deltaTime; var transform = Character.Animancer.transform; var eulerAngles = transform.eulerAngles; eulerAngles.y = Mathf.MoveTowardsAngle(eulerAngles.y, targetAngle, turnDelta); transform.eulerAngles = eulerAngles; } /************************************************************************************************************************/ } }