Цель этого документа — помочь вам настроить перемещение гуманоидных персонажей с помощью навигационной системы.
Для этого мы будем использовать встроенные системы Unity для анимации и навигации, а также специальные сценарии.
Предполагается, что вы знакомы с основами Unity и системой анимации Mecanim.
Доступен пример проекта, поэтому вам не нужно добавлять скриптыфрагмент кода, позволяющий создать собственные Компоненты, запускать игровые события, изменять свойства Компонентов с течением времени и реагировать на действия пользователя любым удобным для вас способом. Подробнее
Посмотрите в Словарь или настройте анимацию и контроллер анимации с нуля:
- NavigationAnimation_53.zip Работает с Unity 5.3+
Создание контроллера анимации
Чтобы получить отзывчивый и универсальный контроллер анимации, охватывающий широкий диапазон движений, нам нужен набор анимаций, движущихся в разных направлениях. Это иногда называют набором стрейфов.
В дополнение к анимации движения нам нужна анимация для стоящего персонажа.
Мы продолжаем упорядочивать набор стрейфов в 2D-дереве смешивания — выбираем тип смешивания: 2D Simple Directional и размещаем анимацию с помощью Вычислить позиции > Скорость XZ
Для управления смешиванием мы добавляем два плавающих параметра velx и vely и назначаем их дереву смешивания.
Здесь мы разместим 7 анимаций бега — каждая с разной скоростью. В дополнение к движениям вперед (+ влево/вправо) и назад (+ влево/вправо) мы также используем анимационный клипданные анимации которые можно использовать для анимированных персонажей или простых анимаций. Это простое «единичное» движение, такое как (один конкретный пример) «Простояние», «Ходьба» или «Бег». Подробнее
См. в Словарь для бега на месте. Последний выделен в центре 2D-карты смешивания ниже. Причина для того, чтобы анимация работала на месте, двояка: во-первых, она сохраняет стиль бега при смешивании с другими анимациями. Во-вторых, анимация предотвращает скольжение ног при смешивании.
Затем мы добавляем клип анимации бездействия в отдельный узел (Idle). Теперь у нас есть два отдельных состояния анимации, которые мы связываем с двумя переходами.
Для управления переключением между состояниями движения и бездействия мы добавляем логический управляющий параметр move. Затем отключите свойство Имеет время выхода для переходов. Это позволяет переходу запускаться в любой момент во время анимации. Время перехода должно быть установлено примерно на 0,10 секунды, чтобы обеспечить плавный переход.
Теперь поместите новый созданный контроллер анимации на персонажа, которого хотите переместить.
Нажмите кнопку воспроизведения и выберите персонажа в окне иерархии. Теперь вы можете вручную управлять значениями анимации в окне Animatorокне, в котором визуализируется и редактируется контроллер Animator. Подробнее
Посмотрите в Словарь и измените состояние движения и скорость.
Следующим шагом является создание других средств управления параметрами анимациииспользуемых для связи между сценарием и контроллером Animator. Некоторые параметры могут быть установлены в сценариях и использоваться контроллером, в то время как другие параметры основаны на пользовательских кривых в анимационных клипах и могут быть выбраны с помощью API сценариев. Подробнее
См. в Словарь.
Управление навигацией
Поместите компонент NavMeshAgent на персонажа и отрегулируйте радиус, высоту и в соответствии с персонажем - дополнительно измените свойство скорости, чтобы оно соответствовало максимальной скорости в дерево смешивания анимацииИспользуется для непрерывного смешивания между похожими анимационными клипами на основе плавающих параметров анимации. Подробнее
См. в Словарь.
Создайте навигационную сеткуСетку, которую Unity создает для аппроксимации пешеходных зон и препятствий в вашей среде для поиска пути и искусственного интеллекта. управляемая навигация. Подробнее
См. Словарь для sceneСцена содержит окружение и меню вашей игры. Думайте о каждом уникальном файле сцены как об уникальном уровне. В каждой сцене вы размещаете свое окружение, препятствия и декорации, по сути проектируя и создавая свою игру по частям. Подробнее
Посмотрите в Словарь, куда вы поместили символ.
Далее нам нужно указать персонажу, куда ему двигаться. Обычно это очень специфично для приложения. Здесь мы выбираем поведение щелчка для перемещения — персонаж перемещается в точку в мире, где пользователь щелкнул на экране.
// ClickToMove.cs
using UnityEngine;
using UnityEngine.AI;
[RequireComponent (typeof (NavMeshAgent))]
public class ClickToMove : MonoBehaviour {
RaycastHit hitInfo = new RaycastHit();
NavMeshAgent agent;
void Start () {
agent = GetComponent ();
}
void Update () {
if(Input.GetMouseButtonDown(0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out hitInfo))
agent.destination = hitInfo.point;
}
}
}
Нажав кнопку воспроизведения и щелкнув по сцене, вы увидите, как персонаж перемещается по сцене. Однако — анимация совершенно не соответствует движению. Нам нужно передать состояние и скорость агента контроллеру анимации.
Для передачи информации о скорости и состоянии от агента к контроллеру анимации мы добавим еще один скрипт.
// LocomotionSimpleAgent.cs
using UnityEngine;
using UnityEngine.AI;
[RequireComponent (typeof (NavMeshAgent))]
[RequireComponent (typeof (Animator))]
public class LocomotionSimpleAgent : MonoBehaviour {
Animator anim;
NavMeshAgent agent;
Vector2 smoothDeltaPosition = Vector2.zero;
Vector2 velocity = Vector2.zero;
void Start ()
{
anim = GetComponent ();
agent = GetComponent ();
// Don’t update position automatically
agent.updatePosition = false;
}
void Update ()
{
Vector3 worldDeltaPosition = agent.nextPosition - transform.position;
// Map 'worldDeltaPosition' to local space
float dx = Vector3.Dot (transform.right, worldDeltaPosition);
float dy = Vector3.Dot (transform.forward, worldDeltaPosition);
Vector2 deltaPosition = new Vector2 (dx, dy);
// Low-pass filter the deltaMove
float smooth = Mathf.Min(1.0f, Time.deltaTime/0.15f);
smoothDeltaPosition = Vector2.Lerp (smoothDeltaPosition, deltaPosition, smooth);
// Update velocity if time advances
if (Time.deltaTime > 1e-5f)
velocity = smoothDeltaPosition / Time.deltaTime;
bool shouldMove = velocity.magnitude > 0.5f && agent.remainingDistance > agent.radius;
// Update animation parameters
anim.SetBool("move", shouldMove);
anim.SetFloat ("velx", velocity.x);
anim.SetFloat ("vely", velocity.y);
GetComponent().lookAtTargetPosition = agent.steeringTarget + transform.forward;
}
void OnAnimatorMove ()
{
// Update position to agent position
transform.position = agent.nextPosition;
}
}
Этот скрипт заслуживает небольшого пояснения. Он размещается на персонаже, к которому прикреплены компоненты Animator и NavMeshAgent, а также скрипт перемещения по щелчку выше.
Сначала сценарий указывает агенту не обновлять позицию символа автоматически. Мы обрабатываем последнее обновление позиции в сценарии. Ориентация обновляется агентом.
Наложение анимации управляется считыванием скорости агента. Она преобразуется в относительную скорость (в зависимости от ориентации персонажа), а затем сглаживается. Преобразованные компоненты горизонтальной скорости затем передаются в Animator, и, кроме того, переключение состояний между бездействием и движением контролируется скоростью (т. е. величиной скорости).
В обратном вызове OnAnimatorMove()
мы обновляем положение символа, чтобы оно соответствовало NavMeshAgent.
Повторное воспроизведение сцены показывает, что анимация максимально точно соответствует движению.
Улучшение качества навигационного символа
Чтобы улучшить качество анимированного и навигационного персонажа, мы рассмотрим несколько вариантов.
Ищу
Важно, чтобы персонаж смотрел и поворачивался к достопримечательностям, чтобы передать внимание и предвкушение. Мы будем использовать API систем анимации. Это требует другого сценария.
// LookAt.cs
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (Animator))]
public class LookAt : MonoBehaviour {
public Transform head = null;
public Vector3 lookAtTargetPosition;
public float lookAtCoolTime = 0.2f;
public float lookAtHeatTime = 0.2f;
public bool looking = true;
private Vector3 lookAtPosition;
private Animator animator;
private float lookAtWeight = 0.0f;
void Start ()
{
if (!head)
{
Debug.LogError("No head transform - LookAt disabled");
enabled = false;
return;
}
animator = GetComponent ();
lookAtTargetPosition = head.position + transform.forward;
lookAtPosition = lookAtTargetPosition;
}
void OnAnimatorIK ()
{
lookAtTargetPosition.y = head.position.y;
float lookAtTargetWeight = looking ? 1.0f : 0.0f;
Vector3 curDir = lookAtPosition - head.position;
Vector3 futDir = lookAtTargetPosition - head.position;
curDir = Vector3.RotateTowards(curDir, futDir, 6.28f*Time.deltaTime, float.PositiveInfinity);
lookAtPosition = head.position + curDir;
float blendTime = lookAtTargetWeight > lookAtWeight ? lookAtHeatTime : lookAtCoolTime;
lookAtWeight = Mathf.MoveTowards (lookAtWeight, lookAtTargetWeight, Time.deltaTime/blendTime);
animator.SetLookAtWeight (lookAtWeight, 0.2f, 0.5f, 0.7f, 0.5f);
animator.SetLookAtPosition (lookAtPosition);
}
}
Добавьте сценарий к персонажу и назначьте свойство головы преобразованию головы в иерархии преобразования персонажей. Сценарий LookAt не имеет понятия об управлении навигацией, поэтому, чтобы контролировать, куда смотреть, мы возвращаемся к сценарию LocomotionSimpleAgent.cs и добавляем пару строк для управления просмотром. Добавьте конец Update()
add:
LookAt lookAt = GetComponent ();
if (lookAt)
lookAt.lookAtTargetPosition = agent.steeringTarget + transform.forward;
Это укажет сценарию LookAt установить точку интереса примерно на следующий угол пути или, если нет углов, на конец пути.
Попробуйте.
Анимация управляемого персонажа с использованием навигации
До сих пор персонаж полностью контролировался позицией, продиктованной агентом. Это гарантирует, что уклонение от других персонажей и препятствий напрямую влияет на позицию персонажа. Однако это может привести к скольжению, если анимация не охватывает предлагаемую скорость. Здесь мы немного ослабим ограничения персонажа. По сути, мы будем обменивать качество уклонения на качество анимации.
Замените обратный вызов OnAnimatorMove()
в сценарии LocomotionSimpleAgent.cs, замените строку следующей строкой
void OnAnimatorMove ()
{
// Update position based on animation movement using navigation surface height
Vector3 position = anim.rootPosition;
position.y = agent.nextPosition.y;
transform.position = position;
}
Попробовав это, вы можете заметить, что персонаж теперь может отклоняться от позиции агента (зеленый каркасный цилиндр). Возможно, вам придется ограничить дрейф анимации персонажа. Это можно сделать либо потянув агента к персонажу, либо потянув персонажа к позиции агента. Добавьте следующее в конец метода Update()
в скрипте LocomotionSimpleAgent.cs.
// Pull character towards agent
if (worldDeltaPosition.magnitude > agent.radius)
transform.position = agent.nextPosition - 0.9f*worldDeltaPosition;
Или — если вы хотите, чтобы агент следовал за персонажем.
// Pull agent towards character
if (worldDeltaPosition.magnitude > agent.radius)
agent.nextPosition = transform.position + 0.9f*worldDeltaPosition;
Что работает лучше всего, во многом зависит от конкретного варианта использования.
Заключение
Мы создали персонажа, который перемещается с помощью системы навигации и соответственно анимируется. Изменение значений времени смешивания, визуального веса и т. д. может улучшить внешний вид и является хорошим способом дальнейшего изучения этой настройки.