Unity 6.3
0 онлайн 55 гостей 3 в системе
Вход
Практическое руководство по разработке игр в Unity Глава 12 из 25 Оригинал, стр. 38

Программирование

Программирование Код в Unity позволяет настраивать и контролировать практически все аспекты игры, создавать визуальные инструменты для команды и даже изменять работу самой Unity. Unity использует C# - современный объектно-ориентированный язык, широко применяемый в индустрии программного обеспечения. Пользовательские компоненты и MonoBehaviour Каждый тип GameObject имеет набор компонентов по умолчанию. Например, пустой GameObject изначально содержит компонент Transform. Этот набор можно расширять пользовательскими компонентами, которые связывают вашу игровую логику непосредственно с объектами в игровых сценах. Можно также дать дизайнерам возможность настраивать значения и поведение через поля компонентов.

Для этого создайте скрипты, а затем добавьте их к GameObject как компоненты. Каждый такой скрипт наследуется от встроенного класса MonoBehaviour. Класс MonoBehaviour можно представить как шаблон для создания компонента нового типа. Каждый раз, когда вы прикрепляете скриптовый компонент к GameObject, по этому шаблону создаётся новый экземпляр соответствующего компонента. Скрипты создаются прямо в Unity. Выберите Assets > Create > C# Script либо щёлкните правой кнопкой мыши и выберите Create > C# Script, чтобы создать новый C#-скрипт на диске. Имя файла должно совпадать с требуемым именем класса. Перетащите скрипт на объект в Hierarchy ExampleScript появится в Inspector как компонент.

Новый C#-скрипт

Примечание. Если изменить имя класса внутри файла, но не имя самого файла, прикреплённый пользовательский компонент может работать неправильно. Unity автоматически создаёт класс ExampleScript, наследующийся от MonoBehaviour.

Текущий шаблон скрипта Unity создаёт класс с двумя функциями:

Start и Update.

C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExampleScript : MonoBehaviour {
    // Start is called before the first frame update

    void Start() { }
    // Update is called once per frame

    void Update() { }
}

Unity вызывает функцию Start до начала игрового процесса и до первого вызова Update. Поэтому Start идеально подходит для настройки переменных, чтения параметров и установления связей с другими GameObject. Функция Update обрабатывает код, выполняемый в каждом кадре: например, перемещение, запуск действий и реакцию на пользовательский ввод. Update и Start - лишь две функции-события MonoBehaviour. Эти встроенные методы выполняются в установленном порядке. Переопределяя функции-события MonoBehaviour, вы формируете игровой процесс и основной игровой цикл. Чтобы познакомиться с основами программирования в Unity, изучите нашу документацию.

Инициализация объектов Опытных программистов может удивить, что объект инициализируется не с помощью конструктора. Объекты конструирует Unity Editor, причём это происходит не в момент начала игрового процесса. Попытка определить конструктор для скриптового компонента нарушает штатную работу Unity и может вызвать ошибки.

Жизненный цикл и структура MonoBehaviour Игровые движки работают на основе бесконечного цикла, который обрабатывает пользовательский ввод, обновляет состояние игры и выводит изображение на экран. В Unity PlayerLoop - низкоуровневый класс, лежащий в основе игрового движка. Он управляет рядом подсистем, отвечающих за инициализацию и покадровые обновления. Чтобы взаимодействовать с PlayerLoop, скрипты наследуются от базового класса MonoBehaviour. Понимание его работы необходимо для создания игрового процесса. На этой блок-схеме показаны функции-события MonoBehaviour и порядок их выполнения в течение жизненного цикла скрипта. Вот основные этапы игрового цикла при работе с MonoBehaviour:

- Первая загрузка сцены - Editor - Перед обновлением первого кадра

- Между кадрами - Порядок обновления - Цикл обновления анимации - Рендеринг - Корутины - При уничтожении объекта - При выходе Опытные пользователи могут даже создавать собственные PlayerLoop и PlayerLoopSystems. Однако рекомендуем начать с изучения класса MonoBehaviour и приведённых ниже наиболее распространённых классов.

Условные обозначения Callback пользователя Внутренняя функция Внутренняя многопоточная функция

Инициализация Reset вызывается, когда скрипт подключён и не выполняется в Play Mode.

Start вызывается один раз для данного скрипта.

Editor Инициализация

Физический цикл может выполняться несколько раз за кадр при малом fixed time step.

Внутреннее обновление анимации

Внутреннее обновление физики

Внутреннее обновление анимации

Физика

События ввода

Корутина продолжает работу, когда завершается операция yield, из-за которой она была приостановлена.

Внутреннее обновление анимации

Игровая логика

Рендеринг сцены

OnDrawGizmos вызывается только при работе в Editor.

OnGUI может вызываться несколько раз за кадр.

Рендеринг Gizmo Рендеринг GUI Конец кадра

OnApplicationPause вызывается после кадра паузы; перед паузой выполняется ещё один кадр.

OnDisable вызывается при отключении скрипта; OnEnable - при его повторном включении.

Пауза

Завершение работы

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

Другие советы по скриптам Бэкенд скриптинга Unity основан на .NET Framework. При разработке скриптов для Unity используйте следующие возможности C#: - Пространства имён. Классы в Unity должны иметь уникальные имена. Когда над одним проектом работают несколько программистов, распространённые имена вроде Controller могут вызвать конфликты. Пространства имён помогают избежать этого и упорядочить типы данных. При необходимости используйте директиву using, чтобы сократить префикс пространства имён. Например, можно создать отдельные пространства имён для Player и Enemy. Тогда Player.Controller и Enemy.Controller смогут безопасно существовать в одном проекте. Подробнее см. на странице руководства Namespaces.

- Корутины. Иногда требуется запустить действие, которое будет выполняться на протяжении нескольких кадров: например, переместить объект из точки A в точку B за определённое время или постепенно изменить его цвет. Обычная функция выполняется до конца и возвращает управление в том же кадре, поэтому реализовать логику, распределённую во времени, сложнее. Корутина может приостановить выполнение, вернуть управление Unity, а в следующем кадре продолжить с места остановки. Корутины позволяют выполнять игровую логику в течение заданного времени. Например, через корутину часто реализуют паузу на определённый интервал. Корутина также может ожидать завершения других корутин; её можно сочетать с циклом while для ожидания условия. Корутины могут работать подобно функции-событию Update в MonoBehaviour, но дополнительно позволяют управлять интервалом обновления. Подробнее см. на странице руководства Coroutine. - Атрибуты. Это маркеры, которые размещают над классом, свойством или функцией, чтобы указать особое поведение. Например, атрибут Range превращает числовое поле в ползунок в Inspector, а Tooltip добавляет к полю всплывающую подсказку. В C# имена атрибутов заключаются в квадратные скобки.

Полный список атрибутов приведён в Scripting API.

Распространённые классы Приступив к написанию скриптов в Unity, изучите наиболее важные встроенные классы. Этот список не исчерпывающий, но поможет начать знакомство с Unity. Полный перечень и дополнительные сведения приведены в Scripting API.

Класс

Описание

GameObject

Тип объектов, которые могут существовать в сцене.

MonoBehaviour

Базовый класс, от которого наследуется каждый скрипт Unity.

Object

Базовый класс для всех объектов, на которые Unity может ссылаться в Editor.

Transform

Управляет положением, поворотом и масштабом GameObject, а также его родительско-дочерними связями.

Vectors

Классы для представления и обработки 2D-, 3D- и 4D-точек, линий и направлений.

Quaternion

Класс для абсолютных и относительных поворотов и операций с ними.

ScriptableObject

Контейнер для хранения больших объёмов данных.

Time

Time

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

Mathf

Набор математических функций, включая тригонометрические и логарифмические.

Random

Средства генерации распространённых типов случайных значений.

Debug

Помогает визуализировать в Editor сведения о выполняемом проекте.

Gizmos and Handles

Средства рисования линий и фигур в Scene и Game, а также создания интерактивных манипуляторов.

Управление памятью Unity поддерживает C# - отраслевой стандарт языка программирования, в некоторых отношениях похожий на Java и C++. C# относится к управляемым языкам: он автоматически выделяет и освобождает память, снижает риск утечек памяти и решает другие задачи управления памятью. В некоторых языках, например C++, программист сам выделяет и освобождает блоки памяти в куче соответствующими вызовами функций. Автоматическое управление памятью в C# требует меньше кода, чем явное выделение и освобождение, и значительно снижает вероятность утечки памяти - ситуации, когда память выделена, но впоследствии не освобождена. Типы значений и ссылочные типы При вызове функции Unity резервирует для неё область памяти и копирует значения её параметров. Типы значений - например, целые числа, числа с плавающей запятой и логические значения - занимают лишь несколько байтов. Unity хранит их непосредственно и копирует при передаче параметров. Другие типы данных, такие как объекты, строки и массивы, относятся к ссылочным. Они занимают больше места, и регулярно копировать их было бы неэффективно. Поэтому Unity хранит их данные в куче и обращается к ним через указатели. Если достаточно структуры (типа значения), она может быть эффективнее класса (ссылочного типа), содержащего те же данные. Хотя явно выделять и освобождать память не требуется, необходимо понимать устройство управляемой кучи и её влияние на производительность игрового приложения. Сборка мусора Блоки памяти в куче считаются «живыми», пока они используются и на них существуют активные ссылки. управляемая куча

Распределитель управляемой памяти автоматически выделяет память в куче.

По мере работы игры ссылки на блок памяти могут исчезать: GameObject уничтожаются, переменным присваиваются новые значения и т. д. Когда на блок памяти больше ничего не ссылается, распределитель управляемой памяти может безопасно использовать его повторно. Распределитель периодически ищет свободные области между живыми блоками памяти. Поиск и освобождение неиспользуемой памяти называется сборкой мусора, или сокращённо GC. Когда игровому приложению требуются новые блоки памяти, распределитель выделяет их из этих свободных областей.

управляемая куча

выделяется новый объект

не помещается

куча расширяется

Сборка мусора освобождает неиспользуемую память, но приостанавливает выполнение кода скриптов.

В Unity используется сборщик мусора Boehm-Demers-Weiser. Во время сборки мусора Unity останавливает выполнение программного кода и возобновляет его только после завершения работы сборщика мусора. Эта пауза может задержать выполнение приложения. Длительность зависит от объёма памяти, который должен обработать сборщик мусора, и от целевой платформы игры: от долей миллисекунды до сотен миллисекунд.

Для приложений реального времени, включая игры, это может стать серьёзной проблемой. Паузы сборщика мусора, называемые скачками GC, способны вызвать подёргивания игрового процесса. Хотя сборка мусора в основном незаметна, она требует значительного процессорного времени.

Учитывайте также, что алгоритм сборщика мусора Boehm не выполняет уплотнение: он не перемещает существующие объекты в памяти, чтобы закрыть промежутки между ними. Это может привести к фрагментации памяти. Если новый объект не помещается в существующие промежутки, распределителю может потребоваться расширить кучу, что способно снизить производительность. управляемая куча

выделяется новый объект

не помещается

куча расширяется

Учитывайте, что куча может расширяться.

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

Unity также предлагает необязательный инкрементный сборщик мусора, распределяющий работу GC между несколькими кадрами. Сейчас эта функция имеет статус Experimental; подробности приведены в этой публикации блога.

Подробнее об управлении памятью и сборке мусора см. в разделах документации Unity Understanding Automatic Memory Management и Understanding the managed heap. Также доступно руководство Memory Management in Unity на сайте Learn.

Многопоточность: C# Job System и компилятор Burst Современные процессоры имеют несколько ядер, но для их использования приложению нужен многопоточный код. Job System в Unity позволяет разбивать крупные задачи на небольшие части, которые параллельно выполняются на дополнительных ядрах процессора. Это может значительно повысить производительность. В традиционной многопоточной программе один поток выполнения процессора - главный - создаёт другие потоки для обработки задач. После завершения работы эти дополнительные рабочие потоки синхронизируются с главным.

Обычная многопоточность

Главный поток

Шкала времени

Главный поток

Новый поток

Новый поток

Поток

Главный поток

зависимость Поток

Рабочие потоки

Главный поток

Шкала времени

При традиционном подходе потоки создаются и уничтожаются. В C# Job System небольшие задания выполняются в пуле потоков.

Этот подход хорошо работает, если имеется несколько длительных задач. Но он менее эффективен для игрового приложения, которому обычно приходится обрабатывать множество коротких задач с частотой 30-60 кадров в секунду. Поэтому Unity применяет несколько иной подход к многопоточности C# Job System. Вместо создания множества короткоживущих потоков работа разбивается на небольшие единицы, называемые заданиями. Задания помещаются в очередь, которая планирует их выполнение в общем пуле рабочих потоков. JobHandles позволяют задавать зависимости и обеспечивают правильный порядок выполнения. Чтобы система безопасности могла предотвращать состояния гонки, задания работают с копиями данных. Затем Native Containers передают результаты обратно в главный поток. Job System дополняет компилятор Burst. С помощью LLVM Burst преобразует байт-код IL/.NET в оптимизированный нативный код. Чтобы получить к нему доступ, достаточно добавить пакет Burst через Package Manager. Burst позволяет разработчикам Unity сохранить удобство использования подмножества C# и при этом повысить производительность. Бэкенды скриптинга в Unity В Unity доступны два бэкенда скриптинга: Mono и IL2CPP (Intermediate Language To C++). Они используют разные методы компиляции: - Mono использует JIT-компиляцию (just-in-time) и компилирует код по мере необходимости во время выполнения. - IL2CPP использует AOT-компиляцию (ahead-of-time) и компилирует всё приложение до его запуска. IL2CPP - разработанный Unity бэкенд скриптинга, который при сборке проектов для некоторых платформ можно использовать вместо Mono. Он способен повысить производительность и уменьшить размер сборки, но часто ценой увеличения времени сборки. При сборке проекта с IL2CPP Unity преобразует IL-код скриптов и сборок в C++, а затем создаёт нативный двоичный файл для выбранной платформы, например .exe, .apk или .xap. Обратите внимание: для сборки под iOS и WebGL доступен только бэкенд скриптинга IL2CPP. Подробнее об использовании IL2CPP см. в серии публикаций The Unity IL2CPP и на странице Building a project using IL2CPP. Скрипты для Editor Чтобы работать эффективнее, среду разработки можно адаптировать к конкретным потребностям команды и проекта.

Если нужен специализированный рабочий процесс, Editor можно расширить собственными инспекторами и окнами. Они могут работать так же, как встроенные окна Inspector, Scene и другие. С помощью пользовательских Property Drawers можно также определять, как отображаются свойства.

Пользовательское окно Editor

Odin Inspector and Serializer Odin Inspector and Serializer позволяет сократить время работы с API EditorWindow. Это сторонний инструмент партнёра Unity Verified Solutions Partner, доступный в Unity Asset Store. Odin содержит более 100 готовых атрибутов, позволяющих создавать пользовательские редакторы без ручного написания и сопровождения GUI-кода.

Чтобы создать пользовательское окно Editor с помощью Odin, унаследуйте свой класс от OdinEditorWindow и снабдите поля, свойства и методы атрибутами. Вот лишь некоторые задачи, которые можно решать с помощью Odin: - Настраивать компоновку с помощью групповых атрибутов, например TabGroup и ToggleGroup - Сериализовать поля, например словари, которые обычно недоступны во встроенном Inspector Unity - Легко создавать кнопки в окне Inspector, добавляя к методам атрибуты Button - Изменять статические члены для тестирования и отладки; например, вызывать статический метод с любыми аргументами непосредственно из Inspector

- Создавать пользовательские редакторы для окна Inspector с помощью атрибутов

- Писать фрагменты C# с выражениями атрибутов непосредственно внутри атрибутов, уменьшая объём шаблонного кода - Проверять пользовательский ввод с помощью таких атрибутов, как Required, ValidateInput и ChildGameObjectsOnly Например, с помощью скрипта можно создать Inspector такого вида:

Пример, созданный в Odin Inspector

Вот пример окна Editor, созданного с помощью Odin:

Окно RPG-редактора, созданное в Odin

Odin Inspector доступен в редакциях Personal и Enterprise через Unity Asset Store.

Поддержка интегрированных сред разработки (IDE) Unity поддерживает несколько IDE, поэтому вы можете работать в предпочитаемой среде разработки. Visual Studio по умолчанию устанавливается вместе с Unity в Windows и macOS. Выберите редактор скриптов в настройках (Unity > Preferences > External

Tools > External Script Editor). Unity изначально поддерживает следующие IDE: - Visual Studio - IDE по умолчанию для Unity в Windows и macOS. В Windows вместе с Unity также устанавливается Visual Studio 2019 Community, а в macOS - Visual Studio for Mac. - Visual Studio Code (Windows, macOS, Linux) - бесплатный, лёгкий и настраиваемый редактор кода с открытым исходным кодом, известный своей скоростью и гибкостью. Подробнее об использовании VS Code с Unity см. в материале Unity Development with VS Code. - JetBrains Rider (Windows, macOS, Linux) построен на основе ReSharper и включает большинство его возможностей. Подробнее см. в документации JetBrains по Rider for Unity. Если выбранный текстовый редактор не входит в этот список и не поддерживается изначально, его может потребоваться настроить для разработки в Unity. Например, сообщество создало множество пакетов плагинов, расширений и дополнений - для использования Sublime Text с Unity. Шаблоны скриптов При создании пользовательских компонентов вы можете заметить, что вносите одни и те же изменения в каждый новый C#-скрипт. Например, может требоваться автоматически удалять функцию-событие Update или добавлять пространство имён по умолчанию. Чтобы сократить ручную работу, настройте шаблон скрипта под текущую задачу.

Unity использует шаблоны из каталога ресурсов ScriptTemplates: - Windows:

C:\Program Files\Unity\Editor\Data\Resources\ScriptTemplates - macOS:

/Applications/Hub/Editor/[version]/Unity/Unity.app/Contents/Resources/Script Templates При необходимости откройте и отредактируйте эти файлы шаблонов, а затем перезапустите Unity Editor, чтобы применить изменения. Обязательно создайте резервные копии как исходных, так и изменённых файлов шаблонов.