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