Это первая из двух частей, посвящённых ориентации одного объекта на другой в Unity. В частности, мы увидим, как заставить 3D-модель камеры наблюдения автоматически поворачиваться в сторону Player, отслеживая его перемещения по сцене.
Видеоверсия этого руководства в настоящее время недоступна на этом языке.
Текстовая расшифровка видео
Всем привет!
Это первая из двух частей, посвящённых ориентации одного объекта на другой в Unity. В частности, мы увидим, как заставить 3D-модель камеры наблюдения автоматически поворачиваться в сторону Player, отслеживая его перемещения по сцене.

Этот урок создан в Unity 2022 и разделён на две части.
В первой части мы проанализируем задачу и решим её с помощью метода Rotate, чтобы получить мгновенный поворот камеры.
Во второй части мы будем использовать кватернионы и метод Slerp для создания более плавного и постепенного вращения.
Перед тем как переходить к реализации, разберём саму задачу.
Камера может вращаться только вокруг своей локальной вертикальной оси, поэтому мы можем свести задачу к двумерной плоскости. Чтобы это было нагляднее, я рассматриваю сцену из вида TOP в режиме Orthogonal.

И 3D-модель камеры наблюдения, и Player, как и все игровые объекты, имеют координаты позиции XYZ, которые определяют их положение в сцене.
Нас не интересует координата position.y, потому что камера может вращаться только вокруг этой локальной оси, поэтому мы будем учитывать только координаты x и z, как если бы оба объекта находились на одной плоскости с одинаковым значением y.
В нашем случае камеру наблюдения можно считать началом системы координат, то есть точкой 0,0 на декартовой плоскости.
Кроме того, направление, в котором «смотрит» камера, а в данном случае это отрицательная локальная ось Y, можно принять за одну из осей декартовой системы.
Мы можем определить направление, соединяющее виртуальную камеру и позицию Player в сцене. Это просто вектор, который в нашем случае мы также будем рассматривать только в двух измерениях, то есть по глобальным X и Z.
Следовательно, вращение, которое нужно применить к камере наблюдения, определяется углом между текущей ориентацией модели, её отрицательной локальной осью Y, и идеальным вектором, соединяющим камеру с Player. Найдя этот угол, мы получим значение поворота вокруг локальной вертикальной оси 3D-модели.

Нам повезло: существует математическая функция двухаргументный арктангенс, реализованная методом Atan2 в Mathf, которая как раз вычисляет этот угол, принимая в качестве параметра вектор направления, полученный как разность координат двух точек.
Конкретно Atan2 вычисляет угол между осью X декартовой плоскости и вектором направления между началом координат и целью.
В нашем случае начало координат задаётся X и Z координатами 3D-модели камеры наблюдения, поэтому координаты цели x и y будут равны разности координат между камерой и Player, а ось X совпадает с отрицательной локальной осью Y камеры.
Функция принимает параметры в обратном порядке. В декартовой системе это atan2(y, x). В нашем случае, учитывая систему координат Unity, где ось x является боковой, ось z фронтальной, а ось y вертикальной, которую мы здесь не учитываем, мы будем писать atan2(x, z).
Согласно документации Unity, Mathf.Atan2 принимает координаты y и x, которые в нашем случае будут x и z, и возвращает значение типа float, представляющее угол в радианах, тангенс которого равен y/x.

Функция Rotate в Unity принимает угол поворота в градусах, поэтому при написании кода необходимо учитывать различие единиц измерения и выполнить преобразование.
Теперь, когда мы разобрали задачу и её решение, перейдём к первой реализации: мгновенный поворот с помощью Rotate.
Сначала создадим новый C#-скрипт под названием LookAtObject и привяжем его к самой камере наблюдения. Я говорю «к самой камере», потому что этот ассет состоит из трёх элементов: основания для крепления на стене, pivot-элемента и самой камеры.
Нас интересует, чтобы камера вращалась вокруг своей вертикальной оси, то есть вокруг своей локальной вертикальной оси.
В виртуальном пространстве Unity глобальная вертикальная ось — это ось y, но у каждого объекта может быть собственная локальная система координат. В случае 3D-модели камеры наблюдения это локальная ось Z, что можно увидеть, переключившись в режим отображения LOCAL в редакторе Unity.

Это различие вносит небольшую сложность в проект. Нам нужно вычислить угол поворота в глобальной плоскости сцены, используя координаты X и Z объектов, а затем выполнить вращение вокруг локальной вертикальной оси ассета.
Сначала создадим ссылку на объект, за которым нужно следить. Для этого объявим переменную типа Transform с именем objectA и свяжем её с Player в Inspector.
[SerializeField] private Transform objectA;

После этого можно перейти в метод Update для реализации решения. Напоминаю, что локальная вертикальная ось камеры — это её ось Z.
Нам понадобятся всего три строки кода.
В первой строке мы вычислим угол между текущей ориентацией камеры и той, которую она должна принять, чтобы смотреть на объект.
Во второй строке вычислим величину поворота вокруг локальной оси Z камеры для достижения нужной ориентации.
В третьей строке выполним сам поворот с помощью метода Rotate, которому нужно передать два параметра: локальный вертикальный вектор и величину поворота вокруг этого вектора.
Начнём с вычисления угла между текущей ориентацией камеры и требуемой, используя функцию Mathf.Atan2, которая возвращает значение в радианах, поэтому его нужно перевести в градусы.
Мы уже видели, что в двумерной плоскости Atan2 принимает два параметра: разность координат Y и разность координат X двух точек, в порядке цель и наблюдатель.
В нашем случае, с глобальной системой координат Unity, где X и Z лежат в плоскости, а глобальная ось Y является вертикальной осью сцены, нужно написать:
float theta = Mathf.Atan2((objectA.transform.position.x - transform.position.x),(objectA.transform.position.z - transform.position.z)) * Mathf.Rad2Deg;
Таким образом мы сразу переводим результат в градусы.
Переходим ко второй строке, где вычисляем величину поворота в градусах:
float deltaAngle = Mathf.DeltaAngle(transform.eulerAngles.z, theta);
Здесь transform.eulerAngles.z — это текущая ориентация камеры наблюдения, а theta — ориентация, рассчитанная на предыдущем шаге, которую она должна принять.
Третья и последняя строка выполняет поворот камеры:
transform.Rotate(Vector3.forward, deltaAngle);
Обратите внимание, что используется Vector3.forward, который обозначает локальную ось Z объекта, вокруг которой выполняется вращение. В данной 3D-модели локальная ось Z соответствует вертикальной оси объекта, а не направлению вперёд и назад.
Сохраните скрипт, вернитесь в редактор Unity и нажмите Play, чтобы оценить результат.
ПРИМЕЧАНИЕ - Если камера наблюдения не направлена на цель, убедитесь, что начальные значения XYZ Rotation камеры в Inspector установлены в 0, и попробуйте снова.


Подведём итог. В первой части мы разобрали задачу, рассмотрели, как свести её к двумерной плоскости, как вычислить вектор направления и как применить его к конкретному случаю камеры наблюдения с учётом её локальной системы координат.
Затем мы реализовали мгновенный поворот объекта, заставляя его в каждом кадре игры смотреть на Player с помощью функции Rotate.
Во второй части мы рассмотрим вычисление кватернионов вращения и использование функции Slerp для более плавного поворота объекта.