Руководство по стилю проекта UE4 от Gamemakin, перевод КоМиГо {
Наиболее разумный подход к Unreal Engine 4
Создание этого руководства было вдохновлено, по большей части, стайл-гайдом Javascript от Airbnb (En).
(От переводчика) Многие вещи будут называться в английском языке, но спокойно мешаться с русскими вариантами. Так, стайл-гайд — это руководство по стилю, ассет — материал для разработки.
Автоматизированный способ проверки соответствия вашего проекта этому руководству можно найти на маркете Unreal Engine. Код плагина станет бесплатен, но чтобы использовать этот плагин без сборки UE4 по его исходникам, понадобится использовать версию с маркета.
Все разделы этого руководства пронумерованы для быстрой адресации. К английской (и самой актуальной) версии руководства можно сослаться, просто добавив хеш-тег ко ссылке http://ue4.style
Например, если вы хотите отправить своему другу ссылку на первый принцип руководства, добавьте #0.1
в конец ссылки — получится http://ue4.style#0.1.
Если вы создали примечательный форк, который не получится организовать в pull-request, отправьте pull request на его добавление сюда. Так же делаем и с переводами.
- Оригинальная, английская версия by Allar
- Корейская версия by ymkim50
- Японская версия by akenatsu
- Китайская версия by Beijing Skylens Tech
Слово 'карта' в основном используется в обозначении слова 'уровень' — эти слова считаем взаимозаменяемыми. То же и с английскими Level и Map. О терминологии уровня смотрите здесь (En) и здесь (RU).
Есть несколько разных способов, как называть вещи. Вот самые популярные методики:
Каждое слово начинается с большой буквы, а всё слово пишется без пробелов, нижних пробелов, дефисов и т.п. Например:
DesertEagle
,StyleGuide
,ASeriesOfWords
.Похож на ДельфиСтиль, но первая буква пишется в нижнем регистре. Например:
desertEagle
,styleGuide
,aSeriesOfWords
.Использование регистра в начале слов не определено, но слова разделяются нижним пробелом. Например:
desert_Eagle
,Style_Guide
,a_Series_of_Words
.
Эти принципы были адаптированы под UE из руководства по стилю idomatic.js (En).
Если вы работаете над существующим проектом, или в команде, которая уже использует руководство стиля, вы должны ему (руководству) следовать. Во всех различиях между имеющимся и новым руководством стиля приоритет отдаётся имеющемуся.
Руководство по стилю, при этом, постоянно меняющийся документ, и рекомендуется предлагать вносить правки в ваше имеющееся руководство по стилю (и в этот документ), если это принесёт реальную пользу сообществу UE.
0.2 Вся структура проекта, материалы для разработки, код в любом Unreal Engine 4 проекте должны выглядеть так, будто всё это создано одним человеком — не важно, как много людей на самом деле участвует
Смена проекта не должна требовать от вас переучиваться под новый стиль и структуру. Использование руководства по стилю избавит вас от гадания на кофейной гуще и неопределённости.
Также, руководство по стилю улучшает продуктивность создателей контента и управляемость проектом, т.к. каждому не требуется думать о стиле — достаточно просто воспользоваться руководством. Это руководство по стилю разрабатывается с учётом лучших практик, и его использование снизит частоту появления трудно отслеживаемых проблем.
Если вы видите, что кто-то работает не по вашему руководству стиля, или вовсе без какого-либо — объясните это тому человеку.
При работе в комаде и во время обсуждений в духе Unreal Slackers (EN) постоянство стиля также помогает быстро найти отклик на свою проблему. Никому не хочется рыться в макаронах блюпринтов и изучать непонятные названия материалов для разработки.
Если вы помогаете тому, кто следует другому, но постоянному и понятному руководству стиля, вы сможете к нему адаптироваться. Если вы увидили, что этот кто-то не следует какому-либо руководству стиля, отправьте ему ссылку на этот документ.
Одним из первых ваших вопросах при вступлении в команду разработчиков на UE4 должен быть "У вас есть руководство стиля?" Если его нет, то следует с подозрением относиться ко способности этих людей работать в команде.
Соглашение о наименованиях должно восприниматься как закон. В проекте, следующем соглашению о наименованиях, легко управлять ассетами, искать и обрабатывать их.
В большинстве случаев, ассетам присваивается префикс-аббревиатура на основе названия типа этого ассета + нижний пробел _
.
Использование русских и иных языков в названиях ассетов строго запрещается. Нет ничего хуже, чем пытаться понять проект с французскими названиями, когда вы не владеете таким языком. Точно так же будет плохо зарубежным друзьям при разборе вашего проекта.
Такие проекты нельзя будет передавать лицам из других стран, когда вам потребуется помощь. Также, не-латинские символы могут вызвать ошибки при компиляции, преобразовании и в ходе других внутренних процессов UE4 и сторонних плагинов — такие ошибки будет трудно отловить и исправить, так что лучше не делайте их вообще.
Не бойтесь пользоваться переводчиком. Лучше чудаковатое название на английском, нежели написанное транслитом слово Pchela
, или, того хуже, так и оставленное на русском Пчела
.
У каждого ассета должно быть своё базовое название. Оно используется как средство логической группировки материалов разработки. Любой ассет, являющийся частью определённой логической группы, должен следовать стандарту Префик_БазовоеНазвание_Вариация_Суффикс
.
Использование схемы Префик_БазовоеНазвание_Вариация_Суффикс
с её осознанием уже гарантирует создание "хороших" имён. Вот подробные правила по использованию этой схемы:
Префикс
иСуффикс
определяется типом ассета, исходя из таблиц Модификаторов имён ассетов.Базовое название
определяется коротким и легко отличимым названием касательно предметной области вашей группы ассетов. Например, если у вас есть персонаж Вася, то все ассеты Васи должны содержатьБазовоеНазвание
=Vasya
.- Для особенных или уникальных вариантов ассета используйте
Вариант
— короткое и легко различимое имя, отражающее логическую группировку ассетов, образующих подмножество на основе базового имя ассета. Например, если у Васи есть несколько скинов, то у этих скинов должно быть всё то же базовое имяVasya
, но также иВариант
. "Злой" скин можно назватьVasya_Evil
, а скин "Ретро"—Vasya_Retro
. - Для уникальных но схожих ассетов, относящихся к одной группе, добавляется нумерующий
Вариант
— двузначное число, нумерация которого начинается с01
. Например, если ваш художник окружения генерирует валуны, которые нельзя просто так взять и назвать, то их можно назвать какRock_01
,Rock_02
,Rock_03
, и т.д. За исключением редких случаев, не используйте трёхзначное число. Если у вас более 100 ассетов в одной группе, вам следует разбить её на несколько других, используя другие базовые названия или варианты. - В зависимости от того, как у вас создаются ассеты, вы можете использовать несколько имён-вариантов, друг за другом. Например, если вы создаёте ассеты для пола, вам будет нужно использовать базовое имя
Flooring
с вариантами вродеFlooring_Marble_01
,Flooring_Maple_01
,Flooring_Tile_Squares_01
.
Тип ассета (RU) | Тип ассета (EN) | Название ассета |
---|---|---|
Скелетный меш | Skeletal Mesh | SK_Bob |
Материал | Material | M_Bob |
Текстура (Diffuse/Albedo) | Texture (Diffuse/Albedo) | T_Bob_D |
Текстура (Normal) | Texture (Normal) | T_Bob_N |
Текстура (Злой Diffuse) | Texture (Злой Diffuse) | T_Bob_Evil_D |
Тип ассета (RU) | Тип ассета (EN) | Название ассета |
---|---|---|
Статичный меш (01) | Static Mesh (01) | S_Rock_01 |
Статичный меш (02) | Static Mesh (02) | S_Rock_02 |
Статичный меш (03) | Static Mesh (03) | S_Rock_03 |
Материал | Material | M_Rock |
Экземпляр материала (Снег) | Material Instance (Snow) | MI_Rock_Snow |
Используйте эти таблицы наряду с базовым названием, когда именуете ассеты.
1.2.1 Часто используемые
1.2.2 Анимация
1.2.3 Искусственный интеллект
1.2.4 Блупринты
1.2.5 Материалы
1.2.6 Текстуры
1.2.7 Разное
1.2.8 Paper 2D
1.2.9 Физика
1.2.10 Звуки
1.2.12 Эффекты
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Карта / уровень | Level / Map | Должны быть в папке Maps . |
||
Уровень (постоянный) | Level (Persistent) | _P | ||
Уровень (аудио) | Level (Audio) | _Audio | ||
Уровень (освещение) | Level (Lighting) | _Lighting | ||
Уровень (геометрия) | Level (Geometry) | _Geo | ||
Уровень (геймплей) | Level (Gameplay) | _Gameplay | ||
Блупринт | Blueprint | BP_ | ||
Материал | Material | M_ | ||
Статичный меш | Static Mesh | S_ или SM_ | Выберите одно. Лучше S_. | |
Скелетный меш | Skeletal Mesh | SK_ | ||
Текстура | Texture | T_ | _? | См. Текстуры |
Система частиц | Particle System | PS_ | ||
Виджет-блупринт | Widget Blueprint | WBP_ или WB_ | Выберите одно. Лучше WBP_. |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Сдвиг прицела | Aim Offset | AO_ | ||
Сдвиг прицела 1D | Aim Offset 1D | AO_ | ||
Блюпринт анимации | Animation Blueprint | ABP_ | ||
Композиция анимации | Animation Composite | AC_ | ||
Монтаж анимации | Animation Montage | AM_ | ||
Последовательность анимаций | Animation Sequence | A_ or AS_ | Выберите одно. Лучше A_. | |
Пространство смешивания | Blend Space | BS_ | ||
Пространство смешивания 1D | Blend Space 1D | BS_ | ||
Последовательность уровня | Level Sequence | LS_ | ||
Точка смешивания | Morph Target | MT_ | ||
Paper Flipbook | Paper Flipbook | PFB_ | ||
Риг | Rig | Rig_ | ||
Скелетный меш | Skeletal Mesh | SK_ | ||
Скелет | Skeleton | SKEL_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
ИИ контроллер | AI Controller | AIC_ | ||
Дерево поведений | Behavior Tree | BT_ | ||
Доска состояний | Blackboard | BB_ | ||
Декторатор | Decorator | BTDecorator_ | ||
Сервис | Service | BTService_ | ||
Задание | Task | BTTask_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Блупринт | Blueprint | BP_ | ||
Библиотека блупринт-функций | Blueprint Function Library | BPFL_ | ||
Блупринт-интерфейс | Blueprint Interface | BPI_ | ||
Библиотека бупринт-макросов | Blueprint Macro Library | BPML_ | По возможности не используйте библиотеки макросов вообще | |
Перечисление | Enumeration | E | Без нижнего пробела | |
Структура | Structure | F или S | Без нижнего пробела | |
Блупринт-виджет | Widget Blueprint | WBP_ или WB_ | Выберите одно. Лучше WBP_ |
Тип ассета (RU) | Тип ассета (En) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Материал | Material | M_ | ||
Материал пост-обработки | Material (Post Process) | PP_ | ||
Функция материалов | Material Function | MF_ | ||
Экземпляр материала | Material Instance | MI_ | ||
Материал Parameter Collection | Material Parameter Collection | MPC_ | ||
Профиль подповерхности | Subsurface Profile | SP_ или SSP_ | Выберите одно. Лучше SP_ | |
Физический материал | Physical Materials | PM_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Текстура | Texture | T_ | ||
Текстура (Diffuse/Альбедо/Основной цвет) | Texture (Diffuse/Albedo/Base Color) | T_ | _D | |
Текстура (Нормаль) | Texture (Normal) | T_ | _N | |
Текстура (Грубость) | Texture (Roughness) | T_ | _R | |
Текстура (Alpha/Прозрачность) | Texture (Alpha/Opacity) | T_ | _A | |
Текстура (Нейтральный свет) | Texture (Ambient Occlusion) | T_ | _O или _AO | Выберите одно. Лучше _O |
Текстура (Неровность) | Texture (Bump) | T_ | _B | |
Текстура (Излучение) | Texture (Emissive) | T_ | _E | |
Текстура (Маска) | Texture (Mask) | T_ | _M | |
Текстура (Блеск) | Texture (Specular) | T_ | _S | |
Текстура (упакованная) | Texture (Packed) | T_ | _* | См. примечание об упаковке текстур. |
Текстура-куб | Texture Cube | TC_ | ||
Текстура-медиа | Media Texture | MT_ | ||
Область прорисовки | Render Target | RT_ или RTT_ | Выберите одно. Лучше RT_ | |
Область прорисовки текстуры-куба | Cube Render Target | RTC_ | ||
Профиль освещения | Texture Light Profile | TLP |
<a name="anc-textures-packing"
Упаковка сразу нескольких слоёв информации в одну текстуру — стандартная практика. Примером служит упаковка текстур Emissive, Roughness, Ambient Occlusion как красный, зелёный и синий каналы одной текстуры. Чтобы построить суффикс для таких текстур, просто последовательно запишите суффиксы отдельных масок из таблицы выше, напр. _ERO
.
Часто альфа-канал включают в карту Diffuse/Альбедо. Так как это стандартная практика, добавлять суффикс
A
в суффикс_D
необязательно.
Упаковывать сразу 4 канала информации в одну текстуру (в RGBA) не рекомендуется, за исключением использования канала A как Alpha вместе с картой Diffuse/Альбедо.
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Анимированное векторное поле | Animated Vector Field | VFA_ | ||
Анимация камеры | Camera Anim | CA_ | ||
Цветовая кривая | Color Curve | Curve_ | _Color | |
Табличная кривая | Curve Table | Curve_ | _Table | |
Набор данных | Data Asset | *_ | Префикс основывается на классе | |
Таблица данных | Data Table | DT_ | ||
Вещественная кривая | Float Curve | Curve_ | _Float | |
Тип растительности | Foliage Type | FT_ | ||
Эффект физического отклика | Force Feedback Effect | FFE_ | ||
Тип растительности | Landscape Grass Type | LG_ | ||
Слой ландшафта | Landscape Layer | LL_ | ||
Данные Matinee | Matinee Data | Matinee_ | ||
Медиапроигрыватель | Media Player | MP_ | ||
Библиотека объектов | Object Library | OL_ | ||
Перенаправление | Redirector | Перенаправление должно быть исправлено при первой возможности | ||
Атлас спрайтов | Sprite Sheet | SS_ | ||
Статичное векторное поле | Static Vector Field | VF_ | ||
Настройка тач-интерфейса | Touch Interface Setup | TI_ | ||
Векторная кривая | Vector Curve | Curve_ | _Vector |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Набор кадров | Paper Flipbook | PFB_ | ||
Спрайт | Sprite | SPR_ | ||
Группа атласов спрайтов | Sprite Atlas Group | SPRG_ | ||
Карта тайлов | Tile Map | TM_ | ||
Тайлсет | Tile Set | TS_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Физический материал | Physical Material | PM_ | ||
Физический ассет | Physical Asset | PHYS_ | ||
Разрушаемый меш | Destructible Mesh | DM_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Голос диалога | Dialogue Voice | DV_ | ||
Запись диалога | Dialogue Wave | DW_ | ||
Звукозапись медиа | Media Sound Wave | MSW_ | ||
Эффект ревербации | Reverb Effect | Reverb_ | ||
Затухание звука | Sound Attenuation | ATT_ | ||
Класс звука | Sound Class | Без префиксов/суффиксов. Должен быть в отдельной папке SoundClasses |
||
Очерёдность звуков | Sound Concurrency | _SC | Должен быть назван на основе SoundClass |
|
Композиция звуков | Sound Cue | A_ | _Cue | |
Микс звуков | Sound Mix | Mix_ | ||
Звукозапись | Sound Wave | A_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Шрифт | Font | Font_ | ||
Кисть Slate | Slate Brush | Brush_ | ||
Стиль виджета Slate | Slate Widget Style | Style_ | ||
Виджет-блупринт | Widget Blueprint | WBP_ или WB_ | Выберите одно. Лучше WBP_ |
Тип ассета (RU) | Тип ассета (EN) | Префикс | Суффикс | Примечания |
---|---|---|---|---|
Система частиц | Particle System | PS_ | ||
Материал постобработки | Material (Post Process) | PP_ |
Структура папок также должна восприниматься как закон. Она проекта так же важна, как и соглашение о наименованиях. Они тесно связаны, и нарушение правил любого из них приводит к ненужному хаосу в проекте.
Есть несколько способов организации контента UE4 проекта, но в этом гайде мы будем использовать структуру, которая основывается больше на возможности поиска и фильтрации в Content Browser — вместо того, чтобы под каждый тип материалов проекта создавать отдельную папку, будет использоваться фильтр/поиск в UE.
Если вы используете соглашение о наименованиях выше, то использование папок вроде
Meshes
,Textures
, иMaterials
будет избыточным действием, т.к. все ассеты уже отсортированы по префиксу и могут быть отфильтрованы в content browser.
|-- Content |-- GenericShooter |-- Art | |-- Industrial | | |-- Ambient | | |-- Machinery | | |-- Pipes | |-- Nature | | |-- Ambient | | |-- Foliage | | |-- Rocks | | |-- Trees | |-- Office |-- Characters | |-- Bob | |-- Common | | |-- Animations | | |-- Audio | |-- Jack | |-- Steve | |-- Zoe |-- Core | |-- Characters | |-- Engine | |-- GameModes | |-- Interactables | |-- Pickups | |-- Weapons |-- Effects | |-- Electrical | |-- Fire | |-- Weather |-- Maps | |-- Campaign1 | |-- Campaign2 |-- MaterialLibrary | |-- Debug | |-- Metal | |-- Paint | |-- Utility | |-- Weathering |-- Placeables | |-- Pickups |-- Weapons |-- Common |-- Pistols | |-- DesertEagle | |-- RocketPistol |-- Rifles
Предпосылки формирования именно такой структуры проекта описаны ниже.
2.1 Названия папок
2.4 Карты
2.5 Ядро игры
2.7 Большие наборы
Все названия папок подчиняются единому набору правил.
2.1.1 Всегда используйте ДельфиСтиль*
ДельфиСтиль означает написание каждого слова строки с большой буквы, без пробелов и нижних пробелов. Например, DesertEagle
, RocketPistol
, и ASeriesOfWords
.
См. Использование верхнего и нижнего регистра.
В дополнение к 2.1.1, не используйте пробелы. Пробелы могут вызвать падения различных средств компиляции и пакетной обработки. В идеале, путь к самому проекту тоже не должен содержать пробелы и быть расположен в папке вроде D:\Project
, а не C:\Пользователи\Моё имя\Мои документы\Unreal Projects
.
(Прим. пер.) Это также касается использования русского языка.
Если одного из ваших персонажей зовут 'Zoë', его папка должна называться Zoe
. Символы юникода могут быть хуже пробелов для инженерных инструментов, а некоторые инструменты UE не поддерживают символы юникода и в путях к файлам.
Если ваш проект страдает по необъяснимым причинам от непонятных ошибок, а имя пользователя вашего компьютера содержит символы юникода (в т.ч. и русские буквы), то любой проект в папке Мои документы
будет страдать аналогичным образом. Чаще всего достаточно переместить папку проекта в, скажем, D:\Project
, и эти загадочные ошибки исчезнут.
Использование символов, отличных от a-z
, A-Z
, и 0-9
, напр. @
, -
, _
, ,
, *
, и #
тоже может привести к неожиданным и трудно отслеживаемым последствиям на других платформах, в системах контроля версиями и неотшлифованных средствах разработки.
Все материалы разработки вашего проекта должны располагаться в папке, названной согласно самому проекту. Например, если проект называется 'Generic Shooter', то всё его содержимое должно лежать в папке Content/GenericShooter
.
Папка
Developers
не для материалов разработки вашего проекта, и потому не относится к папке проекта. См. 2.2 для подробного обоснования.
Есть несколько причин использования такого подхода.
В стайл-гайдах по кодингу часто пишут о том, что вы не должны засорять глобальное пространство имён. Этот пункт следует этому же принципу. Если ассет существует вне проекта, велик риск роста несоблюдения структуры проекта, т.к. такой ассет становится дурным примером для других.
У каждого материала разработки должна быть своя цель, иначе он не принадлежит вашему проекту. Если ассет экспериментальный и не должен использоваться в проекте, его лучше положить в папку Developer
.
При работе над несколькими проектами команда нередко копирует ассеты из проекта в проект, если эти ассеты подходят и полезны сразу двум проектам. В таких случаях лучше произвести копию ресурсов через инструмент Migrate в Content Browser, т.к. он не только копирует сам ассет, но и все его зависимые ресурсы.
Эти зависимости легко генерируют проблемы при слиянии. Если у ваших двух проектов нет одноимённых с названием проекта папок в верхнем уровне контента, то ассеты с аналогичным названием (а то и уже адаптированные из другого проекта) могут быть уничтожены инструментом миграции.
К слову, это главная причина, по которой Epic требует такой же схемы в товарах Магазина.
После миграции, безопасное слияние ассетов может быть сделано через инструмент 'Replace References' content browser-а. После завершения миграции и слияния, лишняя папка на верхнем уровне проекта должна быть удалена. Это гарантирует 100% безопасность ваших миграций.
Скажем, вы сделали главный материал-шаблон в своём проекте и хотели бы использовать его в другом. Для этого вы мигрировали его. Если этот ассет не в верхней папке проекта, у него может быть название вроде Content/MaterialLibrary/M_Master
, и, если в целевом проекте ещё нет такого главного материала, то ошибки не возникнет.
В ходе разработки проекта каждый такой главный материал дорабатывается под нужды конечного проекта.
Проблема возникает, когда, скажем, художник из одного проекта сделал симпатичный модульный набор статичных мешей, и кто-нибудь захотел включить этот набор в другой проект. Если художник — создатель набора — использовал экземпляры материала на основе главного Content/MaterialLibrary/M_Master
, то при миграции возможна ошибка слияния с ранее мигрировавшим ассетом Content/MaterialLibrary/M_Master
.
Эту ошибку трудно предупредить и объяснить. Человек, мигрирующий набор мешей, может быть не наслышан о разработке обоих главных материалов, и потому вовсе не осведомлён о том, что меши зависят от экземпляров материалов, которые, в свою очередь, зависят от главного материала. Но инструмент Migrate должен собрать всю сеть зависимостей с целью последующей работоспособности мигрированных ассетов, и поэтому захватит Content/MaterialLibrary/M_Master
с исходного проекта, чем перезапишет аналогичный файл в конечном проекте.
Если эти материалы не совместимы в принципе, то велик риск обрушить всю библиотеку материалов вашего проекта. А ведь всё по причине отсутствия папки для проекта на верхнем уровне! Так простое перемещение мешей может наделать массу лишней работы.
В дополнение к 2.2.2: если член команды решит добавить пример, файлы шаблона или товар с Магазина, эти ассеты гарантированно не будут пересекаться с файлами вашего проекта, т.к. у них всех есть своя папка верхнего уровня (в том случае, если имена между всеми ними и вашим проектом уникальны).
Тем не менее, нельзя на 100% доверять товарам с Магазина — не все из них следуют правилу папки верхнего уровня. Существует множество ассетов, где большая часть контента которых расположена в папке верхнего уровня, но при этом также используется контент из примеров движка (напр., шаблоны вроде FirstPersonShooter или Starter Content Pack). Эти ассеты из примеров могут быть изменены под нужды товара. Также, карты из этих примеров засоряют корневую папку Content
и тоже могут пересекаться.
Если придерживаться правилу 2.2, худшим сценарием будет использование нескольких товаров с магазина, которые зависят от одного и того же проекта-шаблона. Они могут повредить друг друга, но если именно ваши ассеты располагаются в отдельной папке ресурсов проекта, такой конфликт не затронет ваши наработки.
Если вы планируете выпустить DLC для своего проекта, или же в нём есть несколько связанных подпроектов, все ассеты этих DLC и подпроектов должны находиться в своих папках верхнего уровня. Такие подпроекты затем могут быть мигрированы из проекта или просто не участвовать в сборке. Техника создания индивидуальных папок верхнего уровня помогает с лёгкостью исключить DLC из выпечки основного проекта. Подпроекты также мигрируются с наименьшими усилиями. А если вам нужно в патче, например, заменить один материал или ввести специфичный ассет, меняющий поведение игры, но при этом вы не хотите ломать главный проект, все эти изменения тоже можно вывести в отдельную папку.
Очень часто во время разработки проекта участники команды создают, в некотором роде, "песочницу", где они проводят свои эксперименты без влияния на главный проект. Так как такая практика осуществляется на постоянной основе, члены команды могут пожелать загрузить их ассеты в систему контроля версий. Не все команды требуют использования папки Developers
, но те, что её используют, часто испытывают одни и те же проблемы с загруженными в систему контроля версиями ассетами.
Участники команды могут по случайности начать использовать те ассеты, которые ещё не готовы к этому, а затем, при их удалении, вызывают ошибки. Например, художник может дорабатывать набор модульных статичных мешей и всё ещё корректирует их размерность и соответствие сетке. Если левел-дизайнер обнаружит такой набор в главной папке проекта, он может использовать набор во всём уровне, не зная, что этот набор позднее будет претерпевать серьёзные изменения (не исключая удаление набора). Следствием этой проблемы будет масса переделок для всей команды.
Если эти модульные меши располагаются в папке разработчиков, у левел-дизайнера не было бы причин использовать этот набор, и проблемы бы не возникло. У Content Browser есть специальная настройка в View Options, которая прячет папки разработчиков (и по умолчанию она включена), что делает случайное использование экспериментальных ресурсов невозможным.
Как только ассеты готовы, художнику достаточно переместить эти материалы разработки в соответствующую папку проекта и подфиксить редиректоры. По сути, это "продвижение" ассетов из экспериментальной зоны в продакшн.
2.4 Все уровни* располагаются в отдельной папке под названием Maps
Файлы уровней отличаются от других, и нормальной практикой считается использование своей системы наименований, особенно, если команда работает с подуровнями или стримингом карт. Не важно, как организуются эти карты в вашем проекте — все они должны быть расположены в папке /Content/Project/Maps
.
Возможность открыть определённую карту без необходимости объяснять, где она находится, серьёзно экономит время. Часто карты группируются в папки вроде Maps/Campaign1/
или Maps/Arenas
, но главное, что все они находятся в папке /Content/Project/Maps
.
Такой подход также облегчает работу инженеров по сборке. Когда все карты в одной папке, не нужно перерывать весь проект в поиске предмета сборки/исключения из сборки. Становится труднее случайно не подготовить карту к сборке. Польза проявляется и при запуске скриптов для запекания освещения, а также в ходе процессов контроля качеством.
Используйте папку /Content/Project/Core
для тех ассетов, которые являются основой разработки вашего проекта. Например, основные GameMode
, Character
, PlayerController
, GameState
, PlayerState
и связанные блупринты должны находиться именно здесь.
Это как знак "не трогай" для других участников команды. У не-инженеров обычно нет особых причин заглядывать в эту папку. При соблюдении хорошего стиля кодинга дизайнеры смогут выполнять все изменения, касающиеся геймплея, в дочерних классах. Левел-дизайнеры должны использовать заранее приготовленные блупринты в специально отведенных папках, вместо того, чтобы всё время использовать и модифицировать базовые классы.
Например, если в вашем проекте есть пикапы, которые могут быть расположены в уровне, для них должен быть базовый класс в папке Core/Pickups
. Такой класс будет описывать базовое поведение пикапа. Конкретные пикапы, вроде аптечки или патронов уже будут располагаться в папке /Content/Project/Placeables/Pickups/
. Гейм-дизайнеры могут спокойно редактировать эти пикапы по своему желанию, но они не должны трогать папку Core/Pickups
, т.к. это косвенно может повредить работоспособность всех пикапов вообще.
Все ассеты и так являются ассетами.
Все ассеты именуются с учётом их типа, и их названия уже включают указание типа в префиксе. Такие папки только добавляют избыточности в проект и легко заменяются фильтрами в Content Browser.
Для того, чтобы вывести только статичные меши в папке Environment/Rocks/
, достаточно включить фильтр Static Mesh. Если все ассеты названы правильно, то они также будут отсортированы в алфавитном порядке, независимо от префиксов. Чтобы вывести и статичные, и скелетные меши, включите сразу два фильтра. Так вам не понадобится зажимать Ctrl
и выделять несколько папок в проводнике Content Browser-а.
Есть и небольшое преимущество в длине строки: префикс
S_
(статичный меш) — всего 2 символа, а папкаMeshes/
— семь.
Отсутствие таких папок устраняет ошибки с помещением статичных мешей в папку материалов.
Это псевдо-исключение к правилу 2.6.
Есть определённые виды ассетов, которые образуют большой объём связанных по смыслу файлов, но каждый такой файл обладает своей уникальной целью. Чаще всего это папки с анимациями и аудио. Мы можем добавить новый уровень иерархии в папке тогда, когда эти подуровни образуют группы в 15+ семантически связанных файлов. Не забывайте делать этим подуровням чёткие и наполненные смыслом названия.
Например, есть анимации, которые используются несколькими персонажами — они должны находиться в папке Characters/Common/Animations
, где могут быть поддиректории Locomotion
или Cinematic
.
Это правило неприменимо к ассетам вроде текстур или материалов. Это норма, когда у папки
Rocks
огромное количество текстур, соответствующее аналогичному количеству камней; при этом каждая из этих текстур относится к небольшому количеству мешей и должна быть названа соответствующим образом. Не являются исключением и текстуры в библиотеке материалов.
Если ваш проект использует главные материалы (материалы-шаблоны), слоистые материалы, или любую другую форму многократно используемых материалов и текстур, которые не принадлежат какому-нибудь подмножеству ассетов, они должны быть расположены в папке Content/Project/MaterialLibrary
.
Так все "глобальные" материалы будут находиться в одном легкодоступном месте.
Это также способствует политике "только экземпляры материалов" ('material instances only') в вашем проекте. Если все художники и рабочие материалы используют экземпляры материалы, то обычные материалы будут существовать только в пределах папки. Проверить соблюдение этой политики можно просто поиском основных материалов в папках, отличных от
MaterialLibrary
.
Папка MaterialLibrary
необязательно должна состоять только из материалов. Общие текстуры-помощники, функции материалов и другие аналогичные ассеты могут храниться в этой же папке. Мы также можем объединять их в семантические группы папками. Например, общие текстуры с шумом можно объединить в папке MaterialLibrary/Utility
.
Все материалы, предназначенные для тестирования и/или отладки должны располагаться в папке MaterialLibrary/Debug
. Это позволяет убрать такие материалы при сборке релизной версии, а если они используются в версии для сборки, сообщение об ошибке укажет на эти связи.
Этот раздел направлен на блупринты и их внутреннее обустройство. Где возможно, эти правила подчиняются стандарту кодинга Epic.
3.1 Компиляция
3.2 Переменные
Все блупринты должны компилиться без предупреждений и ошибок. Вы должны исправить все предупреждения и ошибки при первой возможности, т.к. они сразу могут превратиться в снежный ком проблем и вызвать непредвиденное поведение.
Никогда не отправляйте (submit) сломанные блупринты в систему контроля версиями. Если вам нужно хранить их в системе контроля версий, отложите их (shelve).
Сломанные блупринты могут вызвать массу проблем — таких, как поломанные связи, некорректное поведение, падения при сборке, частые ненужные перекомпиляции. Один сломанный блупринт способен порушить всю вашу игру.
3.2.1 Именование
3.2.2
Editable
3.2.3 Категории
3.2.4 Уровни доступа
3.2.5
Advanced
3.2.6
Transient
3.2.7
SaveGame
3.2.8 Переменные
Config
Все не-булевые переменные должны быть чёткими, недвусмысленными, описательными существительными.
Все не-булевые переменные должны быть написаны в ДельфиСтиле.
Score
Kills
TargetPlayer
Range
CrosshairColor
AbilityID
Все булевы значения должны следовать ДельфиСтилю, но иметь префикс в виде малой b
.
Например: bDead
и bEvil
, но не Dead
и Evil
.
Редактор блупринтов в UE4 распознаёт эту b
и не выводит её при выводе удобочитаемого текста.
Все булевые переменные должны быть качественными прилагательными. Не включайте вопросительные слова, например, Is
. Такие слова зарезервированы для функций.
Например: используйте bDead
и bHostile
, но не bIsDead
и bIsHostile
.
Старайтесь также не использовать в названиях глаголы (напр. bRunning
). Глаголы, как правило, ведут к сложным состояниям.
Не используйте булевы переменные для описания сложных и/или зависимых состояний. Это приводит к затруднённому управлению этих состояний и нечитабельности. Вместо этого используйте перечисление (Enum).
Например: описывая пушку, не используйте bReloading
и bEquipping
, если пушка не может одновременно заряжаться и быть в процессе экипировки. Обозначьте перечисление EWeaponState
и используйте вместо булевых переменных одну переменную WeaponState
соответствующего типа. Так добавлять новые состояния пушке будет проще.
Пример: не используйте bRunning
("Бежит"), если вам также нужны bWalking
("Ходит") и bSprinting
("Спринтует", кратковременное ускорение). Вам нужно перечисление с чёткими названиями вариантов.
Все названия переменных не должны быть избыточны и упоминать свой контекст, так как все переменные из блупринта уже обладают своим контекстом.
Пусть у нас есть блупринт BP_PlayerCharacter
.
Нельзя
PlayerScore
PlayerKills
MyTargetPlayer
MyCharacterName
CharacterSkills
ChosenCharacterSkin
Названия этих переменных избыточны. Если переменные описаны в BP_PlayerCharacter
, то это значит, что они характеризуют именно BP_PlayerCharacter
.
Надо
Score
Kills
TargetPlayer
Name
Skills
Skin
Атомарные, или примитивные, переменные — это такие переменные, что описывают своё значение в простейшей форме — напимер, в виде булевого значения, целочисленного, вещественного, перечисления (Enum).
Строки (именно String, не Text!), Rotator и векторы тоже считаются атомарными в блупринтах и тоже не должны включать названия типа в своих именах. Тем не менее, с технической точки зрения они не являются атомарными.
Хоть вектора и состоят каждый из трёх вещественных значений, с векторами часто работают как с единым целым. То же и с Rotator.
Нельзя считать Text атомарным типом, т.к. они включают в себе скрытый функционал по локализации. Атомарным типом является
String
, но неText
.
Атомарные переменные не должны включать название типа переменной в своём названии.
Например: используйте Score
, Kills
и Description
, но не ScoreFloat
, FloatKills
, DescriptionString
.
Единственное исключение этого правила — когда имеется ввиду "количество того-то" и когда использование имени без типа переменной приводит к затруднению чтения.
Например: генератор забора создаёт X досок. Это X нужно сохранить в переменной NumBoards
или BoardsCount
, но не Boards
, т.к. Boards
может уже относиться к массиву переменных типа Board
.
Неатомартные, или сложные, переменные — это такие, что отражают свою информацию как набор атомарных переменных. Структуры, классы, интерфейсы, примитивы со скрытым поведением (вроде Text
и Name
) попадают под это правило.
В то время, как массив атомарных значений — это список атомарных переменных, массивы не меняют "атомарность" типа переменных.
Эти переменные должны включать названия их типов, но также и подразумевать контекст использования.
Если класс владеет экземпляром сложной переменной, напр. если у BP_PlayerCharacter
есть BP_Hat
, то она должна называться как тип переменной, без каких-либо изменений.
Например: используйте Hat
, Flag
, и Ability
, но не MyHat
, MyFlag
, или PlayerAbility
.
Если класс не владеет значением сложной переменной, то нужно использовать существительное вместе с названием типа переменной.
Например: если у BP_Turret
есть способность нацеливаться на BP_PlayerCharacter
, она (туррель) должна хранить переменную TargetPlayer
— так в контексте BP_Turret
будет понятно, что это ссылка на другую переменную сложного типа, которой туррель не владеет.
Массивы подчиняются правилам выше, но описываются во множественном числе.
Например: используйте Targets
, Hats
и EnemyPlayers
, но не TargetList
, HatArray
, EnemyPlayerArray
.
Все переменные, которые спокойно можно менять с целью настройки поведения блупринта, должны быть отмечены как Editable
.
Аналогично, все те переменные, которые небезопасно редактировать и которые не должны быть раскрыты дизайнерам, не должны быть отмечены как Editable
, за исключением тех случаев, когда переменная требует флага Expose On Spawn
.
Не помечайте переменные флагом Editable
произвольным образом.
Все переменные типа Editable
, включая те, что были помечены так только из-за флага Expose On Spawn
, должны иметь своё описание в поле Tooltip
, которое должно показывать, как изменение этого значения меняет поведение блупринта.
Все переменные с флагом Editable
должны использовать слайдеры (Slider) и пределы допустимых значений (Value Range), если есть хоть какое-нибудь значение, которое не должно использоваться.
Пример: блупринт, генерирующий забор, может иметь редактируемую переменную BoardsCount
, и значение -1 не будет являться для него рабочим. Используйте пределы допустимых значений, чтобы обозначить 0 как минимальное значение.
Если редактируемая переменная используется в Construction Script, у неё должен быть настроенный слайдер (Slider Range), который препятствует назначению таких значений, что могут обрушить редактор.
Пределы допустимых значений нужно устанавливать только тогда, когда известны границы этих значений. Если слайдер предотвращает случайный ввод слишком больших значений, то неустановленное значение пределов всё же позволяет указать значения вне диапазона слайдера — "опасные", но всё ещё валидные.
Если у класса совсем немного переменных, то использование категорий не требуется.
Если у класса есть некоторое количество переменных (5-10), все переменные с флагом Editable
должны обладать своей нестандартной категорией. Для общих переменных создаётся категория Config
.
Если у класса большое количество переменных, то все переменные с флагом Editable
должны быть помещены в подкатегории внутри Config
. Нередактируемые переменные (без флага Editable
) должны быть помещены в отдельные категории с понятными названиями.
Вы можете создавать подкатегории, используя символ вертикальной черты
|
, напр.Config | Animations
.
Пример: набор переменных оружия может быть расположен в следующей иерархии:
|-- Config
| |-- Animations
| |-- Effects
| |-- Audio
| |-- Recoil
| |-- Timings
|-- Animations
|-- State
|-- Visuals
В C++ есть реализация уровня доступа переменных. Уровень доступа Public
означает, что все экземпляры любых классов могут получить доступ к этой переменной. Переменные Protected
могут использованы только самим классом и дочерними классами. Private
значит, что только сам класс может видеть эти переменные (дочерние не видят).
В блупринтах, по крайней мере — на данный момент —, нет реализации уровня доступа переменных.
Считайте переменные с флагом Editable
как публичные. Переменные без этого флага считайте как Protected
.
В том случае, если не известно, должна ли быть переменная доступна только в самом классе, но не в дочерних, не отмечайте переменную как Private
. Используйте Protected
и закрывайте переменную только тогда, когда вы абсолютно уверены, что хотите ограничить использование переменных в дочерних классах.
Если переменная должна быть редактируема, но, в большинстве случаев, нетронута, отметьте её флагом Advanced Display
. Переменная будет скрыта, но может быть отображена по клику стрелки в конце категории.
Сам флаг Advanced Display
тоже является скрытым за этой стрелкой на панеле Details
.
Все переменные без флага Editable
и которые должны обладать нулевым начальным значением, должны быть помечены как Transient
.
Такие переменные не требуют сохранения и загрузки их значения и изначально равны нулю (zero или null). Они полезны для тех случаев, когда требуется ссылка на других объектов и актёров, не известная до запуска игры.
Этот флаг предотвращает сохранение ссылки на эту переменную внутри редактора и ускоряет процесс сохранения и загрузки класса.
Используйте флаг SaveGame
только для переменных класса, унаследованного от класса SaveGame
. Отмечайте этот флаг, только если SaveGame
должен сохранить значение. Временные переменные, не хранимые в слоте сохранения, не должны отмечаться этим флагом.
Не смешивайте SaveGame
и Transient
— это бесполезно.
Не используйте флаг Config Variable
. Дизайнерам будет труднее из-за этого контроллировать поведение блупринта. Этот флаг используется только в C++ для редко меняющихся значений, как если бы они были под двойным флагом Advanced Display
.
Copyright (c) 2016 Gamemakin LLC
См. файл LICENSE
Мы поощряем клонирование этого репозитория с целью адаптации под правила вашей команды. Ниже, вы можете указать свои правки, чтобы периодически обновлять стайл-гайд без ошибок слияния.