Основы программирования в Unity

Практический курс по изучению скриптинга на C# для создания игр в Unity. Вы освоите базовый синтаксис, работу с компонентами, физикой и игровой логикой.

1. Введение в скриптинг: структура C# и жизненный цикл MonoBehaviour

Введение в скриптинг: структура C# и жизненный цикл MonoBehaviour

Unity — это не просто графический редактор, а мощный движок, управляемый кодом. Скрипты в Unity — это компоненты, которые оживляют объекты: заставляют персонажа двигаться, врагов — атаковать, а интерфейс — реагировать на нажатия. Основным языком программирования в Unity является C#.

Скрипт как компонент

В Unity действует компонентный подход. Любой объект на сцене (GameObject) — это контейнер. Его свойства определяются компонентами, которые к нему прикреплены. Transform определяет позицию, MeshRenderer — внешний вид, а ваш скрипт — поведение.

Когда вы создаете C# скрипт, вы создаете новый тип компонента, который можно добавить к игровому объекту так же, как и встроенные компоненты Unity.

!Игровой объект состоит из набора компонентов, одним из которых является ваш скрипт

Создание и структура скрипта

При создании нового скрипта через Create -> C# Script, Unity генерирует файл с шаблонным кодом. Рассмотрим его структуру детально.

1. Библиотеки (Namespaces)

В самом верху находятся директивы using. Они подключают пространства имен, содержащие готовые классы и функции. * using UnityEngine; — самая важная строка. Она дает доступ к базовым классам Unity: GameObject, Transform, Debug и другим.

2. Класс и наследование

Строка public class PlayerController : MonoBehaviour объявляет ваш класс.

Критически важное правило: Имя класса (PlayerController) должно в точности совпадать с именем файла (PlayerController.cs). Если они различаются, Unity не сможет подключить скрипт к объекту и выдаст ошибку.

3. Наследование от MonoBehaviour

Обратите внимание на часть : MonoBehaviour. Это означает, что ваш класс наследуется от базового класса Unity — MonoBehaviour. Именно это превращает обычный C# класс в компонент Unity, который можно перетащить в Инспектор, и который имеет доступ к методам жизненного цикла (Start, Update и т.д.).

Переменные и Инспектор

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

Public переменные

Если вы объявите переменную как public, она станет видимой в окне Inspector.

В Инспекторе появятся поля для ввода чисел и слот для перетаскивания игрового объекта.

Private и SerializeField

Переменные private доступны только внутри скрипта и не видны в редакторе. Это полезно для внутренних расчетов.

Однако, часто бывает нужно скрыть переменную от других скриптов (инкапсуляция), но оставить возможность настраивать её в редакторе. Для этого используется атрибут [SerializeField].

Жизненный цикл MonoBehaviour

Скрипты в Unity не выполняются линейно сверху вниз один раз. Они реагируют на события. Unity вызывает определенные методы в вашем скрипте в строго определенные моменты времени. Эти методы называются методами жизненного цикла (Lifecycle Methods) или "магическими методами".

!Порядок выполнения основных методов в Unity

Awake

Метод Awake() вызывается самым первым, как только объект инициализируется. Он срабатывает даже если скрипт выключен (галочка снята), но сам объект активен.

Для чего использовать: * Инициализация переменных. * Получение ссылок на другие компоненты (например, GetComponent<Rigidbody>()). * Настройка синглтонов.

Start

Метод Start() вызывается перед первым кадром, если скрипт включен. Он срабатывает после Awake().

Для чего использовать: * Действия, требующие, чтобы все остальные объекты уже были инициализированы (прошли стадию Awake). * Установка начальных параметров логики игры.

Update

Метод Update() вызывается каждый кадр. Это основное место для игровой логики.

Частота вызова зависит от производительности компьютера (FPS). Если игра идет в 60 FPS, Update вызовется 60 раз в секунду. Если компьютер тормозит и выдает 10 FPS — то 10 раз.

Для чего использовать: * Обработка ввода игрока (нажатия клавиш). * Перемещение персонажей (не физическое). * Таймеры.

FixedUpdate

Метод FixedUpdate() вызывается через фиксированные промежутки времени (по умолчанию 0.02 секунды, или 50 раз в секунду). Он не зависит от частоты кадров.

Для чего использовать: * Любые манипуляции с физикой (работа с Rigidbody). * Приложение сил, импульсов.

Если вы будете двигать физическое тело в Update, движение будет дерганым при нестабильном FPS. В FixedUpdate физика всегда стабильна.

LateUpdate

Метод LateUpdate() вызывается каждый кадр, но строго после того, как отработали все методы Update во всех скриптах.

Для чего использовать: * Камера, следующая за игроком. Это гарантирует, что игрок уже завершил свое движение в Update, и камера переместится в его новую, окончательную позицию, избегая дрожания.

Взаимодействие с консолью

Для проверки работы кода и поиска ошибок используется класс Debug.

Сообщения появятся в окне Console внизу редактора Unity.

Итоги

* Скрипт в Unity — это компонент, наследуемый от MonoBehaviour. Имя файла и имя класса обязаны совпадать. * Переменные public и [SerializeField] private видны в Инспекторе Unity, что позволяет настраивать игру без изменения кода. * Awake используется для инициализации ссылок, Start — для настройки логики перед стартом. * Update зависит от FPS и используется для ввода и логики, FixedUpdate имеет фиксированный шаг и нужен для физики. * LateUpdate выполняется после всех обновлений и идеален для управления камерой.

2. Управление объектами: компонент Transform и векторная математика

Управление объектами: компонент Transform и векторная математика

В предыдущей статье мы разобрали, что поведение объектов определяется скриптами. Однако, прежде чем создавать сложную логику, необходимо научиться базовым вещам: перемещать, вращать и масштабировать объекты. За эти действия отвечает компонент Transform и математический аппарат векторов.

Компонент Transform

Каждый GameObject в Unity обязан иметь компонент Transform. Удалить его невозможно. Он хранит информацию о трех ключевых свойствах объекта в трехмерном пространстве:

  • Position (Позиция): Где находится объект (координаты X, Y, Z).
  • Rotation (Вращение): Как объект повернут.
  • Scale (Масштаб): Размеры объекта по осям.
  • В коде доступ к этому компоненту осуществляется через свойство transform (с маленькой буквы), которое есть у любого наследника MonoBehaviour.

    Векторная математика: Vector3

    Для описания позиции, поворота и масштаба Unity использует структуру Vector3. Это фундаментальный строительный блок всей геометрии в движке.

    Vector3 — это контейнер, хранящий три числа типа float: x, y и z. В зависимости от контекста, вектор может означать разные вещи:

    * Точка в пространстве: Координаты (например, игрок находится в точке 10, 0, 5). * Направление: Куда смотрит объект (например, вектор (0, 0, 1) означает «вперед»). * Величина и направление (Скорость): Куда и как быстро движется объект.

    !Система координат Unity: X — вправо, Y — вверх, Z — вперед

    Базовые векторы

    В Unity есть заготовленные короткие записи для часто используемых направлений:

    * Vector3.zero — (0, 0, 0) * Vector3.up — (0, 1, 0) — Вверх * Vector3.right — (1, 0, 0) — Вправо * Vector3.forward — (0, 0, 1) — Вперед (по синей оси Z)

    Сложение векторов

    Чтобы переместить объект, мы прибавляем к его текущей позиции вектор перемещения. Если объект находится в точке , и мы хотим сдвинуть его на вектор , новая позиция вычисляется так:

    Где — новая позиция, — старая позиция, — вектор смещения.

    Пример: Объект в точке (1, 1, 1). Мы прибавляем Vector3.up (0, 1, 0). Новая позиция станет (1, 2, 1).

    Мировые и локальные координаты

    Это одна из самых важных концепций в иерархии Unity. Объекты могут быть вложены друг в друга (быть дочерними).

    * Global (World) Space: Координаты относительно центра игрового мира (0, 0, 0). В коде это transform.position. * Local Space: Координаты относительно родительского объекта. В коде это transform.localPosition.

    Представьте, что вы сидите в поезде. Поезд движется со скоростью 100 км/ч относительно земли (Global Space). Но вы сидите в кресле и ваша скорость относительно поезда равна 0 (Local Space). Если вы встанете и пойдете по вагону, ваша локальная позиция изменится, а глобальная будет суммой движения поезда и вашего движения.

    !Разница между локальной позицией (относительно родителя) и глобальной (относительно мира)

    Если у объекта нет родителя, его локальные и глобальные координаты совпадают.

    Перемещение объектов

    Существует два основных способа двигать объекты через Transform.

    1. Телепортация (Прямое присваивание)

    Вы просто задаете новые координаты. Объект мгновенно оказывается в новой точке.

    2. Метод Translate (Сдвиг)

    Метод Translate сдвигает объект относительно его текущего положения.

    Проблема FPS и Time.deltaTime

    Если вы напишете код перемещения внутри Update просто прибавляя фиксированное значение (например, 0.1f), скорость объекта будет зависеть от мощности компьютера.

    * Компьютер А (100 FPS): Метод Update вызовется 100 раз за секунду. Объект сдвинется на метров. * Компьютер Б (30 FPS): Метод Update вызовется 30 раз за секунду. Объект сдвинется на метра.

    Это недопустимо. Игрок с мощным ПК будет двигаться быстрее.

    Для решения используется Time.deltaTime. Это время в секундах, прошедшее с момента отрисовки последнего кадра.

    Формула корректного перемещения:

    Где — новая позиция, — текущая позиция, — вектор направления (единичный), — скорость (метров в секунду), — время прошедшее с прошлого кадра (Time.deltaTime).

    Пример расчета: Скорость м/с. * При 100 FPS, . Смещение за кадр: м. За 100 кадров (1 сек) пройдем 10 метров. * При 10 FPS, . Смещение за кадр: м. За 10 кадров (1 сек) пройдем те же 10 метров.

    Правильный код движения вперед:

    Вычисление направления и дистанции

    Частая задача: заставить врага идти к игроку. Для этого нужно найти вектор направления.

    Вектор от точки A к точке B находится вычитанием:

    Где — искомый вектор направления, — точка назначения (цель), — начальная точка.

    * magnitude — длина вектора (расстояние между точками). * normalized — вектор того же направления, но с длиной 1. Это важно, чтобы скорость движения не зависела от расстояния до цели.

    Для простого вычисления расстояния также удобно использовать статический метод: float dist = Vector3.Distance(transform.position, target.position);

    Вращение (Rotation)

    Вращение в 3D — сложная тема, так как Unity использует Кватернионы (Quaternion) во избежание математических проблем (таких как "складывание рамок"). Однако, для простых действий мы можем использовать углы Эйлера (X, Y, Z в градусах), которые мы видим в Инспекторе.

    Метод Rotate

    Вращает объект вокруг осей на заданное количество градусов.

    Метод LookAt

    Самый простой способ повернуть объект лицом к цели.

    Итоги

    * Компонент Transform есть у каждого объекта и отвечает за Позицию, Вращение и Масштаб. * Vector3 — основная структура данных для координат и направлений (x, y, z). * Глобальные координаты (position) — это положение в мире, локальные (localPosition) — относительно родителя. * Для плавного движения, независящего от FPS, всегда умножайте скорость на Time.deltaTime. * Чтобы получить вектор направления к цели, нужно из позиции цели вычесть позицию объекта (Target - Current).