← Назад к вопросам

Можно ли добавлять логику к состояниям аниматора? Что такое StateMachineBehaviour?

1.6 Junior🔥 121 комментариев
#Другое

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Добавление логики к состояниям аниматора

Да, в Unity можно и нужно добавлять логику к состояниям аниматора, и для этого существует специальный класс StateMachineBehaviour. Это ключевой компонент, который позволяет привязывать пользовательский C# код к конкретным состояниям или под-состояниям (под-машинам) внутри Animator Controller. Это обеспечивает глубокую интеграцию между анимацией и геймплейным кодом, сохраняя при этом чистоту архитектуры — логика анимации инкапсулируется в самом аниматоре, а не размазывается по основным скриптам объекта.

Что такое StateMachineBehaviour?

StateMachineBehaviour — это абстрактный класс в пространстве имён UnityEngine.Animator, который можно наследовать для создания собственных поведений, прикрепляемых к состояниям Animator Controller прямо в редакторе Unity. Он предоставляет виртуальные методы (коллбэки), автоматически вызываемые системой анимации в ключевые моменты жизненного цикла состояния.

Основные коллбэки StateMachineBehaviour:

  • OnStateEnter(): Вызывается при первом кадре, когда аниматор начинает воспроизводить это состояние.
  • OnStateUpdate(): Вызывается на каждом кадре, пока активно это состояние (кроме первого и последнего кадра).
  • OnStateExit(): Вызывается при последнем кадре состояния, перед переходом к следующему.
  • OnStateMove() и OnStateIK(): Специализированные методы для обработки смещения корневой анимации (Root Motion) и инверсной кинематики (Inverse Kinematics) соответственно.

Практический пример использования

Представим, что у нас есть состояние атаки AttackState. Мы хотим в его начале включить коллайдер оружия, а в конце — выключить, а также наносить урон в определённый момент.

  1. Создаём скрипт поведения состояния:
using UnityEngine;

public class AttackStateBehaviour : StateMachineBehaviour
{
    // Ссылка на коллайдер оружия. Можно задать через код или SerializeField.
    [SerializeField] private Collider weaponCollider;
    // Ссылка на компонент, наносящий урон.
    private IDamageDealer damageDealer;

    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // Получаем ссылки на необходимые компоненты. Ищем на том же GameObject, что и Animator.
        if (weaponCollider == null)
            weaponCollider = animator.GetComponentInChildren<WeaponCollider>();
        if (damageDealer == null)
            damageDealer = animator.GetComponent<IDamageDealer>();

        // Включаем коллайдер оружия в начале анимации атаки.
        if (weaponCollider != null)
            weaponCollider.enabled = true;

        Debug.Log("Атака началась!");
    }

    // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // Предположим, урон нужно нанести в середине анимации (normalizedTime от 0 до 1).
        // normalizedTime — это прогресс воспроизведения текущей анимации состояния.
        if (damageDealer != null && stateInfo.normalizedTime > 0.4f && stateInfo.normalizedTime < 0.6f)
        {
            damageDealer.TryDealDamage();
        }
    }

    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // Гарантированно выключаем коллайдер при выходе из состояния.
        if (weaponCollider != null)
            weaponCollider.enabled = false;

        // Сбрасываем триггер, чтобы он не "завис" и не вызвал мгновенный повторный переход.
        animator.ResetTrigger("Attack");

        Debug.Log("Атака завершена.");
    }
}
  1. Прикрепляем скрипт в редакторе Unity:
    *   Откройте окно **Animator**.
    *   Выберите нужное состояние (например, `AttackState`).
    *   В инспекторе нажмите **"Add Behaviour"** и выберите ваш скрипт (`AttackStateBehaviour`).
    *   Теперь при входе, обновлении и выходе из этого состояния будут вызываться соответствующие методы.

Ключевые преимущества и сценарии использования:

  • Инкапсуляция логики состояния: Весь код, отвечающий за конкретную анимацию (звуки, эффекты, события геймплея), находится в одном месте.
  • Чистота основного кода: Монобеhaviour EnemyController или PlayerController освобождается от прямого управления деталями анимаций.
  • Реакция на события анимации: Идеально для синхронизации действий с временной шкалой анимации (например, удар ногой в определенный кадр).
  • Управление параметрами аниматора: Поведение может читать и устанавливать параметры (animator.SetBool(...)) для управления переходами.
  • Работа с под-машинами состояний (Sub-State Machines): StateMachineBehaviour можно добавить и к целой под-машине, что полезно для группировки логики (например, для всех состояний, связанных с перемещением).

Важные замечания:

  • Производительность: Коллбэки вызываются очень часто. Код внутри них должен быть максимально оптимизирован.
  • Контекст: Поведение знает только о Animator и AnimatorStateInfo. Для доступа к другим компонентам GameObject (к которому прикреплён Animator) нужно использовать методы типа animator.GetComponent<>() или кэшировать ссылки.
  • Порядок исполнения: Несколько StateMachineBehaviour, прикреплённых к одному состоянию, выполняются в порядке их добавления.

Таким образом, StateMachineBehaviour — это мощный и элегантный инструмент для создания тесной, организованной связи между визуальной анимацией и игровой логикой, являясь стандартным и рекомендуемым способом "оживления" состояний аниматора в Unity.

Можно ли добавлять логику к состояниям аниматора? Что такое StateMachineBehaviour? | PrepBro