JD2022-TU1/.agents/rules/ubiart_coding_standards.md

221 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UbiArt Framework & Just Dance Coding Standards and Rules
Этот документ описывает правила архитектуры, стиля кода и соглашений, принятых в проекте Just Dance (UbiArt Framework). Все изменения и новые модули должны строго следовать этим правилам.
---
## 1. Типы данных и язык C++
1. **Базовые типы**:
- Никогда не использовать стандартный `bool` в коде движка; использовать `bbool`, `btrue`, `bfalse`.
- Целочисленные типы строго фиксированной длины: `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`.
- Числа с плавающей точкой: `f32`, `f64`.
2. **Строки и идентификаторы**:
- `String8` — для строк ASCII / UTF-8 и динамических путей.
- `StringID` — для хешированных идентификаторов (32-битный CRC/хеш).
- `SafeStringID` — когда требуется отладочное сохранение исходной строки вместе с хешем.
- `Path` / `DynPath` — для путей к ресурсам игры.
- Для пустой строки использовать константу `String8::emptyString`.
3. **Контейнеры**:
- Использовать движковые макросы-обёртки:
- `ITF_VECTOR<T>` (вместо `std::vector`)
- `ITF_MAP<Key, Value>` (вместо `std::map`)
- `ITF_LIST<T>` (вместо `std::list`)
- `SafeArray<T>` для фиксированных безопасных массивов.
4. **Управление памятью**:
- Использовать системные аллокаторы `ITF::Memory::mallocCategory(...)` или `ITF_NEW` / `ITF_DELETE`.
- Использовать `AutoPointer<T>` или `RefCountingPointer<T>` / `ObjectRef` для объектов с подсчётом ссылок.
---
## 2. Соглашения об именовании (Naming Conventions)
1. **Классы и структуры**:
- `PascalCase`.
- Для компонентов и логики Just Dance использовать префикс `JD_` (например, `JD_SongDatabaseComponent`, `JD_ScoreComponent`).
- Для подсистем движка UbiArt использовать `UAF_` или смысловой префикс (`CSerializerObjectLua`, `ActorComponent`).
2. **Переменные-члены (Member variables)**:
- Префикс `m_` с последующим `camelCase` или `PascalCase`: `m_pathCreationFormats`, `m_template`, `m_isPaused`.
3. **Параметры функций (Function parameters)**:
- Обязательный префикс `_`: `void Update(f32 _dt)`, `const String8& getPath(const char* _formatName)`.
4. **Методы**:
- `camelCase`: `onActorLoaded()`, `needsUpdate()`, `needsDraw()`, `getPathCreationFormat()`.
5. **Макросы и CRC**:
- `UPPER_CASE_WITH_UNDERSCORES`: `JD_SongDatabaseComponent_CRC`, `ITF_ASSERT_CRASH`.
---
## 3. Архитектурный паттерн: Компоненты и Шаблоны (ActorComponent & Template)
Каждая логическая сущность разделяется на пару: **Шаблон (Template)** и **Экземпляр (Component)**.
1. **Шаблон компонента (`ActorComponent_Template`)**:
- Хранит неизменяемые данные ассета (конфигурацию, пути, базовые параметры).
- Объявляется с макросом `DECLARE_ACTORCOMPONENT_TEMPLATE(ComponentClass)`:
```cpp
class JD_MyComponentTemplate : public ActorComponent_Template
{
DECLARE_OBJECT_CHILD_RTTI(JD_MyComponentTemplate, ActorComponent_Template, JD_MyComponentTemplate_CRC)
DECLARE_ACTORCOMPONENT_TEMPLATE(JD_MyComponent)
DECLARE_SERIALIZE()
public:
JD_MyComponentTemplate();
virtual ~JD_MyComponentTemplate();
private:
f32 m_someParam;
};
```
2. **Экземпляр компонента (`ActorComponent`)**:
- Хранит динамическое состояние актора в мире.
- Получает шаблон через `getTemplate()`:
```cpp
class JD_MyComponent : public ActorComponent
{
DECLARE_OBJECT_CHILD_RTTI(JD_MyComponent, ActorComponent, JD_MyComponent_CRC)
DECLARE_SERIALIZE()
DECLARE_VALIDATE_COMPONENT()
public:
JD_MyComponent();
virtual ~JD_MyComponent();
virtual bbool needsUpdate() const override { return btrue; }
virtual bbool needsDraw() const override { return bfalse; }
virtual void Update(f32 _dt) override;
virtual void onActorLoaded(Pickable::HotReloadType _hotReload) override;
ITF_INLINE const JD_MyComponentTemplate* getTemplate() const
{
return static_cast<const JD_MyComponentTemplate*>(m_template);
}
};
```
---
## 4. Система RTTI и Сериализация
1. **RTTI**:
- Каждый класс, наследующий `IRTTIObject`, обязан иметь CRC-дефайн:
`#define ClassName_CRC ITF_GET_STRINGID_CRC(ClassName, <UniqueInt32>)`
- В хедере: `DECLARE_OBJECT_CHILD_RTTI(ClassName, BaseClass, ClassName_CRC)`
- В `.cpp`: `IMPLEMENT_OBJECT_RTTI(ClassName)`
- Приведение типов: `DYNAMIC_CAST(pointer, TargetType)`
2. **Сериализация**:
- В хедере: `DECLARE_SERIALIZE()`
- В `.cpp`:
```cpp
BEGIN_SERIALIZATION_CHILD(ClassName)
SERIALIZE_MEMBER("fieldName", m_fieldName)
SERIALIZE_CONTAINER("containerName", m_container)
END_SERIALIZATION()
```
3. **Валидация компонентов**:
- В хедере: `DECLARE_VALIDATE_COMPONENT()`
- В `.cpp`:
```cpp
BEGIN_VALIDATE_COMPONENT(ClassName)
END_VALIDATE_COMPONENT()
```
---
## 5. Ассерты и обработка ошибок
- Использовать `ITF_ASSERT_CRASH(condition, format, ...)` для критических инвариантов.
- Использовать `ITF_ASSERT(condition, format, ...)` для базовых проверок.
- Использовать `ITF_WARNING("Category", condition, format, ...)` для некритичных замечаний.
---
## 6. Правила работы с Lua-файлами данных (.tpl, .act, .isc, .ilu)
1. Все данные шаблонов, акторов и сцен пишутся в формате Lua-таблиц:
```lua
params =
{
NAME = "Actor_Template",
Actor_Template =
{
SCALE = vector2dNew(1.0, 1.0),
COMPONENTS =
{
{
NAME = "MyComponent_Template",
MyComponent_Template =
{
someParam = 10.0,
},
},
},
},
}
```
2. Векторы конструируются встроенными C-функциями: `vector2dNew(x, y)` и `vectorNew(x, y, z)`.
3. Ссылки на внешние шаблоны задаются через параметр `LUA = "path/to/template.tpl"`.
4. Вспомогательные настройки разбиваются на файлы `.ilu` и подключаются через `includeReference(...)`.
---
## 7. Локализация и отображение текста в UI (Localization Standards)
1. **Категорический запрет на хардкод текстовых строк в C++**:
- Запрещено писать строковые литералы на естественных языках (русский, английский и др.) прямо в коде движка (`.cpp` / `.h`).
- Запрещено делать проверки языка в коде для подстановки текста, например:
```cpp
// ТАК ДЕЛАТЬ КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО!
if (LOCALISATIONMANAGER->getCurrentLanguage() == ITF_LANGUAGE_RUSSIAN)
textBox->setText("Карты отсутствуют");
```
2. **Декларативное задание текста через `locId` в сценах (`.isc`, `.act`, `.tpl`)**:
- Движок UbiArt автоматически загружает и отображает локализованный текст для компонента `UITextBox`, если в XML/сцене указан атрибут `locId="..."`:
```xml
<UITextBox locId="12896" rawText="" ... />
```
- При инициализации актора движок самостоятельно извлекает нужную строку из таблицы локализации для активного языка. Дополнительный C++ код для этого не требуется.
3. **Категорический запрет на привязку статического текста к маркерам в C++**:
- **Запрещено** добавлять маркеры (`<MARKERS VAL="..." />`) на текстовые акторы и писать C++ код (`getFirstActorByMarker`, `setLoc()`) исключительно для того, чтобы задать локализацию или текст элементу интерфейса.
- Маркеры в UbiArt предназначены строго для динамической логики, интерактивных контролов и анимаций (Tape), а не для локализации.
- Статические надписи, предупреждения, заглушки отсутствия контента (empty states), подсказки и заголовки настраиваются исключительно декларативно в файлах сцен (`.isc`) через атрибут `locId`.
4. **Когда допустимо управление текстом из C++**:
- Только для динамических данных времени выполнения (имя игрока, набранные очки, числовой счетчик, динамические названия категорий при скролле сетки из событий движка, таймеры).
- Если в редких случаях коду необходимо динамически изменить локализацию существующего элемента, используется `textBox->setLoc(LocalisationId(...))`, но без создания лишних маркеров под статические элементы.
5. **Мультиязычность**:
- Всегда опираться на существующие `LocalisationId` из базы игры (`localisation.json` / `.loc8`), которые уже переведены на все поддерживаемые платформой языки (английский, французский, немецкий, испанский, итальянский, русский, японский, китайский и т.д.), либо при необходимости добавлять новые ID в файлы локализации сразу для всех языков.
---
## 8. Архитектура UI, анимации (Tape) и разделение ответственности (Presentation vs Logic)
1. **Принцип разделения ответственности (Separation of Concerns)**:
- **C++ код** (`GameScreens`, `GSStates`, контроллеры) отвечает исключительно за **состояние и логику** (например: "поиск пуст" vs "поиск с результатами", "активен" vs "неактивен", переходы между состояниями).
- **Сцены (`.isc`) и анимационные ленты (`.tape`)** отвечают за **визуальное представление** (альфа-прозрачность, плавные переходы, анимации появления/скрытия, масштаб и взаимное расположение элементов).
2. **Категорический запрет на процедурное переключение видимости в C++**:
- **Запрещено** писать C++ код с ручным поиском акторов по маркерам (`getFirstActorByMarker`), ручными вызовами `actor->enable()` / `actor->disable()` и установкой альфы (`textBox->setAlpha()`), если в интерфейсе для этого предназначен `.tape`.
- Процедурное скрытие элементов ломает стейт-машину ленты UbiArt, приводит к рассинхронизации состояний экрана при переходах между вкладками и засоряет логику фреймворка ненужным низкоуровневым кодом.
3. **Использование анимационных меток (Labels) и `UIComponent::playAnim`**:
- Каждое состояние экрана/страницы оформляется в `.tape` в виде ключевых кадров с соответствующей меткой (Label), например `NORMAL`, `EMPTY`, `IN`, `OUT`, `LOOP`, `FOCUSED`.
- В C++ переключение состояния выполняется одной декларативной командой:
```cpp
// Пример: переключение состояния страницы поиска (пустая / с песнями)
m_searchPageUIComponent->playAnim(hasNoSongs ? "EMPTY" : "NORMAL");
```
- Механизм работы: `UIComponent` на `SubSceneActor` автоматически обнаруживает корневой компонент субсцены (`UIRootComponent`) и делегирует вызов в `TapeCase_Component`, который запускает воспроизведение соответствующего блока в `.tape`.
4. **Адресация акторов в `.tape` (Friendly Names vs Markers)**:
- Треки внутри `.tape` (`Track2D`, `AlphaTrack`, `TransformTrack` и др.) адресуют акторы по их понятным именам:
```lua
ActorPaths = { { VAL = "friendly_name" } }
```
что точно соответствует атрибуту `USERFRIENDLY="friendly_name"` в `.isc`.
- **Для анимации в `.tape` маркеры (`<MARKERS VAL="..." />`) НЕ НУЖНЫ**.
- Не добавлять в `.isc` маркеры для элементов, управляемых через Tape. Маркеры в `.isc` используются **только** тогда, когда C++ коду действительно требуется получить указатель на интерактивный контрол через `getFirstActorByMarker` (например, сетку, карусель, поле ввода).
5. **Правильные настройки текстовых и UI-акторов в `.isc` для работы с Tape**:
- `DEFAULTENABLE="1"`: акторы по умолчанию должны быть активны в сцене, а их отображение/сокрытие контролируется ключевыми кадрами `AlphaTrack` ленты `.tape`.
- `alpha="1.000000"` у `UITextBox`: базовое значение альфы у компонента должно быть равно единице, чтобы треки ленты могли полностью регулировать видимость от 0.0 до 1.0.
- `useParentAlpha="0"` в `<Bind>`: если прозрачность дочернего актора должна управляться собственным треком в Tape независимо от альфы родителя.