Двухмерный персонаж бежит по платформам, оранжевый видоискатель камеры плавно следует за ним

Как сделать камеру за персонажем в Unity 2D: скрипт и Cinemachine

·  10 мин чтения Для начинающих Unity 2D C# Туториал

Коротко. Камера за персонажем в Unity 2D — это скрипт на Main Camera, который в LateUpdate() переносит камеру к герою: transform.position = Vector3.SmoothDamp(transform.position, target.position + offset, ref velocity, smoothTime);. Если камера дёргается, включите у Rigidbody2D героя Interpolate. Без кода то же самое делает пакет Cinemachine: 2D Camera, Position Composer и Confiner 2D.

Ниже: готовый скрипт с границами уровня под Unity 6.6, замеры дрожания и отставания камеры и пошаговая настройка Cinemachine 3.

Камера — первое, что замечает игрок. Если она отстаёт, дёргается или показывает пустоту за краем уровня, игра сразу выглядит недоделанной. Чтобы не гадать, почему так бывает, мы собрали уровень в Unity 6.6 (6000.6) и измерили, как ведут себя разные варианты камеры на 30, 60 и 144 кадрах в секунду. Цифры ниже взяты из этого теста, а весь код компилируется в этой версии без предупреждений.

Тест пригодился сразу. Первая версия нашего скрипта ограничивала центр камеры жёсткими числами, и в широком окне Game кадр вышел за левый край уровня на 1,13 единицы. На мониторе другой ширины граница сдвинулась бы иначе. В итоговом скрипте этой ошибки нет: он сам считает ширину кадра.

Как работает камера, которая следует за персонажем

Камера в Unity — обычный объект сцены, поэтому «следовать» значит каждый кадр менять её transform.position. Самый простой вариант — одна строка в LateUpdate():

transform.position = target.position + offset;

Здесь offset — сдвиг относительно героя. По оси Z он должен быть −10: камера стоит перед спрайтами, а при Z = 0 она окажется в их плоскости, и экран станет пустым. Такая камера жёстко прилипает к герою и повторяет каждое его движение. Обычно хочется мягче, поэтому дальше добавим плавность.

Шаг 1. Скрипт плавной камеры

Создайте скрипт CameraFollow2D, повесьте его на Main Camera и перетащите героя в поле Target. Плавность даёт Vector3.SmoothDamp: он мягко разгоняет и тормозит камеру, а параметр smoothTime — это примерное время в секундах, за которое камера догоняет героя.

C#
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class CameraFollow2D : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private Vector3 offset = new Vector3(0f, 1f, -10f);
    [SerializeField] private float smoothTime = 0.2f;

    [Header("Края уровня")]
    [SerializeField] private bool useBounds = true;
    [SerializeField] private Vector2 levelMin = new Vector2(-12f, -6f);
    [SerializeField] private Vector2 levelMax = new Vector2(32f, 9f);

    private Camera cam;
    private Vector3 velocity;

    private void Awake()
    {
        cam = GetComponent<Camera>();
    }

    // LateUpdate: к этому моменту персонаж уже сдвинулся в этом кадре
    private void LateUpdate()
    {
        if (target == null) return;

        Vector3 desired = target.position + offset;
        Vector3 pos = Vector3.SmoothDamp(transform.position, desired, ref velocity, smoothTime);

        if (useBounds)
        {
            // Половина видимой области: высоту задаёт Size, ширина зависит от экрана
            float halfHeight = cam.orthographicSize;
            float halfWidth = halfHeight * cam.aspect;
            pos.x = Mathf.Clamp(pos.x, levelMin.x + halfWidth, levelMax.x - halfWidth);
            pos.y = Mathf.Clamp(pos.y, levelMin.y + halfHeight, levelMax.y - halfHeight);
        }

        transform.position = pos;
    }

    // Жёлтая рамка в окне Scene: края уровня, за которые камера не заглянет
    private void OnDrawGizmosSelected()
    {
        if (!useBounds) return;

        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube((levelMin + levelMax) / 2f, levelMax - levelMin);
    }
}
Unity: на Main Camera скрипт Camera Follow 2D с полями Target, Offset, Smooth Time, Level Min и Level Max, в окне Scene жёлтая рамка краёв уровня
Жёлтая рамка рисуется методом OnDrawGizmosSelected и видна, когда камера выделена.

Почему LateUpdate(), а не Update(): Unity вызывает его после всех Update() в кадре. К этому моменту герой уже сдвинулся, и камера едет к его новой позиции, а не к позиции прошлого кадра. Так советует и документация Unity для камер, следующих за объектом.

Lerp или SmoothDamp

Во многих уроках камеру сглаживают так: Vector3.Lerp(transform.position, target, 5f * Time.deltaTime). Это работает, но результат сильнее зависит от FPS. Мы гоняли героя со скоростью 6 м/с и мерили, насколько камера отстаёт от него при разной частоте кадров:

Частота кадровLerp × Time.deltaTimeSmoothDamp (0,2 с)
30 FPS1,001,10
60 FPS1,101,15
144 FPS1,161,18
Разброс16%7%

Отставание указано в единицах Unity. С Lerp камера на слабом компьютере держится к герою заметно ближе, чем на быстром, и ощущение от управления меняется. SmoothDamp стабильнее, а его настройку легче понять: это время в секундах.

Шаг 2. Почему камера дёргается и как это исправить

Главная причина дрожания — не скрипт камеры, а герой. Rigidbody2D двигается в физическом шаге, 50 раз в секунду, а кадров на экране больше. Без интерполяции в одних кадрах герой стоит на месте, в других прыгает сразу на целый шаг физики. Мы проверили это на 144 FPS, герой двигался со скоростью 6 м/с:

Interpolate у герояКамераНеравномерность хода камерыДрожание героя на экране
Noneжёстко за героем138%0 px
NoneSmoothDamp0,5%6,2 px
Interpolateжёстко за героем0%0 px
InterpolateSmoothDamp0%0 px

Без интерполяции дёргается что-то одно: либо весь мир (камера едет рывками), либо сам герой (камера едет плавно, а он прыгает по экрану на 6 пикселей при Full HD). Лекарство одно — галочка в инспекторе героя:

Инспектор Unity: у героя в компоненте Rigidbody 2D поле Interpolate установлено в Interpolate
Interpolate сглаживает положение героя между шагами физики.

Если всё ещё дёргается: проверьте, что героя с Rigidbody2D не двигают через transform.position, только через linearVelocity или силы. И что камерой управляет кто-то один: скрипт и Cinemachine одновременно будут спорить за её позицию. Как правильно двигать героя, разобрано в статье про движение и прыжок в Unity 2D.

Шаг 3. Границы уровня, чтобы камера не показывала пустоту

Без ограничений камера у края уровня покажет то, что за стеной: пустой фон или недостроенные куски сцены. Поэтому в скрипте есть поля Level Min и Level Max — левый нижний и правый верхний угол уровня. Центр камеры нельзя подпускать к краю вплотную: от края нужно отступить на половину кадра.

С этой поправкой в нашем тесте левый край кадра остановился ровно на краю уровня, на отметке −12,00, при любой ширине окна Game.

Шаг 4. Камера через Cinemachine 3

Cinemachine — официальный пакет Unity для управления камерой без кода. Всё, что мы написали выше, в нём уже есть, и даже больше: мёртвая зона, взгляд вперёд, плавные переходы между камерами. Мы проверяли на версии 3.1.7.

  1. Откройте Window → Package Management → Package Manager, во вкладке Unity Registry найдите Cinemachine и нажмите Install.
  2. Создайте камеру через GameObject → Cinemachine → Targeted Cameras → 2D Camera. Unity сам добавит на Main Camera компонент Cinemachine Brain: теперь главная камера слушается Cinemachine. Скрипт CameraFollow2D с неё уберите.
  3. Перетащите героя в поле Tracking Target. В нашем тесте меню его не заполнило, даже когда герой был выделен.
  4. В поле Lens задайте размер кадра — аналог Size у обычной камеры.
Unity: Cinemachine Camera с полем Tracking Target и компонент Cinemachine Position Composer с настройками Dead Zone и Damping, в окне Game видна тёмная мёртвая зона
Тёмный прямоугольник в окне Game — мёртвая зона. Его видно, когда выделена Cinemachine Camera.

За следование отвечает компонент Cinemachine Position Composer. Три настройки, которые стоит знать:

Границы уровня в Cinemachine задаёт расширение Confiner 2D. Создайте пустой объект CameraBounds, добавьте ему Polygon Collider 2D с галочкой Is Trigger и растяните фигуру по краям уровня. Затем у Cinemachine Camera в списке Add Extension выберите Cinemachine Confiner 2D и перетащите CameraBounds в поле Bounding Shape 2D.

Unity: компонент Cinemachine Confiner 2D с полем Bounding Shape 2D, в окне Scene жёлтая рамка границ камеры по краям уровня
Confiner 2D сам учитывает размер кадра, и вручную ничего считать не нужно.

Галочка Is Trigger нужна, чтобы рамка камеры не стала стеной для героя: о том, чем триггер отличается от обычного коллайдера, мы подробно писали в статье про физику, коллайдеры и триггеры. В тесте Confiner 2D остановил левый край кадра на −12,00, ровно как наш скрипт.

Скрипт или Cinemachine: что выбрать

Свой скриптCinemachine
Установканичего не нужнопакет из Package Manager
Плавность и границыпишете самиDamping и Confiner 2D
Мёртвая зона, взгляд вперёдещё кодгалочка в инспекторе
Понимание, как всё устроенополноепосле первого скрипта

Наш совет: один раз напишите камеру скриптом, чтобы понять принцип, а в настоящих проектах берите Cinemachine. Когда камера и герой готовы, следующий шаг — переход между уровнями.

Вывод

Плавная камера в 2D — это SmoothDamp в LateUpdate(), края уровня с поправкой на размер кадра и Interpolate у Rigidbody2D героя. Последняя галочка решает большинство жалоб «камера дёргается». В Cinemachine то же самое собирается из Position Composer и Confiner 2D без единой строки кода.

Если вы только начинаете, загляните в основы C# для Unity: там разобраны SerializeField и GetComponent из этого скрипта. Собрать из этих механик целую игру с проверкой заданий можно на бесплатном курсе Unity Старт и дальше по полной программе.

Частые вопросы

Как сделать, чтобы камера следовала за персонажем в Unity 2D?

Повесьте на Main Camera скрипт, который в LateUpdate ставит камеру в позицию героя со сдвигом: transform.position = target.position + offset. Сдвиг по Z должен быть -10, иначе камера окажется в плоскости спрайтов. Для плавности используйте Vector3.SmoothDamp или Cinemachine.

Почему камера дёргается при движении персонажа?

Чаще всего у Rigidbody2D героя не включена интерполяция. Физика считается 50 раз в секунду, а кадров на экране больше, и без Interpolate герой движется рывками. Поставьте у Rigidbody2D Interpolate: Interpolate, а камеру двигайте в LateUpdate.

Что лучше для камеры: Lerp или SmoothDamp?

Для камеры лучше Vector3.SmoothDamp: он плавно разгоняется и тормозит, а плавность задаётся в секундах. Популярный вариант Lerp(pos, target, speed * Time.deltaTime) зависит от FPS сильнее: в нашем тесте отставание камеры менялось на 16% между 30 и 144 кадрами, у SmoothDamp на 7%.

Как ограничить камеру границами уровня?

Ограничьте центр камеры через Mathf.Clamp, отступив от краёв уровня на половину видимой области: по высоте это Orthographic Size, по ширине Size, умноженный на camera.aspect. В Cinemachine то же делает расширение Confiner 2D с Polygon Collider 2D по краям уровня.

Что такое Cinemachine и нужен ли он новичку?

Cinemachine — официальный пакет Unity для управления камерой без кода. В нём уже есть плавное следование, мёртвая зона и ограничение границами уровня. Новичку стоит один раз написать камеру скриптом, чтобы понять принцип, а в проектах использовать Cinemachine.

Хочешь собрать платформер с камерой, врагами и уровнями?

На бесплатном курсе Unity Старт мы ставим движок, делаем первый проект и первый скрипт, а дальше по программе собираем полноценные игры с проверкой заданий.

Начать бесплатно