# 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` (вместо `std::vector`) - `ITF_MAP` (вместо `std::map`) - `ITF_LIST` (вместо `std::list`) - `SafeArray` для фиксированных безопасных массивов. 4. **Управление памятью**: - Использовать системные аллокаторы `ITF::Memory::mallocCategory(...)` или `ITF_NEW` / `ITF_DELETE`. - Использовать `AutoPointer` или `RefCountingPointer` / `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(m_template); } }; ``` --- ## 4. Система RTTI и Сериализация 1. **RTTI**: - Каждый класс, наследующий `IRTTIObject`, обязан иметь CRC-дефайн: `#define ClassName_CRC ITF_GET_STRINGID_CRC(ClassName, )` - В хедере: `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 ``` - При инициализации актора движок самостоятельно извлекает нужную строку из таблицы локализации для активного языка. Дополнительный C++ код для этого не требуется. 3. **Категорический запрет на привязку статического текста к маркерам в C++**: - **Запрещено** добавлять маркеры (``) на текстовые акторы и писать 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` маркеры (``) НЕ НУЖНЫ**. - Не добавлять в `.isc` маркеры для элементов, управляемых через Tape. Маркеры в `.isc` используются **только** тогда, когда C++ коду действительно требуется получить указатель на интерактивный контрол через `getFirstActorByMarker` (например, сетку, карусель, поле ввода). 5. **Правильные настройки текстовых и UI-акторов в `.isc` для работы с Tape**: - `DEFAULTENABLE="1"`: акторы по умолчанию должны быть активны в сцене, а их отображение/сокрытие контролируется ключевыми кадрами `AlphaTrack` ленты `.tape`. - `alpha="1.000000"` у `UITextBox`: базовое значение альфы у компонента должно быть равно единице, чтобы треки ленты могли полностью регулировать видимость от 0.0 до 1.0. - `useParentAlpha="0"` в ``: если прозрачность дочернего актора должна управляться собственным треком в Tape независимо от альфы родителя.