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

16 KiB
Raw Permalink Blame History

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):
      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():
      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:
      BEGIN_SERIALIZATION_CHILD(ClassName)
          SERIALIZE_MEMBER("fieldName", m_fieldName)
          SERIALIZE_CONTAINER("containerName", m_container)
      END_SERIALIZATION()
      
  3. Валидация компонентов:
    • В хедере: 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)

  1. Все данные шаблонов, акторов и сцен пишутся в формате 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).
    • Запрещено делать проверки языка в коде для подстановки текста, например:
      // ТАК ДЕЛАТЬ КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО!
      if (LOCALISATIONMANAGER->getCurrentLanguage() == ITF_LANGUAGE_RUSSIAN)
          textBox->setText("Карты отсутствуют");
      
  2. Декларативное задание текста через locId в сценах (.isc, .act, .tpl):
    • Движок UbiArt автоматически загружает и отображает локализованный текст для компонента UITextBox, если в XML/сцене указан атрибут locId="...":
      <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++ переключение состояния выполняется одной декларативной командой:
      // Пример: переключение состояния страницы поиска (пустая / с песнями)
      m_searchPageUIComponent->playAnim(hasNoSongs ? "EMPTY" : "NORMAL");
      
    • Механизм работы: UIComponent на SubSceneActor автоматически обнаруживает корневой компонент субсцены (UIRootComponent) и делегирует вызов в TapeCase_Component, который запускает воспроизведение соответствующего блока в .tape.
  4. Адресация акторов в .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 (например, сетку, карусель, поле ввода).
  5. Правильные настройки текстовых и UI-акторов в .isc для работы с Tape:

    • DEFAULTENABLE="1": акторы по умолчанию должны быть активны в сцене, а их отображение/сокрытие контролируется ключевыми кадрами AlphaTrack ленты .tape.
    • alpha="1.000000" у UITextBox: базовое значение альфы у компонента должно быть равно единице, чтобы треки ленты могли полностью регулировать видимость от 0.0 до 1.0.
    • useParentAlpha="0" в <Bind>: если прозрачность дочернего актора должна управляться собственным треком в Tape независимо от альфы родителя.