🐨
Unreal Engine Tips
  • 🐦О сайте
  • 🐸Об авторе
  • Unreal Engine
    • 🦉C++
    • 💎Материалы
    • 🎆Niagara
    • 🦴Анимации
    • 🌞Управление уровнями
    • 🌴Game Design
    • 🐾Git
    • 🪱Workflow разработки
    • 🐙Coding Standard
    • 🦞Руководство по стилю
    • 🦋Рефакторинг
    • 🐞JetBrains Rider
    • 🐧Разное
    • 🐫Полезные ссылки
Powered by GitBook
On this page
  • Оглавление
  • Важная терминология
  • Уровни/карты
  • Идентификаторы
  • Варианты написания идентификаторов
  • Переменные/Свойства
  • Свойство
  • Переменная
  • 0. Принципы
  • 0.1 Если в вашем проекте уже есть руководство по стилю, вы должны следовать ему
  • 0.2 Вся структура, ассеты и код в любом проекте должны выглядеть так, как будто их создал один человек, независимо от того, сколько людей внесли свой вклад
  • 0.3 Друзья не позволяют своим друзьям иметь плохой стиль
  • 0.4 Команда без руководства по стилю — не моя команда
  • 0.5 Не нарушайте закон
  • 00. Общепринятые правила
  • 00.1 Запрещённые символы
  • Идентификаторы
  • 1. Соглашения об именовании ассетов
  • 1.1 Базовое имя ассета - Prefix_BaseAssetName_Variant_Suffix
  • 1.1 Примеры
  • 1.1e1 Боб
  • 1.1e2 Камни
  • 1.2 Модификаторы имени ассета
  • 1.2.1 Наиболее популярные
  • 1.2.2 Анимации
  • 1.2.3 Искусственный интеллект
  • 1.2.4 Blueprints
  • 1.2.5 Материалы
  • 1.2.6 Текстуры
  • 1.2.6.1 Упакованные текстуры
  • 1.2.7 Разное
  • 1.2.8 Paper 2D
  • 1.2.9 Физика
  • 1.2.10 Звуки
  • 1.2.11 Пользовательский интерфейс
  • 1.2.12 Эффекты
  • 2. Структура каталога Content
  • 2e1 Пример структуры Content
  • 2.1 Имена папок
  • 2.1.1 Всегда используйте PascalCase *
  • 2.1.2 Никогда не используйте пробелы
  • 2.1.3 Никогда не используйте символы Unicode и другие символы
  • 2.2 Используйте папку верхнего уровня для проекта
  • 2.2.1 Нет глобальных ассетов
  • 2.2.2 Уменьшение конфликтов при миграции
  • 2.2.2e1 Пример мастер-материала
  • 2.2.3 Образцы, шаблоны и контент из Marketplace не создают риска внедрения
  • 2.2.4 DLC, подпроекты и патчи легко поддерживать
  • 2.3 Используйте папку Developers для локального тестирования
  • 2.4 Все файлы Map лежат папке с названием Maps
  • 2.5 Используйте папку Core для важных Blueprints и других ассетов
  • 2.6 Не создавайте папки с именами Assets или AssetTypes
  • 2.6.1 Создание папки с именем Assets - излишне
  • 2.6.2 Создание папки с именем Meshes, Textures или Materials является избыточным
  • 2.7. Очень большие наборы ассетов получают собственную папку
  • 2.8 MaterialLibrary
  • 2.9 Никаких пустых папок
  • 3. Blueprints
  • 3.1 Компиляция
  • 3.2 Переменные
  • 3.2.1 Именование
  • 3.2.1.1 Существительные
  • 3.2.1.2 PascalCase
  • 3.2.1.2e Примеры
  • 3.2.1.3 Префикс b для boolean
  • 3.2.1.4 Имена для boolean
  • 3.2.1.4.1 Общая и независимая информация о состоянии
  • 3.2.1.4.2 Комплексные состояния
  • 3.2.1.5 Учитывайте контекст
  • 3.2.1.5e Примеры
  • 3.2.1.6 Не включайте имена атомарных типов
  • 3.2.1.7 Включайте имена неатомарных типов
  • 3.2.1.8 Массивы
  • 3.2.2 Редактируемые переменные
  • 3.2.2.1 Подсказки
  • 3.2.2.2 Слайдер и диапазоны значений
  • 3.2.3 Категории
  • 3.2.4 Уровень доступа к переменным
  • 3.2.4.1 Закрытые переменные
  • 3.2.5 Дополнительные переменные
  • 3.2.6 Transient-переменные
  • 3.2.7 Config-переменные
  • 3.3 Функции, события и диспетчеры событий
  • 3.3.1 Именование функций
  • 3.3.1.1 Все имена функций должны быть глаголами
  • 3.3.1.2 Свойство RepNotify всегда OnRep_Variable
  • 3.3.1.3 Информационные функции, возвращающие bool, должны задавать вопросы
  • 3.3.1.4 Обработчики событий и диспетчеры должны начинаться сOn
  • 3.3.1.5 Удаленные вызовы процедур должны начинаться с префикса Target
  • 3.3.2 Все функции должны иметь Return Nodes
  • 3.3.3 Ни одна функция не должна иметь более 50 узлов
  • 3.3.4 Все публичные (public) функции должны иметь описание
  • 3.3.5 Все пользовательские статические BlueprintCallable функции подключаемых модулей должны быть классифицированы по имени подключаемого модуля
  • 3.4 Blueprint Graphs
  • 3.4.1 Никакого спагетти-кода
  • 3.4.2 Выравнивание проводов, а не узлов
  • 3.4.3 Белые линии Exec имеют наивысший приоритет
  • 3.4.4 Код должен быть разумно прокомментирован
  • 3.4.5 Код должен обрабатывать ошибки Cast, где это уместно
  • 3.4.6 В коде не должно быть висящих/свободных/мёртвых узлов
  • 4. Static Meshes
  • 4.1 Static Mesh UV
  • 4.1.1 Все Static Meshes должны иметь UV
  • 4.1.2 Все Static Meshes не должны иметь перекрывающихся UV для карт освещения
  • 4.2 LOD должны быть настроены правильно
  • 4.3 Модульные ассеты без сокетов должны четко привязываться к Static Mesh
  • 4.4 Все Static Meshes должны иметь коллизию
  • 4.5 Все Static Meshes должны быть правильно масштабированы
  • 5. Niagara
  • 5.1 Без пробелов, всегда
  • 6. Уровни/карты
  • 6.1 Отсутствие ошибок или предупреждений
  • 6.2 Освещение должно быть рассчитано
  • 6.3 Игрок не должен видеть пересекающиеся текстуры (Z Fighting)
  • 6.4 Особые правила Marketplace
  • 6.4.1 Overview уровень
  • 6.4.2 Demo уровень
  • 7. Текстуры
  • 7.1 Размеры должны быть степенью двойки
  • 7.2 Плотность текстур должна быть одинаковой
  • 7.3 Текстуры не должны быть больше 8192 пикселей
  • 7.4 У текстур должна быть выбрана правильная группа
  • Поправки
  • Отличия от оригинала
  1. Unreal Engine

Руководство по стилю

PreviousCoding StandardNextРефакторинг

Last updated 1 year ago

Данное руководство является переводом:

Однако, данный перевод имеет некоторые от оригинала.

Оглавление

Важная терминология

Уровни/карты

Идентификаторы

Идентификатор - это всё, что напоминает или служит «именем». Например, имя ассета, имя материала, свойство blueprint, переменная, имя папки, имя строки таблицы и т. д.

Варианты написания идентификаторов

Есть несколько разных способов написания идентификаторов. Вот некоторые распространенные варианты:

PascalCase

Используйте каждое слово с заглавной буквы и удалите все пробелы, например DesertEagle, StyleGuide, ASeriesOfWords.

camelCase

Первая буква всегда строчная, но каждое последующее слово начинается с прописной, например desertEagle, styleGuide, aSeriesOfWords.

Snake_case

Слова могут произвольно начинаться с верхнего или нижнего регистра, но слова разделяются символом подчеркивания, например desert_Eagle, Style_Guide, a_Series_of_Words.

Переменные/Свойства

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

Свойство

Обычно относится к переменной, определенной в классе. Например, если у BP_Barrel есть переменная bExploded, то bExploded можно назвать свойством BP_Barrel.

Переменная

Обычно это либо аргумент функции, либо просто локальная переменная внутри функции.

0. Принципы

0.1 Если в вашем проекте уже есть руководство по стилю, вы должны следовать ему

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

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

0.2 Вся структура, ассеты и код в любом проекте должны выглядеть так, как будто их создал один человек, независимо от того, сколько людей внесли свой вклад

Переход от одного проекта к другому не должен вызывать повторного изучения стиля и структуры. Соответствие руководству по стилю устраняет ненужные догадки и двусмысленности.

Это также позволяет более продуктивно создавать и поддерживать проект, поскольку не нужно думать о стиле. Просто следуйте инструкциям. Это руководство по стилю написано с учётом лучших практик, а это означает, что, следуя этому руководству по стилю, вы также сведёте к минимуму проблемы, которые трудно отследить.

0.3 Друзья не позволяют своим друзьям иметь плохой стиль

Если вы видите, что кто-то работает либо против руководства по стилю, либо вообще без такового, постарайтесь это исправить.

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

0.4 Команда без руководства по стилю — не моя команда

Когда вы присоединяетесь к команде Unreal-разработчиков, одним из ваших первых вопросов должен быть: «Есть ли у вас руководство по стилю?». Если ответ отрицательный, вы должны скептически относиться к их способности работать в команде.

0.5 Не нарушайте закон

Gamemakin LLC не является юристом, но, пожалуйста, не привносите в проект незаконные действия и поведение, включая, помимо прочего:

  • Не распространяйте контент, на распространение которого у вас нет прав

  • Не нарушайте чьи-либо авторские права или товарные знаки

  • Не воруйте контент

  • Соблюдайте лицензионные ограничения контента

00. Общепринятые правила

00.1 Запрещённые символы

Идентификаторы

Ни в каком идентификаторе никогда не используйте следующие символы, кроме случаев крайней необходимости:

  • Пробельный символ любого типа

  • Обратная косая черта\

  • Символы, такие как #!@$%

  • Любой Unicode-символ

Любой идентификатор должен стремиться иметь только следующие символы, когда это возможно (RegEx [A-Za-z0-9_]+)

  • ABCDEFGHIJKLMNOPQRSTUVWXYZ

  • abcdefghijklmnopqrstuvwxyz

  • 1234567890

  • _

Соблюдение этих правил обеспечит наибольшую совместимость всех данных на всех платформах во всех инструментах и ​​поможет предотвратить простои из-за потенциально неправильной обработки символов идентификаторов в коде, который вы не контролируете.

1. Соглашения об именовании ассетов

Соглашения об именовании ассетов следует рассматривать как закон. В проекте, где поддерживается это соглашение, очень легко управлять ассетами, искать, анализировать и поддерживать их.

Большинство ассетов имеют префикс, который обычно представляет собой аббревиатуру типа ассета, за которой следует символ подчеркивания.

1.1 Базовое имя ассета - Prefix_BaseAssetName_Variant_Suffix

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

Помнить шаблон Prefix_BaseAssetName_Variant_Suffix и руководствоваться здравым смыслом, как правило, достаточно, чтобы гарантировать хорошее имя ассета. Вот несколько подробных правил относительно каждого элемента.

BaseAssetName должно определяться коротким и легко узнаваемым именем, относящимся к контексту данной группы ассетов. Например, если у вас есть персонаж по имени Боб, все ассеты Боба будут иметь BaseAssetName - Bob.

Для уникальных и специфических вариантов ассетов используется Variant. Variant - это короткое и легко узнаваемое имя, представляющее логическую группу ассетов, являющихся подмножеством базового имени ассета. Например, если у Боба есть несколько скинов, эти скины все равно должны использовать Bob как BaseAssetName. Но при этом скин «Evil» будет называться Bob_Evil, а скин «Ретро» будет называться Bob_Retro.

Для уникальных, но универсальных вариантов ассетов Variant - это двузначное число, начинающееся с 01. Например, если у вас есть художник по окружающей среде, создающий невзрачные камни, они будут называться Rock_01, Rock_02, Rock_03 и т. д. За редчайшим исключением, у вас никогда не должен быть трехзначный номер варианта. Если у вас более 100 ассетов, вам следует рассмотреть возможность организации их с разными базовыми именами или с использованием нескольких вариантов имён.

В зависимости от того, как создаются варианты ваших ассетов, вы можете связать вместе имена вариантов. Например, если вы создаете элементы напольного покрытия для проекта Arch Viz, вы должны использовать базовое имя Flooring со связанными вариантами, такими как Flooring_Marble_01, Flooring_Maple_01, Flooring_Tile_Squares_01.

1.1 Примеры

1.1e1 Боб

Тип ассета

Имя ассета

Skeletal Mesh

SK_Bob

Material

М_Bob

Texture (Diffuse/Albedo)

T_Bob_D

Texture (Normal)

T_Bob_N

Texture (Evil Diffuse)

T_Bob_Evil_D

1.1e2 Камни

Тип ассета

Имя ассета

Static Mesh (01)

S_Rock_01

Static Mesh (02)

S_Rock_02

Static Mesh (03)

S_Rock_03

Material

M_Rock

Material Instance (Снег)

MI_Rock_Snow

1.2 Модификаторы имени ассета

1.2.1 Наиболее популярные

Тип ассета
Prefix
Suffix
Примечание

Level / Map

Level (Persistent)

_P

Level (Audio)

_Audio

Level (Lighting)

_Lighting

Level (Geometry)

_Geo

Level (Gameplay)

_Gameplay

Blueprint

BP_

Material

M_

Static Mesh

SM_

Skeletal Mesh

SK_

Texture

T_

_?

Particle System

PS_

Niagara System

NS_

Widget Blueprint

WBP_

1.2.2 Анимации

Тип ассета
Prefix
Suffix
Примечание

Aim Offset

AO_

Aim Offset 1D

AO_

Animation Blueprint

ABP_

Animation Composite

AC_

Animation Montage

AM_

Animation Sequence

A_

Blend Space

BS_

Blend Space 1D

BS_

Level Sequence

LS_

Morph Target

MT_

Paper Flipbook

PFB_

Rig

Rig_

Skeletal Mesh

SK_

Skeleton

SKEL_

1.2.3 Искусственный интеллект

Тип ассета
Prefix
Suffix
Примечание

AI Controller

AIC_

Behavior Tree

BT_

Blackboard

BB_

Decorator

BTDecorator_

Service

BTService_

Task

BTTask_

Environment Query

EQS_

EnvQueryContext

EQS_

Context

1.2.4 Blueprints

Тип ассета
Prefix
Suffix
Примечание

Blueprint

BP_

Blueprint Component

BP_

Component

Например, BP_InventoryComponent

Blueprint Function Library

BPFL_

Blueprint Interface

BPI_

Blueprint Macro Library

BPML_

Желательно не использовать библиотеки макросов

Enumeration

E

Без черты подчёркивания

Structure

F

Без черты подчёркивания

Tutorial Blueprint

TBP_

Widget Blueprint

WBP_

1.2.5 Материалы

Тип ассета
Prefix
Suffix
Примечание

Material

M_

Material (Post Process)

PP_

Material Function

MF_

Material Instance

MI_

Material Parameter Collection

MPC_

Subsurface Profile

SP_

Physical Materials

PM_

Decal

M_, MI_

_Decal

1.2.6 Текстуры

Тип ассета
Prefix
Suffix
Примечание

Texture

T_

Texture (Diffuse/Albedo/Base Color)

T_

_D

Texture (Normal)

T_

_N

Texture (Roughness)

T_

_R

Texture (Alpha/Opacity)

T_

_A

Texture (Ambient Occlusion)

T_

_O

Texture (Bump)

T_

_B

Texture (Emissive)

T_

_E

Texture (Mask)

T_

_M

Texture (Specular)

T_

_S

Texture (Metallic)

T_

_M

Texture (Packed)

T_

_*

Texture Cube

TC_

Media Texture

MT_

Render Target

RT_

Cube Render Target

RTC_

Texture Light Profile

TLP

1.2.6.1 Упакованные текстуры

Обычной практикой является упаковка нескольких слоёв данных в одну текстуру. Примером этого является объединение Emissive, Roughness, Ambient Occlusion в качестве красного, зеленого и синего каналов текстуры соответственно. Чтобы определить суффикс, просто сложите буквы соответствующего суффикса из таблицу сверху вместе. Получится, например, _ERO.

Обычно допустимо включать слой Alpha/Opacity в альфа-канал Diffuse/Albedo, и, поскольку это обычная практика, добавление суффикса A к _D необязательно.

Упаковывать 4 канала данных в текстуру (RGBA) не рекомендуется, за исключением маски Alpha/Opacity в альфа-канале Diffuse/Albedo, поскольку текстура с альфа-каналом влечёт за собой больше накладных расходов, чем текстура без него.

1.2.7 Разное

Тип ассета
Prefix
Suffix
Примечание

Animated Vector Field

VFA_

Camera Anim

CA_

Curve Color

Curve_

_Color

Curve Table

Curve_

_Table

Data Asset

*_

Префикс должен основываться на классе

Data Table

DT_

Curve Float

Curve_

_Float

Foliage Type

FT_

Force Feedback Effect

FFE_

Landscape Grass Type

LG_

Landscape Layer

LL_

Matinee Data

Matinee_

Media Player

MP_

File Media Source

FMS_

Object Library

OL_

Redirector

Должны быть исправлены как можно скорее.

Sprite Sheet

SS_

Static Vector Field

VF_

Substance Graph Instance

SGI_

Substance Instance Factory

SIF_

Touch Interface Setup

TI_

Vector Curve

Curve_

_Vector

1.2.8 Paper 2D

Тип ассета
Prefix
Suffix
Notes

Paper Flipbook

PFB_

Sprite

SPR_

Sprite Atlas Group

SPRG_

Tile Map

TM_

Tile Set

TS_

1.2.9 Физика

Тип ассета
Prefix
Suffix
Примечание

Physical Material

PM_

Physics Asset

PHYS_

Destructible Mesh

DM_

1.2.10 Звуки

Тип ассета
Prefix
Suffix
Примечание

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

S_

_Cue

Sound Mix

Mix_

Sound Wave

S_

1.2.11 Пользовательский интерфейс

Тип ассета
Prefix
Suffix
Примечание

Font

Font_

Slate Brush

Brush_

Slate Widget Style

Style_

Widget Blueprint

WBP_

1.2.12 Эффекты

Тип ассета
Prefix
Suffix
Примечание

Particle System

PS_

Niagara System

NS_

Niagara Emitter

NE_

Niagara Module

NM_

Niagara Function Script

NM_

_S

Material (Post Process)

PP_

2. Структура каталога Content

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

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

2e1 Пример структуры Content

|-- 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 Имена папок

Это общие правила именования любой папки в Content.

PascalCase означает, что имя начинается с заглавной буквы, а затем вместо использования пробелов каждое последующее слово также начинается с заглавной буквы. Например, DesertEagle, RocketPistol и ASeriesOfWords.

2.1.2 Никогда не используйте пробелы

2.1.3 Никогда не используйте символы Unicode и другие символы

Использование других символов вне a-z, A-Z и 0-9 таких как @, -, _, ,, * и # также может привести к неожиданным и трудно отслеживаемым проблемам на других платформах, в системе контроля версий и в различных дополнительных инструментах.

2.2 Используйте папку верхнего уровня для проекта

Все ассеты проекта должны находиться в папке с именем проекта. Например, если ваш проект называется «Generic Shooter», всё его содержимое должно находиться в папке Content/GenericShooter.

Есть несколько причин для такого подхода.

2.2.1 Нет глобальных ассетов

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

2.2.2 Уменьшение конфликтов при миграции

При работе над несколькими проектами команда обычно копирует ассеты из одного проекта в другой, если она сделала что-то, что нужно обоим проектам. Когда это происходит, самый простой способ выполнить копирование — использовать функцию Migrate в Content Browser, так как она копирует не только выбранный ресурс, но и все его зависимости.

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

Это также основная причина, по которой персонал Epic Marketplace применяет ту же политику для представленных ассетов.

После миграции безопасное слияние ресурсов можно выполнить с помощью инструмента «Replace References» в Content Browser. Также это позволяет понимать, что те ассеты, которые находятся за пределами папки проекта, очевидно, ожидают слияния. После полной миграции ресурсов и их объединения в дереве Content не должно быть другой папки верхнего уровня. Этот метод на 100% гарантирует полную безопасность любых миграций.

2.2.2e1 Пример мастер-материала

Например, предположим, что вы создали мастер-материал в одном проекте, который хотели бы использовать в другом проекте, поэтому вы перенесли этот ассет. Если этот ресурс не находится в папке верхнего уровня, он может иметь имя, например: Content/MaterialLibrary/M_Master. Если в целевом проекте еще нет ассета с таким же именем, это должно работать без проблем.

По мере продвижения работы над одним или обоими проектами их соответствующие мастер-материалы могут изменяться, чтобы быть адаптированными для их конкретных проектов.

Проблема возникает, когда, например, художник для одного проекта создал хороший модульный набор Static Meshes, а кто-то хочет включить этот набор Static Meshes во второй проект. Если художник, создавший ассеты, использовал экземпляры материалов Content/MaterialLibrary/M_Master, при выполнении миграции существует большая вероятность конфликта для ранее перенесенного Content/MaterialLibrary/M_Master.

Эту проблему трудно предсказать и трудно предупредить. Человек, переносящий Static Meshes, может не быть тем же человеком, который знаком с разработкой мастер-материала обоих проектов, и он может даже не знать, что рассматриваемые Static Meshes зависят от экземпляров материала, которые затем зависят от мастер-материала. Однако, для работы инструмента Migrate требуется вся цепочка зависимостей, поэтому он будет вынужден захватить Content/MaterialLibrary/M_Master, когда копирует эти ассеты в другой проект, и после инструмент Migrate просто перезапишет существующий ассет мастер-материала.

Именно на этом этапе, если мастер-материалы для обоих проектов каким-либо образом несовместимы, вы рискуете, возможно, сломать всю библиотеку материалов для проекта, а также любые другие зависимости, которые, возможно, уже были перенесены, просто потому, что ассеты не были сохранены в папке верхнего уровня. Простая миграция Static Meshes теперь становится очень неприятной и опасной задачей.

2.2.3 Образцы, шаблоны и контент из Marketplace не создают риска внедрения

2.2.4 DLC, подпроекты и патчи легко поддерживать

Если ваш проект планирует выпустить DLC или имеет несколько связанных с ним подпроектов, которые могут быть либо перенесены, либо просто не включены в сборку, то ресурсы, относящиеся к этим проектам, должны иметь свою собственную отдельную папку содержимого верхнего уровня. Это значительно упрощает подготовку DLC отдельно от основного контента проекта. Подпроекты также можно переносить и удалять с минимальными усилиями. Если вам нужно изменить материал ассета или добавить какое-то особенное поведение ассета в патче, вы можете легко поместить эти изменения в папку патча и работать с ней безопасно, не нарушая основной проект.

2.3 Используйте папку Developers для локального тестирования

Во время разработки проекта члены команды часто имеют своего рода «песочницу», где они могут свободно экспериментировать, не рискуя основным проектом. Поскольку эта работа может продолжаться, эти члены команды могут захотеть поместить свои ассеты на сервер управления исходным кодом проекта. А это может привести к определённым проблемам.

Участнику команды очень легко случайно использовать ассеты, которые ещё не готовы к этому, что вызовет проблемы после удаления этих ассетов. Например, художник может ещё продолжать работу над модульным набором Static Meshes, меняя его размеры и привязку. Если дизайнер уровня увидит эти ассеты в основной папке проекта, он может использовать их на уровне, не подозревая, что они могут подвергнуться серьёзным изменениям и/или удалению. Это вызывает огромное количество повторной работы для всех в команде.

Если бы эти ассеты были помещены в папку Developers, у дизайнера уровня никогда было не было причин их использовать, и проблема бы не возникла. Content Browser имеет специальные параметры просмотра, которые скрывают папку разработчика (она скрыта по умолчанию), что делает невозможным случайное использование ресурсов разработчика при обычном использовании.

Как только ассеты готовы к использованию, художник просто должен переместить их в папку для конкретного проекта и исправить редиректы. По сути, это «продвижение» ассетов из experimental в production.

2.4 Все файлы Map лежат папке с названием Maps

Файлы карт сильно отличаются, и каждый проект обычно имеет свою собственную систему именования карт, особенно если они работают с подуровнями или streaming-уровнями. Независимо от того, какая система организации карт используется для конкретного проекта, все уровни должны находиться в /Content/Project/Maps.

Возможность сказать кому-то открыть конкретную карту, не объясняя, где она находится, — это отличная экономия времени и общее улучшение «качества жизни». Обычно уровни находятся в подпапках Maps, таких как Maps/Campaign1/ или Maps/Arenas, но самое главное здесь то, что все они существуют внутри /Content/Project/Maps.

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

2.5 Используйте папку Core для важных Blueprints и других ассетов

Используйте папку /Content/Project/Core для ассетов, которые абсолютно необходимы для работы проекта. Например, базовые GameMode, Character, PlayerController, GameState, PlayerState и связанные с ними Blueprints должны находиться в этой папке.

Это создаёт чёткое послание: «Не трогайте это» - для других членов команды. У не-программистов должно быть очень мало причин для входа в папку Core. Следуя хорошему стилю структуры кода, дизайнеры должны вносить изменения в игровой процесс в дочерних классах, расширяющих функциональность базовых. Дизайнеры должны использовать дочерние классы в отдельных папках, а не напрямую базовые классы.

Например, если в вашем проекте требуются поднимаемые предметы, которые можно разместить на уровне, то должен существовать базовый класс Pickup в Core/Pickups, определяющий базовое поведение поднимаемого предмета. Конкретные подбираемые предметы, такие как Health или Ammo, должны находиться в папке, например /Content/Project/Placeables/Pickups/. Game Designers могут определять и настраивать предметы в этой папке, как им заблагорассудится, но они не должны касаться папки Core/Pickups, так как они могут непреднамеренно сломать базовый класс.

2.6 Не создавайте папки с именами Assets или AssetTypes

2.6.1 Создание папки с именем Assets - излишне

Поскольку все ассеты и так являются ассетами.

2.6.2 Создание папки с именем Meshes, Textures или Materials является избыточным

Все имена ассетов названы с учетом их типа. Эти папки предлагают только избыточную информацию, и использование этих папок может быть легко заменено надежной и простой в использовании системой фильтрации, предоставляемой Content Browser.

Хотите просматривать только Static Meshes в папке Environment/Rocks/? Просто включите фильтр Static Mesh. Если все ассеты названы правильно, они также будут отсортированы в алфавитном порядке независимо от префиксов. Хотите просматривать Static Meshes и Skeletal Meshes? Просто включите оба фильтра. Это устраняет необходимость потенциального выбора с помощью Ctrl-Click двух папок в дереве Content Browser.

Отсутствие подобных папок также предотвращает неизбежное размещение кем-либо Static Mesh или текстуры в папке Materials.

2.7. Очень большие наборы ассетов получают собственную папку

Существуют определенные типы ассетов, которые имеют огромный объем связанных файлов, где каждый ресурс имеет уникальное назначение. Двумя наиболее распространенными являются анимация и аудио-ассеты. Если вы обнаружите, что у вас есть более 15 таких ассетов, которые принадлежат друг другу, то их надо разместить все вместе в отдельной папке.

Например, анимация, которая используется несколькими персонажами, должна находиться в подпапках Characters/Common/Animations. Эти подпапки могут иметь, например, такие имена: Locomotion или Cinematic.

2.8 MaterialLibrary

Если в вашем проекте используются мастер-материалы, многослойные материалы или любые формы повторно используемых материалов или текстур, которые не принадлежат ни к одному подмножеству ресурсов, то эти ресурсы должны быть расположены в папке Content/Project/MaterialLibrary.

Таким образом, все «глобальные» материалы имеют своё место и легко обнаруживаются.

Это также позволяет в рамках проекта невероятно легко применять политику: «Применять можно только экземпляры материала». Если все дизайнеры и ассеты должны использовать только экземпляры материалов, то в этой папке должны существовать только обычные материалы. Вам будет легко проверять это, просто выполняя поиск базовых материалов в любой папке, кроме папки MaterialLibrary.

Директория MaterialLibrary не обязательно должна состоять только из материалов. Общие служебные текстуры, функции материалов и другие подобные вещи также должны храниться здесь, а также в папках, обозначающих их предназначение. Например, общие текстуры шума должны находиться в директории MaterialLibrary/Utility.

Любые материалы для тестирования или отладки должны находиться в директории MaterialLibrary/Debug. Это позволяет легко удалять отладочные материалы из проекта перед отправкой, а также, благодаря отображению ошибок делает очевидным, если кто-то их ещё использует.

2.9 Никаких пустых папок

Просто не должно быть пустых папок. Они загромождают Content Browser.

Если вы обнаружите, что в Content Browser есть пустая папка, которую вы не можете удалить, выполните следующие действия:

  1. Убедитесь, что вы используете систему контроля версий.

  2. Немедленно запустите Fix Up Redirectors в своём проекте.

  3. Перейдите к папке на диске и удалите ассеты внутри.

  4. Закройте редактор.

  5. Убедитесь, что ваша система контроля версий синхронизирована (например, если вы используете Perforce, запустите Reconcile Offline Work в вашем каталоге Content).

  6. Откройте редактор. Убедитесь, что всё работает по-прежнему. Если это не так, вернитесь, выясните, что пошло не так, и повторите попытку.

  7. Убедитесь, что папка исчезла.

  8. Отправьте изменения в систему контроля версий.

3. Blueprints

3.1 Компиляция

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

Не отправляйте неработающие Blueprint в систему контроля версий. Если вы должны хранить их там, то лучше отложите их.

Сломанные Blueprints могут вызвать проблемы, проявляющиеся по-разному: неработающие ссылки, неожиданное поведение, ошибки при сборке и частая ненужная перекомпиляция. Сломанный Blueprint может сломать всю вашу игру.

3.2 Переменные

Слова переменная и свойство могут использоваться взаимозаменяемо.

3.2.1 Именование

3.2.1.1 Существительные

Все имена небулевских переменных должны быть чёткими, однозначными и описательными существительными.

3.2.1.2 PascalCase

3.2.1.2e Примеры

  • Score

  • Kills

  • TargetPlayer

  • Range

  • CrosshairColor

  • AbilityID

3.2.1.3 Префикс b для boolean

Все булевские переменные должны быть названы в формате PascalCase, но с префиксом b в нижнем регистре.

Пример: Используйте bDead и bEvil, а не Dead и Evil.

Редактор Blueprint знает, что не следует включать b при отображении переменной.

3.2.1.4 Имена для boolean

3.2.1.4.1 Общая и независимая информация о состоянии

Все булевские переменные следует именовать с помощью описательных прилагательных, если они представляют общую информацию. Не включайте слова, формулирующие переменную как вопрос, например Is. Это зарезервировано для функций.

Пример: Используйте bDead и bHostile, а не bIsDead и bIsHostile.

Старайтесь не использовать такие глаголы, как bRunning. Глаголы, как правило, приводят к комплексным состояниям.

3.2.1.4.2 Комплексные состояния

Не используйте булевские переменные для представления комплексных и/или зависимых состояний. Это делает добавление и удаление состояний сложными и неудобными для чтения. Вместо этого используйте перечисление.

Пример: при определении оружия не используйте bReloading и bEquipping, если оружие нельзя одновременно перезаряжать и экипировать. Вместо этого определите перечисление с именем EWeaponState и используйте переменную этого типа с именем WeaponState. Это значительно упрощает добавление новых состояний к оружию.

Пример: Не используйте bRunning, если вам также нужны bWalking или bSprinting. Это должно быть определено как перечисление с чётко определенными именами состояний.

3.2.1.5 Учитывайте контекст

Все имена переменных не должны дублировать свой контекст, поскольку все переменные в Blueprint и так его имеют.

3.2.1.5e Примеры

Рассмотрим Blueprint с именем BP_PlayerCharacter.

Плохие примеры:

  • PlayerScore

  • PlayerKills

  • MyTargetPlayer

  • MyCharacterName

  • CharacterSkills

  • ChosenCharacterSkin

Все эти переменные имеют избыточное имя. Очевидно, что эти переменные и так принадлежат классу BP_PlayerCharacter, потому что именно он их и определяет.

Хорошие примеры:

  • Score

  • Kills

  • TargetPlayer

  • Name

  • Skills

  • Skin

3.2.1.6 Не включайте имена атомарных типов

Атомарные или примитивные переменные — это переменные, которые представляют данные в их простейшей форме, такой как boolean, integer, float и перечисления.

Строки (String) и векторы считаются атомарными с точки зрения стиля при работе с Blueprints, однако технически они не являются таковыми.

В то время как векторы состоят из трёх float, векторами часто можно манипулировать как единым целым, аналогично и с ротаторами.

Не считайте переменные Text атомарными, поскольку они содержат в себе функциональность локализации. Атомарный тип строки символов — String, а не Text.

Атомарные переменные не должны иметь имя своего типа в своём имени.

Пример: используйте Score, Kills, Description, а не ScoreFloat , FloatKills, DescriptionString.

Единственным исключением из этого правила является случай, когда переменная представляет собой «количество» чего-либо и когда использование имени без типа переменной затрудняет понимание её назначения.

Пример: Генератор забора должен сгенерировать X секций. Сохраните X в NumPostsили PostsCount вместо просто Posts, который потенциально может быть воспринят как массив типа Post.

3.2.1.7 Включайте имена неатомарных типов

Неатомарные или сложные переменные — это переменные, представляющие данные в виде набора атомарных переменных. Структуры, классы, интерфейсы и примитивные типы со скрытым поведением, такие как Text и Name - все подпадают под это правило.

Несмотря на то, что массив атомарного типа представляет собой список переменных, он всё равно не изменяет «атомарность» хранимого типа.

Эти переменные должны включать имя своего типа, но при этом учитывать их контекст.

Если класс владеет сложной переменной, т. е. если BP_PlayerCharacter владеет BP_Hat, она должна храниться с именем своего типа без каких-либо изменений.

Пример: используйте Hat, Flag и Ability, а не MyHat , MyFlag и PlayerAbility.

Если класс не владеет значением, которое представляет сложная переменная, вы должны использовать существительное вместе с типом переменной.

Пример: если у класса BP_Turret есть возможность нацеливаться на BP_PlayerCharacter, то он должен хранить свою цель так TargetPlayer, чтобы в контексте BP_Turret было ясно, что это ссылка на другой тип сложной переменной, которой он не владеет.

3.2.1.8 Массивы

Массивы следуют тем же правилам именования, что и выше, но должны иметь имя в виде существительного во множественном числе.

Пример: используйте Targets, Hats и EnemyPlayers, а не TargetList , HatArray и EnemyPlayerArray.

3.2.2 Редактируемые переменные

Все переменные, значение которых можно безопасно изменить для настройки поведения blueprint, должны быть помечены как Editable.

И наоборот, все переменные, изменение которых небезопасно или которые не должны предоставляться дизайнерам, не должны быть помечены как Editable, за исключением тех случаев, когда по техническим причинам переменная должна быть помечена как Expose On Spawn.

Не помечайте переменные как Editable без веской причины.

3.2.2.1 Подсказки

Все Editable переменные, в том числе те, которые помечены как редактируемые только для того, чтобы их можно было пометить как Expose On Spawn, должны иметь описание в своих Tooltip-полях, объясняющее, как изменение этого значения влияет на поведение Blueprint.

3.2.2.2 Слайдер и диапазоны значений

Все Editable переменные должны использовать слайдер и диапазоны значений, если существует значение, на которое не следует устанавливать переменную.

Пример: Blueprint, создающий секции забора, может иметь редактируемую переменную с именем PostsCount. В этом случае значение -1 не имеет никакого смысла. Используйте поля для указания диапазона, чтобы отметить 0 как минимум.

Если редактируемая переменная используется в Construction Script, для нее должен быть определен разумный диапазон слайдера, чтобы кто-то не мог случайно присвоить ей большое значение, которое может привести к сбою редактора.

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

3.2.3 Категории

Если класс имеет небольшое количество переменных, категории не требуются, но всем переменным Editable она должна быть присвоена. Распространённая категория — Config.

Если класс имеет большое количество переменных, все переменные Editable следует разделить на подкатегории, используя категорию Config в качестве базовой категории. Нередактируемые переменные следует разделить на категории с содержательными именами, описывающие их использование.

Вы можете определить подкатегории с помощью вертикальной черты |, например, Config | Animations

Пример: набор переменных класса оружия может быть организован следующим образом:

|-- Config
|    |-- Animations
|    |-- Effects
|    |-- Audio
|    |-- Recoil
|    |-- Timings
|-- Animations
|-- State
|-- Visuals

3.2.4 Уровень доступа к переменным

В C++ переменные имеют понятие уровня доступа. Открытый (public) означает, что любой код вне класса может получить доступ к переменной. Защищенный (protected) означает, что только класс и любые дочерние классы могут получить доступ к ней. Закрытый (private) означает, что только этот класс может иметь доступ к переменной.

В настоящее время Blueprints не содержат концепции защищенного (protected) доступа для переменных.

3.2.4.1 Закрытые переменные

Все переменные, включая Editable, должны быть объявлены закрытыми (private). При необходимости доступа к ним в дочерних классах можно использовать protected get/set функции, а для доступа за пределами иерархии - public get/set функции. Разумеется, создавать функции доступа нужно лишь при их реальной необходимости.

3.2.5 Дополнительные переменные

Если переменная должна быть редактируемой, но часто нетронутой, пометьте ее как Advanced Display. Это делает переменную скрытой, пока не будет нажата стрелка расширенного отображения.

Найти параметр Advanced Display можно при отображении расширенных параметров в настройках переменной.

3.2.6 Transient-переменные

Transient-переменные — это переменные, значения которых не нужно сохранять и загружать и которые имеют начальное значение равные 0 или null. Это полезно для ссылок на другие объекты и actors, значение которых неизвестно до времени выполнения. Это предотвращает сохранение ссылки редактором и ускоряет сохранение и загрузку Blueprint.

Из-за этого все transient-переменные всегда должны быть инициализированы 0 или null. В противном случае, это приведет к трудным для отладки ошибкам.

3.2.7 Config-переменные

Не используйте флаг Config Variable. Из-за этого дизайнерам сложнее контролировать поведение Blueprint. Config-переменные должны использоваться только в C++ для редко изменяемых переменных. Думайте о них как об Advanced Advanced Display.

3.3 Функции, события и диспетчеры событий

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

3.3.1 Именование функций

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

  • Это чистая функция?

  • Получает ли она информацию о состоянии?

  • Это обработчик?

  • Это RPC?

  • Какова её цель?

На эти и многие другие вопросы можно ответить, если назвать функцию надлежащим образом.

3.3.1.1 Все имена функций должны быть глаголами

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

Функции OnRep, обработчики событий и диспетчеры событий являются исключением из этого правила.

Хорошие примеры:

  • Fire - хороший пример, если в классе Character/Weapon, так как в этом случае она имеет контекст. Плохо, если в Barrel/Grass/любом другом неоднозначном контексте.

  • Jump - хороший пример, если в классе персонажа, в противном случае нужен контекст.

  • Explode

  • ReceiveMessage

  • SortPlayerArray

  • GetArmOffset

  • GetCoordinates

  • UpdateTransforms

  • EnableBigHeadMode

Плохие примеры:

  • Dead - мёртв или умрёт?

  • Rock

  • ProcessData - неоднозначно, поскольку эти слова ничего не значат.

  • PlayerState - только лишь существительные всегда являются неоднозначными для функцией.

  • Color - глагол без контекста или неоднозначное существительное.

3.3.1.2 Свойство RepNotify всегда OnRep_Variable

Все функции-уведомления при репликации должны иметь вид OnRep_Variable. Это принудительное требование редактора Blueprint. Однако, если вы пишете в C++ функцию OnRep, она также должна следовать этому соглашению при использовании её в Blueprint.

3.3.1.3 Информационные функции, возвращающие bool, должны задавать вопросы

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

Хорошие примеры:

  • IsDead

  • IsOnFire

  • IsAlive

  • IsSpeaking

  • IsHavingAnExistentialCrisis

  • IsVisible

Плохие примеры:

  • Fire - в огне сейчас или надо стрелять?

  • OnFire - можно спутать с диспетчером событий для стрельбы.

  • Dead - мёртв или умрёт?

  • Visibility - видно или нет, или надо установить видимость?

3.3.1.4 Обработчики событий и диспетчеры должны начинаться сOn

Использовать слово Handle не допускается, поскольку Unreal использует именно On, тогда как другие фреймворки могут использовать Handle вместо On.

Хорошие примеры:

  • OnDeath - распространённое словосочетание в играх

  • OnPickup

  • OnReceiveMessage

  • OnMessageRecieved

  • OnTargetChanged

  • OnClick

  • OnLeave

Плохие примеры:

  • OnData

  • OnTarget

  • HandleMessage

  • HandleDeath

3.3.1.5 Удаленные вызовы процедур должны начинаться с префикса Target

Каждый раз, когда создается RPC, функция должна иметь префикс Server, Client или Multicast. Без исключений.

После префикса следуйте всем остальным правилам именования функций.

Хорошие примеры:

  • ServerFireWeapon

  • ClientNotifyDeath

  • MulticastSpawnTracerEffect

Плохие примеры:

  • FireWeapon - не указывает, что это какой-то RPC.

  • ServerClientBroadcast - запутанно.

  • AllNotifyDeath - используйте Multicast, никогда All.

  • ClientWeapon - нет глагола, двусмысленно.

3.3.2 Все функции должны иметь Return Nodes

Все функции должны иметь Return Nodes, причём без исключений.

Return Nodes явно отмечают, что функция завершила своё выполнение. В мире, где Blueprints могут быть заполнены узлами Sequence, ForLoopWithBreak и движением линии Exec в обратном направлении (например, на Break у цикла после Loop и Branch), явный поток выполнения важен для удобочитаемости, обслуживания и упрощения отладки.

Компилятор Blueprint может отслеживать поток выполнения и предупреждать вас, если есть ветвь вашего кода с необработанным Return Node.

Например, если программист добавляет новую линию в узле Sequence или добавляет логику после завершения цикла, но при этом выход из функции происходит во время итерации, то такая ситуация часто приводит к случайной ошибке. Предупреждения компилятора Blueprint немедленно предупредят об этих проблемах.

3.3.3 Ни одна функция не должна иметь более 50 узлов

Ни одна функция не должна иметь более 50 узлов. Любая такая большая функция должна быть разбита на более мелкие функции для удобочитаемости и простоты обслуживания.

Следующие узлы не учитываются, поскольку считается, что они не увеличивают сложность функции:

  • Комментарии

  • Route

  • Cast

  • Получение переменной

  • Breaking структуры

  • Входной узел функции

  • Self

3.3.4 Все публичные (public) функции должны иметь описание

Это правило в большей степени относится к общедоступным или Blueprints для marketplace, чтобы другим было легче ориентироваться и использовать ваш API.

Проще говоря, любая функция, которая имеет спецификатор доступа public, должна иметь своё описание.

3.3.5 Все пользовательские статические BlueprintCallable функции подключаемых модулей должны быть классифицированы по имени подключаемого модуля

Если ваш проект включает модуль, который определяет static BlueprintCallable функции, их категория должна быть установлена ​​на имя подключаемого модуля.

Например, Zed Camera Interface или Zed Camera Interface | Image Capturing.

3.4 Blueprint Graphs

В этом разделе рассматриваются вещи, которые применимы ко всем Blueprint Graphs.

3.4.1 Никакого спагетти-кода

Провода (линии) должны иметь чёткие начало и конец. Многие из следующих разделов посвящены уменьшению количества спагетти-кода.

3.4.2 Выравнивание проводов, а не узлов

Всегда выравнивайте провода, а не узлы. Вы не всегда можете контролировать размер и расположение выводов на узле, но вы всегда можете контролировать расположение узла и, таким образом, управлять проводами. Прямые провода обеспечивают чёткий линейный поток. Волнистые провода, наоборот, затрудняют понимание. Вы можете выпрямить провода с помощью команды «Straighten Connections» с выбранными узлами. Горячая клавиша: Q.

Хороший пример: вершины узлов расположены в шахматном порядке, чтобы сохранить идеально прямую белую линию выполнения.

Плохой пример: верхние части узлов выровнены, создавая волнистую белую линию выполнения.

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

3.4.3 Белые линии Exec имеют наивысший приоритет

Если вам когда-нибудь придется выбирать между выпрямлением линии выполнения (белой линии) и выпрямлением каких-либо линий данных, то всегда выпрямляйте именно линию выполнения.

3.4.4 Код должен быть разумно прокомментирован

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

3.4.5 Код должен обрабатывать ошибки Cast, где это уместно

Если функция или событие предполагает, что приведение всегда завершается успешно, оно должно соответствующим образом сообщать об ошибке в логике, если приведение не удаётся. Это позволяет другим узнать, почему то, что «должно работать», не работает. Функция также должна корректно работать и при неудачном приведении, если известно, что оно может быть таковым.

Это не означает, что ошибка приведения в каждом узле Cast должна обрабатываться. Во многих случаях, особенно в случаях, связанных с такими вещами, как коллизия, ожидается, что поток выполнения просто завершится при неудачном приведении типа.

3.4.6 В коде не должно быть висящих/свободных/мёртвых узлов

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

4. Static Meshes

В этом разделе основное внимание будет уделено ассетам Static Mesh и их внутреннему устройству.

4.1 Static Mesh UV

Если Linter сообщает о плохих UV-развертках, и вы не можете это отследить, то откройте полученный .log файл в папке вашего проекта Saved/Logs, чтобы узнать точную причину. Я надеюсь включить эти сообщения в отчёт Linter в будущем.

4.1.1 Все Static Meshes должны иметь UV

Довольно просто. Все Static Meshes, независимо от того, как они будут использоваться, должны иметь UV.

4.1.2 Все Static Meshes не должны иметь перекрывающихся UV для карт освещения

Довольно просто. Все Static Meshes, независимо от того, как они будут использоваться, должны иметь корректные неперекрывающиеся UV-развёртки.

4.2 LOD должны быть настроены правильно

Это субъективно для каждого проекта, но, как правило, любой Static Mesh, который можно увидеть на разных расстояниях, должен иметь правильные уровни детализации.

4.3 Модульные ассеты без сокетов должны четко привязываться к Static Mesh

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

От проекта зависит следует ли привязываться к сетке размером 2 юнита или к сетке с размером 10 юнитов. Однако, если вы создаете модульные Static Mesh без сокетов для marketplace, то требование Epic состоит в том, чтобы они чётко привязывались, когда сетка установлена ​​​​на 10 юнитов или больше.

4.4 Все Static Meshes должны иметь коллизию

Независимо от того, будет ли у ассета включена коллизия на уровне, все Static Meshes должны иметь правильное определение коллизий. Это помогает движку с такими вещами, как расчёт границ, occlusion и освещение. Коллизия также должна иметь правильную форму, подходящую для данного Static Mesh.

4.5 Все Static Meshes должны быть правильно масштабированы

Это субъективно для каждого проекта, однако, все Static Meshes должны быть правильно масштабированы для проекта. Дизайнеры уровней или Blueprint-разрботчики не должны подгонять масштаб. Масштабирование они должны рассматривать только как переопределение масштаба, а не как его коррекцию.

5. Niagara

В этом разделе основное внимание будет уделено ассетам Niagara и их внутреннему устройству.

5.1 Без пробелов, всегда

6. Уровни/карты

В этом разделе основное внимание будет уделено ассетам уровней и их внутренностям.

6.1 Отсутствие ошибок или предупреждений

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

Вы можете запустить проверку карты на открытом уровне в редакторе с помощью консольной команды: «map check».

Обратите внимание: Linter еще более строг к проверкам карт, чем редактор в настоящее время, и будет ловить даже ошибки загрузки, которые редактор исправляет самостоятельно.

6.2 Освещение должно быть рассчитано

Во время разработки это нормально, когда уровни иногда не имеют рассчитанного освещения. Однако, при выполнении тестовой/внутренней/shipping сборки или любой другой сборки, которая должна быть распространена, освещение всегда должно быть рассчитано.

6.3 Игрок не должен видеть пересекающиеся текстуры (Z Fighting)

На уровнях не должно быть пересекающихся текстур, видимых игроку.

6.4 Особые правила Marketplace

Если проект будет продаваться на UE Marketplace, он должен соответствовать этим правилам.

6.4.1 Overview уровень

Если ваш проект содержит ассеты, которые необходимо визуализировать или продемонстрировать, в вашем проекте должна быть карта с названием «Overview».

Например, InteractionComponent_Overview.

6.4.2 Demo уровень

Если ваш проект содержит ассеты, которые должны быть продемонстрированы или снабжены каким-либо учебным пособием, в вашем проекте должна быть карта с названием «Demo». Этот уровень также должен содержать документацию в той или иной форме, иллюстрирующую, как использовать ваш проект. Хорошие примеры того, как это сделать, см. в проекте Epic Content Examples.

Если ваш проект представляет собой некую игровую механику, а не арт-пак, то эта карта может быть такой же, что и карта «Overview».

Например, InteractionComponent_Overview_Demo, ExplosionKit_Demo.

7. Текстуры

В этом разделе основное внимание будет уделено текстурным ассетам и их внутреннему устройству.

7.1 Размеры должны быть степенью двойки

Все текстуры, кроме текстур пользовательского интерфейса, должны иметь размеры, кратные степени двойки. Текстуры не обязательно должны быть квадратными.

Например, 128x512, 1024x1024, 2048x1024, 1024x2048, 1x512.

7.2 Плотность текстур должна быть одинаковой

Все текстуры должны иметь размер, соответствующий их стандартному варианту использования. Конкретная плотность варьируется от проекта к проекту, но все текстуры в этом проекте должны иметь одну и ту же плотность.

Например, если плотность текстуры проекта составляет 8 пикселей на 1 юнит, текстура, предназначенная для применения к кубу 100x100 единиц, должна иметь размер 1024x1024, поскольку это ближайшая степень числа 2, соответствующая плотности текстуры проекта.

7.3 Текстуры не должны быть больше 8192 пикселей

Никакая текстура не должна иметь размер, превышающий 8192 пикселя, если только у вас нет явной причины для этого. Как правило, использование такой большой текстуры - это просто пустая трата ресурсов.

7.4 У текстур должна быть выбрана правильная группа

У каждой текстуры есть свойство Texture Group, используемое для LOD, и его следует правильно установить в зависимости от её использования. Например, все текстуры пользовательского интерфейса должны принадлежать группе UI.

Поправки

Мы рекомендуем вам скопировать это руководство и изменить правила, чтобы они соответствовали стилю вашей команды. Ниже вы можете перечислить некоторые поправки к руководству по стилю.

Отличия от оригинала

  1. Убрано упоминание в руководстве, что речь идёт о 4-й версии, поскольку оно актуально и для 5-й версии движка в том числе.

Слово «карта» обычно относится к тому, что обычный человек называет «уровнем», и может использоваться взаимозаменяемо. Смотрите историю этого термина .

Эти принципы были адаптированы из .

При работе в команде или при обсуждении в сообществе, таком как , гораздо проще помогать и просить о помощи, когда люди последовательны. Никому не нравится распутывать чьи-то спагетти в Blueprint или иметь дело с ассетами, названия которых они не могут понять.

Prefix и Suffix должны определяться типом ассета с помощью таблиц .

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

См.

См.

Если вы используете , описанное выше, использование папок по типу Meshes, Textures и Materials является избыточной практикой, поскольку типы ресурсов уже отсортированы по префиксу, а также могут быть отфильтрованы в Content Browser.

2.1.1 Всегда используйте PascalCase

См. .

Повторно применяя , никогда не используйте пробелы. Пробелы могут привести к сбою различных инструментов и процессов. В идеале, корень вашего проекта также не должен содержать пробелов. Например, он может находиться в D:\Project вместо C:\Users\My Name\My Documents\Unreal Projects.

Если одного из ваших игровых персонажей зовут Zoë, имя его папки должно быть Zoe. Символы Unicode могут быть хуже, чем для различных инструментов, и некоторые части UE также не поддерживают символы Unicode в путях.

В связи с этим, если в вашем проекте есть , а имя пользователя вашего компьютера содержит символ Unicode (например, ваше имя Zoë), любой проект, расположенный в вашей My Documents папке, будет страдать от этой проблемы. Часто простое перемещение вашего проекта в директорию наподобие D:\Project, решает эти загадочные проблемы.

Папка Developers не предназначена для ассетов, от которых зависит ваш проект, и, следовательно, не зависит от проекта. См. для получения подробной информации об этом.

У каждого ассета должна быть цель, иначе ему не место в проекте. Если ассет является экспериментальным, и он не должен использоваться в проекте, его следует поместить в папку .

Расширение версии . Если член команды решит добавить образец, файлы шаблонов или ассеты, купленные на Marketplace, то гарантировано, что эти новые ресурсы не будут влиять на существующие ассеты в вашем проекте.

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

При соблюдении наихудший конфликт на Marketplace может возникнуть, если два пакета на рынке имеют одинаковый образец контента Epic. Если все ваши ассеты находятся в папке конкретного проекта, включая образец контента, который вы, возможно, переместили в свою папку, ваш проект никогда не сломается.

Это можно рассматривать как псевдо-исключение для .

Это не относится к таким ассетам, как текстуры и материалы. Обычно папка Rocks имеет большое количество текстур, если в ней много камней, однако эти текстуры, как правило, связаны только с несколькими конкретными камнями, поэтому они просто должны иметь соответствующие имена. Даже если эти текстуры являются частью .

В этом разделе основное внимание будет уделено классам Blueprint и их внутреннему устройству. Когда это возможно, правила стиля соответствуют .

Все имена небулевских переменных должны быть в форме .

IsEnemy -

При написании функции, которая не изменяет состояние и не модифицирует какой-либо объект, и предназначена исключительно для получения информации, состояния или вычисления значения да/нет, должна задавать вопрос. Здесь также применимо .

HasWeapon -

WasCharging - Используйте «было» при обращении к «предыдущему кадру» или «предыдущему состоянию».

CanReload -

Любая функция, которая обрабатывает событие или отправляет событие, должна начинаться с On и дальше следовать . Однако, глагол может перейти в конец, если прошедшее время читается лучше.

Как упоминалось в , пробелы и все пробельные символы запрещены в идентификаторах. Это особенно важно для систем Niagara, поскольку это значительно усложняет, если не делает невозможной работу с HLSL и другими средствами сценариев в Niagara при попытке сослаться на идентификатор.

См. относительно «уровней» и «карт».

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

Префикс для StaticMesh - SM_, в оригинале S_ (раздел ). Поскольку де-факто SM_ - это самый популярный префикс для StaticMesh, тогда как S_ очень часто встречается у Sound Wave.

Добавлен префикс - NS_ для Niagara System (раздел ).

Префикс для структур - F, в оригинале F или S (раздел ). Я посчитал, что здесь не должно быть выбора, и поскольку в C++ используется префикс именно F, то и в Blueprint для консистентности я выбрал именно F.

Префиксы для Sound Cue и Sound Wave - S_, в оригинале A_ (раздел ). S_ - более логично, а префикс A_ был выбран в оригинале явно по причине того, что S_ они отдали Static Mesh.

Добавлены модификаторы имён ассетов для Niagara (раздел ).

Раздел - изменён, так как в оригинале написано, что, если переменных мало, то категория для Editable-переменных не нужна. Я же считаю, что всегда все Editable-переменные должны находиться в отдельной категории.

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

🦞
здесь
руководства по стилю idomatic.js
Ребекка Мерфи
Unreal Slackers
пробелы
необъяснимые проблемы
стандарту кодирования Epic
"Is" - это глагол.
"Has" - это глагол.
«Was» — это прошедшее время от «be».
"Can" - это глагол.
рекомендациями Epic
ссылка на оригинал
отличия
Важная терминология
Уровни/карты
Идентификаторы
Варианты написания идентификаторов
Переменные/Свойства
Свойство
Переменная
0. Принципы
0.1 Если в вашем проекте уже есть руководство по стилю, вы должны следовать ему
0.2 Вся структура, ассеты и код в любом проекте должны выглядеть так, как будто их создал один человек, независимо от того, сколько людей внесли свой вклад
0.3 Друзья не позволяют своим друзьям иметь плохой стиль
0.4 Команда без руководства по стилю — не моя команда
0.5 Не нарушайте закон
00. Общепринятые правила
00.1 Запрещённые символы
Идентификаторы
1. Соглашения об именовании ассетов
1.1 Базовое имя ассета - Prefix_BaseAssetName_Variant_Suffix
1.1 Примеры
1.2 Модификаторы имени ассета
1.2.1 Наиболее популярные
1.2.2 Анимации
1.2.3 Искусственный интеллект
1.2.4 Blueprints
1.2.5 Материалы
1.2.6 Текстуры
1.2.6.1 Упакованные текстуры
1.2.7 Разное
1.2.8 Paper 2D
1.2.9 Физика
1.2.10 Звуки
1.2.11 Пользовательский интерфейс
1.2.12 Эффекты
2. Структура каталога Content
2e1 Пример структуры каталога Content
2.1 Имена папок
2.1.1 Всегда используйте PascalCase
2.1.2 Никогда не используйте пробелы
2.1.3 Никогда не используйте символы Unicode и другие символы
2.2 Используйте папку верхнего уровня для проекта
2.2.1 Нет глобальных ассетов
2.2.2 Уменьшение конфликтов при миграции
2.2.2e1 Пример мастер-материала
2.2.3. Образцы, шаблоны и контент из Marketplace не создают риска внедрения
2.2.4 DLC, подпроекты и патчи легко поддерживать
2.3 Используйте папку Developers для локального тестирования
2.4 Все файлы Map лежат папке с названием Maps
2.5 Используйте папку Core для важных Blueprints и других ассетов
2.6 Не создавайте папки с именами Assets или AssetTypes
2.6.1 Создание папки с именем Assets - излишне
2.6.2 Создание папки с именем Meshes, Textures или Materials является избыточным
2.7 Очень большие наборы ассетов получают собственную папку
2.8 MaterialLybrary
2.9 Никаких пустых папок
3. Blueprints
3.1 Компиляция
3.2 Переменные
3.2.1 Именование
3.2.1.1 Существительные
3.2.1.2 PascalCase
3.2.1.2e Примеры
3.2.1.3 Префикс b для boolean
3.2.1.4 Имена для boolean
3.2.1.4.1 Общая и независимая информация о состоянии
3.2.1.4.2 Комплексные состояния
3.2.1.5 Учитывайте контекст
3.2.1.5e Примеры
3.2.1.6 Не включайте имена атомарных типов
3.2.1.7 Включайте имена неатомарных типов
3.2.1.8 Массивы
3.2.2 Редактируемые переменные
3.2.2.1 Подсказки
3.2.2.2 Слайдер и диапазоны значений
3.2.3 Категории
3.2.4 Уровень доступа к переменным
3.2.4.1 Закрытые переменные
3.2.5 Дополнительные переменные
3.2.6 Transient-переменные
3.2.7 Config-переменные
3.3 Функции, события и диспетчеры событий
3.3.1 Именование функций
3.3.1.1 Все имена функций должны быть глаголами
3.3.1.2 Свойство RepNotify всегда OnRep_Variable
3.3.1.3 Информационные функции, возвращающие bool, должны задавать вопросы
3.3.1.4 Обработчики событий и диспетчеры должны начинаться с On
3.3.1.5 Удаленные вызовы процедур должны начинаться с префикса Target
3.3.2 Все функции должны иметь Return Nodes
3.3.3 Ни одна функция не должна иметь более 50 узлов
3.3.4 Все публичные (public) функции должны иметь описание
3.3.5 Все пользовательские статические BlueprintCallable функции подключаемых модулей должны быть классифицированы по имени подключаемого модуля
3.4 Blueprint Graphs
3.4.1 Никакого спагетти-кода
3.4.2 Выравнивание линий, а не узлов
3.4.3 Белые линии Exec имеют наивысший приоритет
3.4.4 Код должен быть разумно прокомментирован
3.4.5 Код должен обрабатывать ошибки Cast, где это уместно
3.4.6 В коде не должно быть висящих/свободных/мёртвых узлов
4. Static Meshes
4.1 Static Mesh UV
4.1.1 Все Static Meshes должны иметь UV
4.1.2 Все Static Meshes не должны иметь перекрывающихся UV для карт освещения
4.2 LOD должны быть настроены правильно
4.3 Модульные ассеты без сокетов должны четко привязываться к Static Mesh
4.4 Все Static Meshes должны иметь коллизию
4.5 Все Static Meshes должны быть правильно масштабированы
5. Niagara
5.1 Без пробелов, всегда
6. Уровни/карты
6.1 Отсутствие ошибок или предупреждений
6.2 Освещение должно быть рассчитано
6.3 Игрок не должен видеть пересекающиеся текстуры (Z Fighting)
6.4 Особые правила Marketplace
6.4.1 Overview уровень
6.4.2 Demo уровень
7. Текстуры
7.1 Размеры должны быть степенью двойки
7.2 Плотность текстур должна быть одинаковой
7.3 Текстуры должны быть не больше 8192
7.4 У текстур должна быть выбрана правильная группа
модификаторов имени ассета
базовым именем ассета
⬆ Наверх
соглашение об именовании префиксов
*
варианты написания идентификаторов
2.1.1
папка Developers
Developers
2.2.2
правилу папки верхнего уровня
2.2
2.6
библиотеки материалов
⬆ Наверх
PascalCase
правило глагола
правилу глагола
⬆ Наверх
⬆ Наверх
00.1 Запрещенные идентификаторы
⬆ Наверх
Примечание по терминологии
⬆ Наверх
⬆ Наверх
1.2.1
1.2.1
1.2.4
1.2.10
1.2.12
3.2.3
3.2.4
3.2.4.1
Должны находиться в папке Maps
текстуры
упакованные текстуры