123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- // 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.Collections.Generic;
- using UnityEngine;
- namespace Animancer.Samples.Sprites
- {
- /// <summary>
- /// A more complex version of the <see cref="DirectionalBasics"/> which adds
- /// running and pushing animations as well as the ability to actually move around.
- /// </summary>
- ///
- /// <remarks>
- /// <strong>Sample:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/samples/sprites/character">
- /// Directional Character</see>
- /// </remarks>
- ///
- /// https://kybernetik.com.au/animancer/api/Animancer.Samples.Sprites/DirectionalCharacter
- ///
- [AddComponentMenu(Strings.SamplesMenuPrefix + "Sprites - Directional Character")]
- [AnimancerHelpUrl(typeof(DirectionalCharacter))]
- public class DirectionalCharacter : MonoBehaviour
- {
- /************************************************************************************************************************/
- #if UNITY_PHYSICS_2D
- /************************************************************************************************************************/
- [Header("Physics")]
- [SerializeField] private CapsuleCollider2D _Collider;
- [SerializeField] private Rigidbody2D _Rigidbody;
- [SerializeField, MetersPerSecond] private float _WalkSpeed = 1;
- [SerializeField, MetersPerSecond] private float _RunSpeed = 2;
- [Header("Animations")]
- [SerializeField] private AnimancerComponent _Animancer;
- [SerializeField] private DirectionalAnimationSet _Idle;
- [SerializeField] private DirectionalAnimationSet _Walk;
- [SerializeField] private DirectionalAnimationSet _Run;
- [SerializeField] private DirectionalAnimationSet _Push;
- [SerializeField] private Vector2 _Facing = Vector2.down;
- private Vector2 _Movement;
- private DirectionalAnimationSet _CurrentAnimationSet;
- private static readonly TimeSynchronizer<AnimationGroup>
- TimeSynchronizer = new();
- public enum AnimationGroup
- {
- Other,
- Movement,
- }
- /************************************************************************************************************************/
- protected virtual void Awake()
- {
- _CurrentAnimationSet = _Idle;
- }
- /************************************************************************************************************************/
- protected virtual void Update()
- {
- _Movement = SampleInput.WASD;
- if (_Movement != Vector2.zero)
- {
- // Snap the movement to the exact directions we have animations for.
- // When using DirectionalAnimationSets this means the character will only move up/right/down/left.
- // But DirectionalAnimationSet8s will allow diagonal movement as well.
- _Movement = _CurrentAnimationSet.Snap(_Movement);
- _Movement = Vector2.ClampMagnitude(_Movement, 1);
- _Facing = _Movement;
- UpdateMovementState();
- }
- else
- {
- Play(_Idle, AnimationGroup.Other);
- }
- }
- /************************************************************************************************************************/
- private void Play(DirectionalAnimationSet animations, AnimationGroup group)
- {
- // Store the current time.
- TimeSynchronizer.StoreTime(_Animancer);
- _CurrentAnimationSet = animations;
- _Animancer.Play(animations.GetClip(_Facing));
- // If the new animation is in the synchronization group, give it the same time the previous animation had.
- TimeSynchronizer.SyncTime(_Animancer, group);
- }
- /************************************************************************************************************************/
- // Pre-allocate a list of contacts so Unity doesn't need to allocate a new one every time.
- // It's static because every character can use the same list one at a time.
- private static readonly List<ContactPoint2D> Contacts = new();
- private void UpdateMovementState()
- {
- int contactCount = _Collider.GetContacts(Contacts);
- for (int i = 0; i < contactCount; i++)
- {
- // If we're moving directly towards an object (or within 30 degrees of it), we are pushing it.
- if (Vector2.Angle(Contacts[i].normal, _Movement) > 180 - 30)
- {
- Play(_Push, AnimationGroup.Movement);
- return;
- }
- }
- DirectionalAnimationSet animations = SampleInput.LeftShiftHold ? _Run : _Walk;
- Play(animations, AnimationGroup.Movement);
- }
- /************************************************************************************************************************/
- protected virtual void FixedUpdate()
- {
- // Determine the desired speed based on the current animation.
- float speed = _CurrentAnimationSet == _Run ? _RunSpeed : _WalkSpeed;
- _Rigidbody.velocity = _Movement * speed;
- }
- /************************************************************************************************************************/
- #if UNITY_EDITOR
- /************************************************************************************************************************/
- /// <summary>[Editor-Only]
- /// Sets the character's starting sprite in Edit Mode so you can see it while working in the scene.
- /// </summary>
- /// <remarks>Called in Edit Mode whenever this script is loaded or a value is changed in the Inspector.</remarks>
- protected virtual void OnValidate()
- {
- if (_Idle != null)
- _Idle.GetClip(_Facing).EditModePlay(_Animancer);
- }
- /************************************************************************************************************************/
- #endif
- /************************************************************************************************************************/
- #else
- /************************************************************************************************************************/
- protected virtual void Awake()
- {
- SampleReadMe.LogMissingPhysics3DModuleError(this);
- }
- /************************************************************************************************************************/
- #endif
- /************************************************************************************************************************/
- }
- }
|