221 lines
16 KiB
Markdown
221 lines
16 KiB
Markdown
# 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 независимо от альфы родителя.
|
||
|
||
|
||
|