Примеры кода

Пример 1: Основы Animator

Работа с компонентом Animator и его параметрами:

using UnityEngine;

public class AnimatorExample : MonoBehaviour
{
    [Header("Animator")]
    public Animator animator;
    
    [Header("Parameters")]
    public string walkParam = "IsWalking";
    public string speedParam = "Speed";
    public string jumpTrigger = "Jump";

    void Start()
    {
        animator = GetComponent<Animator>();
        ShowAnimatorInfo();
    }

    void Update()
    {
        HandleInput();
        UpdateParameters();
    }

    void HandleInput()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            SetWalking(true);
        }
        if (Input.GetKeyUp(KeyCode.W))
        {
            SetWalking(false);
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            TriggerJump();
        }
    }

    void UpdateParameters()
    {
        float speed = Input.GetAxis("Vertical");
        animator.SetFloat(speedParam, Mathf.Abs(speed));
        
        bool isMoving = Mathf.Abs(speed) > 0.1f;
        animator.SetBool(walkParam, isMoving);
    }

    void SetWalking(bool walking)
    {
        animator.SetBool(walkParam, walking);
        Debug.Log($"Ходьба: {walking}");
    }

    void TriggerJump()
    {
        animator.SetTrigger(jumpTrigger);
        Debug.Log("Прыжок!");
    }

    void ShowAnimatorInfo()
    {
        Debug.Log($"Controller: {animator.runtimeAnimatorController?.name}");
        Debug.Log($"Слоев: {animator.layerCount}");
        Debug.Log($"Параметров: {animator.parameterCount}");
    }

    bool IsAnimationFinished(string animName)
    {
        AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0);
        return state.IsName(animName) && 
               state.normalizedTime >= 1.0f && 
               !animator.IsInTransition(0);
    }
}

Пример 2: Управление состояниями

Работа с состояниями анимации:

using UnityEngine;

public class AnimationStates : MonoBehaviour
{
    [Header("States")]
    public Animator animator;
    public string currentState = "Idle";
    
    private float stateTimer;

    void Start()
    {
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        HandleStateInput();
        UpdateTimer();
        CheckStateCompletion();
    }

    void HandleStateInput()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
            ChangeState("Idle");
        if (Input.GetKeyDown(KeyCode.Alpha2))
            ChangeState("Walk");
        if (Input.GetKeyDown(KeyCode.Alpha3))
            ChangeState("Run");
        if (Input.GetKeyDown(KeyCode.Alpha4))
            ChangeState("Jump");
    }

    void ChangeState(string newState)
    {
        if (currentState == newState) return;

        OnStateExit(currentState);
        string oldState = currentState;
        currentState = newState;
        stateTimer = 0f;
        OnStateEnter(newState);
        
        Debug.Log($"Переход: {oldState} → {newState}");
    }

    void OnStateEnter(string state)
    {
        switch (state)
        {
            case "Idle":
                animator.SetBool("IsWalking", false);
                animator.SetBool("IsRunning", false);
                break;
            case "Walk":
                animator.SetBool("IsWalking", true);
                animator.SetFloat("Speed", 1f);
                break;
            case "Run":
                animator.SetBool("IsRunning", true);
                animator.SetFloat("Speed", 2f);
                break;
            case "Jump":
                animator.SetTrigger("Jump");
                break;
        }
    }

    void OnStateExit(string state)
    {
        // Очистка при выходе из состояния
    }

    void UpdateTimer()
    {
        stateTimer += Time.deltaTime;
    }

    void CheckStateCompletion()
    {
        if (currentState == "Jump")
        {
            AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0);
            if (state.normalizedTime >= 1.0f && !animator.IsInTransition(0))
            {
                ChangeState("Idle");
            }
        }
    }

    void OnGUI()
    {
        GUI.Box(new Rect(10, 10, 200, 100), "Состояния");
        GUI.Label(new Rect(20, 30, 180, 20), $"Текущее: {currentState}");
        GUI.Label(new Rect(20, 50, 180, 20), $"Время: {stateTimer:F1}с");
    }
}

Пример 3: Blend Tree

Работа с Blend Tree для плавных переходов:

using UnityEngine;

public class BlendTreeController : MonoBehaviour
{
    [Header("Blend Tree")]
    public Animator animator;
    public float moveSpeed = 5f;
    
    private Vector2 inputVector;
    private Vector2 smoothInput;

    void Start()
    {
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        HandleInput();
        UpdateBlendTree();
        HandleMovement();
    }

    void HandleInput()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        
        inputVector = new Vector2(horizontal, vertical);
        
        // Сглаживание
        smoothInput = Vector2.Lerp(smoothInput, inputVector, Time.deltaTime * 5f);
    }

    void UpdateBlendTree()
    {
        // 2D Blend Tree параметры
        animator.SetFloat("Horizontal", smoothInput.x);
        animator.SetFloat("Vertical", smoothInput.y);
        
        // Общая скорость
        float speed = smoothInput.magnitude;
        animator.SetFloat("Speed", speed);
        
        // Направление
        if (inputVector.magnitude > 0.1f)
        {
            float direction = Mathf.Atan2(inputVector.x, inputVector.y) * Mathf.Rad2Deg;
            animator.SetFloat("Direction", direction);
        }
    }

    void HandleMovement()
    {
        if (inputVector.magnitude > 0.1f)
        {
            Vector3 moveDir = new Vector3(inputVector.x, 0, inputVector.y);
            transform.Translate(moveDir * moveSpeed * Time.deltaTime, Space.World);
            
            // Поворот
            Quaternion targetRotation = Quaternion.LookRotation(moveDir);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 10f);
        }
    }

    // 1D Blend Tree пример
    void Update1DBlendTree()
    {
        float speed = 0f;
        
        if (Input.GetKey(KeyCode.W))
        {
            speed = Input.GetKey(KeyCode.LeftShift) ? 2f : 1f;
        }
        
        animator.SetFloat("MovementSpeed", speed);
    }

    void OnGUI()
    {
        GUI.Box(new Rect(10, 10, 200, 80), "Blend Tree");
        GUI.Label(new Rect(20, 30, 180, 20), $"Input: ({inputVector.x:F2}, {inputVector.y:F2})");
        GUI.Label(new Rect(20, 50, 180, 20), $"Speed: {smoothInput.magnitude:F2}");
    }
}

Пример 4: События анимации

Работа с Animation Events:

using UnityEngine;

public class AnimationEvents : MonoBehaviour
{
    [Header("Events")]
    public AudioSource audioSource;
    public AudioClip footstepSound;
    public AudioClip attackSound;
    
    [Header("Effects")]
    public GameObject dustEffect;
    public Transform effectPoint;

    void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    // Animation Event: Шаг
    public void OnFootstep()
    {
        Debug.Log("Шаг!");
        
        if (audioSource && footstepSound)
        {
            audioSource.PlayOneShot(footstepSound);
        }
        
        if (dustEffect && effectPoint)
        {
            GameObject effect = Instantiate(dustEffect, effectPoint.position, effectPoint.rotation);
            Destroy(effect, 2f);
        }
    }

    // Animation Event: Атака
    public void OnAttackHit()
    {
        Debug.Log("Удар!");
        
        if (audioSource && attackSound)
        {
            audioSource.PlayOneShot(attackSound);
        }
        
        CheckAttackHit();
    }

    // Animation Event: Приземление
    public void OnLanding()
    {
        Debug.Log("Приземление!");
        
        if (dustEffect)
        {
            Vector3 groundPos = transform.position;
            groundPos.y = 0f;
            
            GameObject effect = Instantiate(dustEffect, groundPos, Quaternion.identity);
            Destroy(effect, 2f);
        }
    }

    // Animation Event с параметрами
    public void OnEventWithParameter(string message)
    {
        Debug.Log($"Событие: {message}");
    }

    public void OnEventWithFloat(float value)
    {
        Debug.Log($"Значение: {value}");
    }

    void CheckAttackHit()
    {
        Vector3 attackDir = transform.forward;
        float attackRange = 2f;
        
        RaycastHit hit;
        if (Physics.Raycast(transform.position, attackDir, out hit, attackRange))
        {
            Debug.Log($"Попадание: {hit.collider.name}");
            
            Rigidbody rb = hit.collider.GetComponent<Rigidbody>();
            if (rb)
            {
                rb.AddForce(attackDir * 10f, ForceMode.Impulse);
            }
        }
    }

    void Update()
    {
        // Тест событий
        if (Input.GetKeyDown(KeyCode.T))
        {
            OnFootstep();
        }
    }
}

Пример 5: Продвинутые скрипты

Продвинутые техники анимации:

using UnityEngine;

public class AdvancedAnimation : MonoBehaviour
{
    [Header("Animation")]
    public Animator animator;
    
    [Header("IK")]
    public Transform leftHandTarget;
    public Transform rightHandTarget;
    public Transform lookTarget;
    
    private bool ikActive = false;

    void Start()
    {
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        HandleControls();
    }

    void HandleControls()
    {
        // IK переключение
        if (Input.GetKeyDown(KeyCode.I))
        {
            ikActive = !ikActive;
            Debug.Log($"IK: {ikActive}");
        }

        // Скорость анимации
        if (Input.GetKey(KeyCode.Q))
        {
            animator.speed = 0.5f;
        }
        else if (Input.GetKey(KeyCode.E))
        {
            animator.speed = 2f;
        }
        else
        {
            animator.speed = 1f;
        }

        // Вес слоя
        if (Input.GetKeyDown(KeyCode.L))
        {
            float weight = animator.GetLayerWeight(1);
            animator.SetLayerWeight(1, weight > 0 ? 0 : 1);
        }
    }

    void OnAnimatorIK(int layerIndex)
    {
        if (!ikActive) return;

        // IK рук
        if (leftHandTarget)
        {
            animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1f);
            animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1f);
            animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandTarget.position);
            animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandTarget.rotation);
        }

        if (rightHandTarget)
        {
            animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1f);
            animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1f);
            animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandTarget.position);
            animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandTarget.rotation);
        }

        // Look At
        if (lookTarget)
        {
            animator.SetLookAtWeight(1f);
            animator.SetLookAtPosition(lookTarget.position);
        }
    }

    // Кроссфейд между анимациями
    void CrossfadeToAnimation(string animationName, float duration = 0.3f)
    {
        animator.CrossFade(animationName, duration);
        Debug.Log($"Кроссфейд к: {animationName}");
    }

    // Воспроизведение анимации в определенном слое
    void PlayInLayer(string animationName, int layer)
    {
        animator.Play(animationName, layer);
    }

    // Получение информации о состоянии
    void GetStateInfo()
    {
        AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0);
        Debug.Log($"Состояние: {state.shortNameHash}");
        Debug.Log($"Время: {state.normalizedTime}");
        Debug.Log($"Длительность: {state.length}");
    }

    void OnGUI()
    {
        GUI.Box(new Rect(10, 10, 200, 120), "Продвинутая анимация");
        GUI.Label(new Rect(20, 30, 180, 20), $"IK: {ikActive}");
        GUI.Label(new Rect(20, 50, 180, 20), $"Скорость: {animator.speed:F1}");
        GUI.Label(new Rect(20, 70, 180, 20), "I - IK, Q/E - скорость");
        GUI.Label(new Rect(20, 90, 180, 20), "L - вес слоя");
    }
}