Примеры кода
Пример 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 - вес слоя");
}
}