Это третье руководство по созданию механики Культа Агнца в Unity. Если вы хотите продолжить серию, вот что мы делали до сих пор:

  • Ground Zero: автомат начальных состояний в 2D-платформере
  • Часть 1: Движение в 3D и сопровождение камеры
  • Часть 2: Прокачка персонажа
  • Часть 3: Анимируем как программист
  • Часть 4: Состояние комбо атаки часть 1

Сначала нам нужно изменить некоторые вещи, которые мы сделали, чтобы решить проблему с состоянием Roll. Проблема прямо сейчас заключается в том, что если мы нажмем кнопку поворота в режиме ожидания и переместимся через несколько секунд, вместо обычного движения он выполнит перекат. Это не то, чего мы хотим. Чтобы исправить это, сначала мы идем в класс PlayerInput и добавляем новое событие. Мы можем назвать это RollCancelledEvent:

namespace Player.Input
{
    public class PlayerInput : MonoBehaviour, GameControls.IPlayerActions
    {
        ...
        public event UnityAction RollCancelledEvent = delegate { };

        public void OnRoll(InputAction.CallbackContext context)
        {
            if (context.performed)
            {
                RollEvent?.Invoke();
            } 
            if (context.canceled)
            {
                RollCancelledEvent?.Invoke();
            }
        }
    }
}

После этого нам нужно перейти к классу PlayerStateMachine:

namespace Player
{
    public class PlayerStateMachine : StateMachine<PlayerStateMachine>
    {
        ...
        
        public bool RollPressed => _rollPressed;
        private bool _rollPressed;        
        
        private void OnEnable()
        {
            ...
            _playerInput.RollCancelledEvent += HandleCancelledRoll;
        }

        private void OnDisable()
        {
            ...
            _playerInput.RollCancelledEvent -= HandleCancelledRoll;
        }

        private void HandleCancelledRoll()
        {
            _rollPressed = false;
        }
        ...
    }
}

В PlayerRollState мы можем удалить строку 24:

// instantly set this to false so there's no double rolling
parent.RollPressed = false;

Нет, мы можем продолжать.

Добавление анимации

Для этого урока я нарисовал эскиз своего плеера и импортировал файл .psb в Unity. Если хотите, можете найти спрайты и анимации в этом .zip файле. Конечно, вы можете использовать любые спрайты и анимации. Убедитесь, что вы импортировали 2D-пакеты из диспетчера пакетов.

После импорта их в редактор перетащите префаб characters_spritesheet в сцену, распакуйте префаб и замените дочерний элемент Sprite вашего игрока этим игровым объектом Player. Также добавьте компонент Animator и назначьте контроллер Animator.

На самом Аниматоре нам особо ничего делать не нужно, просто перетащите на него анимацию.

Нам не нужны переходы, так как мы анимируем как программист.

Давайте напишем сценарии

Мы знаем, что Unity основан на скриптах. Каждый сценарий должен (надеюсь) отвечать за одну вещь. Здесь мы собираемся использовать немного другой подход. Сначала мы создаем новый чистый класс C#, который не наследуется от MonoBehaviour. Назовем его PlayerAnimations. Этот класс будет отвечать за все, что связано с анимацией.

using UnityEngine;

namespace Player
{
    public class PlayerAnimations
    {
        private Animator _animator;

        // this needs to match the names on the Animator view
        private int PLAYER_IDLE = Animator.StringToHash("Idle");
        private int PLAYER_MOVE = Animator.StringToHash("Move");
        private int PLAYER_ROLL = Animator.StringToHash("Roll");

        private float _transitionDuration = .1f;

        public PlayerAnimations(Animator animator)
        {
            _animator = animator;
        }

        public void PlayIdle()
        {
            _animator.CrossFade(PLAYER_IDLE, _transitionDuration);
        }

        public void PlayMove()
        {
            _animator.CrossFade(PLAYER_MOVE, _transitionDuration);
        }

        public void PlayRoll()
        {
            _animator.CrossFade(PLAYER_ROLL, _transitionDuration);
        }
    }
}

Теперь нам нужно добавить ссылку на этот новый класс в скрипт PlayerStateMachine и инициализировать его:

namespace Player
{
    public class PlayerStateMachine : StateMachine<PlayerStateMachine>
    {
        ...
        [SerializeField] private Animator _animator;        
        public PlayerAnimations Animations { get; private set; }        

        protected override void Awake()
        {
            base.Awake();
            Animations = new PlayerAnimations(_animator);
        }        
    }
}

Это позволит нам вызывать аниматора из наших состояний следующим образом:

namespace Player.States
{    
    public class PlayerMove3DState : State<PlayerStateMachine>
    {       
        ...
        public override void Enter(PlayerStateMachine parent)
        {
            base.Enter(parent);
            parent.Animations.PlayMove();
        }
        ...
    }
}
namespace Player.States
{
    public class PlayerRollState : State<PlayerStateMachine>
    {        
        public override void Enter(PlayerStateMachine parent)
        {
            base.Enter(parent);
            parent.Animations.PlayRoll();
            ...
        }
        ...        
    }
}
namespace Player.States
{
    public class PlayerIdleState : State<PlayerStateMachine>
    {
        public override void Enter(PlayerStateMachine parent)
        {
            base.Enter(parent);
            ...
            parent.Animations.PlayIdle();
        }
        ...
    }
}

Не забудьте назначить ссылку на аниматор компоненту PlayerStateMachine в окне инспектора проигрывателя.

Нажмите кнопку воспроизведения, и теперь вы увидите анимацию!

Увидимся в следующем!

Авеста: Генератор процедурных тайлмап

Прата: Диалоги за секунды

Pointo: RTS Blueprints

Первоначально опубликовано на https://itch.io.