Skip to content

Latest commit

 

History

223 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PRUnitySDK

Фреймворк для игр на Unity. Он задаёт форму приложения: точку входа, порядок инициализации, жизненный цикл компонентов, модель данных и способ, которым игра расширяет его поведение.

Что это значит на практике:

  • игра запускается из bootstrap-сцены, а порядок инициализации задают MethodHookAttribute и этапы, а не ваш Start;
  • компоненты наследуют PRMonoBehaviour и получают PRUpdate, паузу, игровое время и готовность через ReadySignal; обычный MonoBehaviour всего этого не получает;
  • поведение расширяется подключением к ядру, а не правкой ядра: хуки урона, провайдеры с автопоиском по атрибутам, фабрики окон, трекеры сущностей;
  • данные игры описаны его моделью: каталоги, определения предметов, награды, проекты.

Название историческое: строка PRUnitySDK зашита в пути Resources, пункты меню и сериализованные ассеты, поэтому переименование требует отдельной миграции.

Warning

Проект находится в активной разработке. Публичные API, структура каталогов и процесс инициализации могут изменяться. Перед обновлением фиксируйте используемый commit.

Возможности

Система Назначение
PRUnitySDK Центральная точка доступа к настройкам, менеджерам, трекерам и сервисам
Bootstrap Единая точка запуска и последовательная инициализация фреймворка
PRMonoBehaviour Базовый Unity-компонент с PR lifecycle, поддержкой паузы и физическими callback'ами
EventBus Глобальная типизированная шина событий с защитой от повторной подписки
PauseManager Раздельное управление общей, логической, музыкальной и другими видами паузы
PRTime / PRTimeScale Игровое и реальное время с учётом логической паузы и пользовательских time scale
PRCoroutineBase Обёртки над Unity-корутинами с единым запуском и остановкой
BackgroundTasks Работа по расписанию вне сцены и наблюдение за значениями, которые сами о себе не сообщают
HookSystem Последовательная обработка изменяемых и отменяемых событий
FlagsSystem Совместное управление состояниями объекта из нескольких источников
Entity / Items / Wallet / Reward Базовые модели сущностей, предметов, ресурсов и наград
GameRules Глобальные ограничения характеристик поверх персональных модификаторов
Conditions Настраиваемые правила «когда это разрешено»: ассетом или прямо в инспекторе
GameDataStorage Сохранение прогресса и состояния объектов сцены между запусками
State / Damage Переиспользуемые игровые модули
Quality / Localization / Logging Качество предметов, переводы и структурированное логирование

Карта фреймворка

Как части связаны между собой. Стрелка — «зависит от».

graph TD
    Boot["Bootstrap<br/>единственная точка входа"]
    Facade["PRUnitySDK<br/>facade, ServiceResolver"]
    Hooks["HookSystem<br/>MethodHook и стадии"]

    Boot --> Facade
    Facade --> Hooks

    subgraph BASE["Базис"]
        Models["Models<br/>Enumeration, ProjectData"]
        Utils["#Utils, #Extensions, Debug"]
        Attrs["@Attributes"]
    end

    subgraph LIFE["Жизненный цикл и время"]
        Mono["PRMonoBehaviour"]
        Pause["PauseSystem"]
        Time["PRTime, PRTimeScale"]
        Coro["Coroutines, Yields"]
        Bg["BackgroundTasks"]
    end

    subgraph DATA["Данные"]
        Storage["GameDataStorage<br/>ProjectData, ObjectState"]
        Db["#Database<br/>PRSDKDatabase, PRSDKSettings"]
        Paths["ResourcePaths"]
    end

    subgraph WORLD["Модель мира"]
        Entity["@Entity<br/>EntityBase, Metadata,<br/>Stats, Player"]
        Items["Items"]
        Wallet["Wallet"]
        Reward["Reward"]
        Rules["GameRules"]
        Props["PropertyContainer"]
        Flags["FlagsSystem"]
        Cond["@Conditions<br/>ICondition, ConditionBase"]
    end

    subgraph RUNTIME["Рантайм-инфраструктура"]
        Managers["@Managers<br/>PRManagerContainer"]
        Trackers["Trackers<br/>Entities, Players, Cameras, Windows"]
        Factories["Factories, ObjectPool"]
        Bus["@Events<br/>EventBus"]
        Bots["Bots"]
        Input["Input<br/>InputTranslator, PlayerInputState"]
    end

    subgraph UIL["Интерфейс"]
        Wnd["#UI<br/>MonoWindow, Notifiers,<br/>PRWindowsContainer"]
    end

    subgraph MODS["Модули"]
        Damage["@DamageSystem"]
        HitBox["HitBox"]
        Cam["Camera"]
        Tr["Translate"]
        Tween["DOTweenEffects"]
    end

    Editor["Core/Editor<br/>окна Database и PRUnitySDKDebug"]

    Mono --> Pause
    Mono --> Time
    Mono --> Bus
    Bg --> Mono

    Entity --> Models
    Entity --> Mono
    Entity --> Factories
    Entity --> Rules
    Entity --> Input
    Items --> Models
    Items --> Tr
    Props --> Models
    Flags --> Models
    Rules --> Models
    Reward --> Items
    Wallet --> Models
    Cond --> Items
    Cond --> Wallet

    Managers --> Storage
    Managers --> Items
    Trackers --> Entity
    Trackers --> Wnd
    Bus --> Items
    Bots --> Bus

    Wnd --> Mono
    Wnd --> Pause
    Cam --> Trackers
    Damage --> Entity
    Damage --> Hooks
    Damage --> Items
    HitBox --> Damage
    Tween --> Time

    Editor --> Entity
    Editor --> Db
    Editor --> Wnd
    Editor --> Bg
Loading

Игра к фреймворку не подмешивается: зависимость всегда направлена от игры к нему. Как именно к нему подключаться — в разделе «Какой механизм расширения выбрать».

Слои

Папка Что в ней
Assets/PRUnitySDK сам фреймворк: общее для любой игры
Assets/PRUnitySDKPrivate проектный слой: механики конкретной игры, надстроенные над фреймворком
Assets/PRUnityData данные игры: каталоги, настройки, проекты

Данные вынесены из фреймворка намеренно: его обновление не должно задевать содержимое игры, а один и тот же фреймворк обслуживает несколько игр. Какая из них собирается сейчас, указывает PRSDKProject — окно PRUnitySDK/Windows/Project. Подробности — в PRUnityData/README.md.

Опыт и уровни теперь тоже в проектном слое: XPManager, фоновое начисление и бустеры опыта живут в PRUnitySDKPrivate/Modules/@ProgressionModule.

Требования

Некоторые интеграции имеют дополнительные зависимости:

  • YG2.Integration требует YG2 Plugin и соответствующие модули YG2.

Установка

Репозиторий пока не оформлен как UPM-пакет: в корне отсутствует package.json. Размещайте содержимое репозитория внутри Assets/PRUnitySDK.

Через Git submodule:

git submodule add https://github.com/prethink/PRUnitySDK.git Assets/PRUnitySDK
git submodule update --init --recursive

Либо скачайте репозиторий и скопируйте его содержимое в:

Assets/PRUnitySDK

После импорта убедитесь, что Unity завершил компиляцию без ошибок и установлены перечисленные выше зависимости.

Быстрый старт

1. Подготовьте bootstrap-сцену

  1. Создайте отдельную сцену и добавьте её первой в File → Build Settings.
  2. Поместите в неё GameObject с компонентом Bootstrap.
  3. Добавьте основную игровую сцену следующей, с build index 1.

В текущей реализации после завершения инициализации Bootstrap вызывает переход на сцену с индексом 1. В Unity Editor класс PlayFromBootstrap автоматически назначает сцену с индексом 0 стартовой при входе в Play Mode.

Important

Если проект использует другую схему загрузки сцен, измените обработчик Bootstrap.OnInitialized() или переопределите bootstrap-процесс через OverrideBootstrapAttribute.

2. Используйте PR lifecycle

using UnityEngine;

public class RotatingObject : PRMonoBehaviour
{
    [SerializeField] private float speed = 90f;

    protected override void PRUpdate()
    {
        transform.Rotate(Vector3.up, speed * PRTime.Instance.GameDeltaTime);
    }
}

PRUpdate, PRLateUpdate и PRFixedUpdate не выполняются во время логической паузы. Для временных расчётов используйте PRTime, если система должна следовать правилам паузы фреймворка.

Физические callback'и объявлены в базовом классе, поэтому Unity вызывает их у любого наследника. Ненужные отключаются на уровне типа:

[DisableMethods("OnTriggerStay", "OnCollisionStay")]
public class Pickup : PRMonoBehaviour { }

Атрибут действует только на физические callback'и и OnPauseStateChanged, имена задаются строками, а у наследника список заменяется целиком, а не дополняется — подробности в PRMonoBehaviour. Там же чек-лист «метод не вызывается — что проверить».

3. Подпишитесь на глобальное событие

Событие описывается интерфейсом:

public interface ICoinsChanged : IGlobalSubscriber
{
    void OnCoinsChanged(int value);
}

Подписчик реализует интерфейс и регистрируется в EventBus:

public class CoinsView : MonoBehaviour, ICoinsChanged
{
    private void OnEnable() => EventBus.Subscribe(this);
    private void OnDisable() => EventBus.Unsubscribe(this);

    public void OnCoinsChanged(int value)
    {
        Debug.Log($"Coins: {value}");
    }
}

Вызов события:

EventBus.RaiseEvent<ICoinsChanged>(listener => listener.OnCoinsChanged(10));

Компоненты, наследуемые от PRMonoBehaviour, автоматически регистрируются в EventBus при стандартной инициализации и снимаются с регистрации при уничтожении.

4. Управляйте логической паузой

PRUnitySDK.PauseManager.SetLogicPaused(true, this);
PRUnitySDK.PauseManager.SetLogicPaused(false, this);

Состояние доступно через:

bool isPaused = PRUnitySDK.PauseManager.IsLogicPaused;

5. Запустите PR-корутину

var delay = new WaitGameSecondsCoroutine(
    callback: () => Debug.Log("Completed"),
    duration: 2f,
    instance: this);

delay.Execute();

Если MonoBehaviour не передан, корутина запускается на глобальном PRMonoBehaviourHost. Для повторного запуска одного объекта корутины используйте StopAndExecute(), а для остановки — Stop().

Инициализация

Основная последовательность запуска:

  1. Bootstrap.Awake() вызывает PRUnitySDK.InitializeSDK().
  2. Инициализируются правила, конвертеры, singleton-сервисы и фабрики.
  3. Через method hooks подключаются дополнительные модули.
  4. Инициализируются контейнеры менеджеров и окон.
  5. Устанавливается PRUnitySDK.IsInitialized.
  6. Публикуется ISDKEvents.OnInitialized() и устанавливается ReadySignal.

Повторный параллельный запуск блокируется флагом PRUnitySDK.IsStartInitialize. Готовность можно проверить через PRUnitySDK.IsInitialized или PRUnitySDK.ReadySignal.

Какой механизм расширения выбрать

Во фреймворке шесть способов вклиниться в чужое поведение. Они не взаимозаменяемы: каждый решает свою задачу, и выбор определяется одним вопросом — что вам нужно сделать с чужим кодом.

Нужно Механизм Где описан
Выполнить свой код в определённый момент MethodHookAttribute Attributes
Узнать, что что-то уже произошло EventBus EventBus
Изменить или запретить действие до того, как оно случилось HookSystem HookSystem
Согласовать решение, на которое влияют несколько компонентов FlagsSystem FlagsSystem
Собрать данные из всех модулей в один список InvokePartialAttribute Attributes
Подменить стандартную реализацию сервиса OverridePropertyAttribute Attributes
Выполнять работу по расписанию вне сцены BackgroundTask BackgroundTasks
Узнать об изменении значения, которое само о себе не сообщает WatcherTask<T> BackgroundTasks

Как отличить друг от друга

MethodHook — «выполни это на такой-то стадии». Инициализация модуля, регистрация фабрик, клонирование данных. Порядок задаётся числом, результат не собирается.

[MethodHook(MethodHookStage.SDK, order: 20)]
private static void InitializeInventory() { }

EventBus — «сообщи, что уже случилось». Отправитель не ждёт ответа и не знает подписчиков. Обновить UI, проиграть звук, записать метрику.

public class CoinsView : MonoBehaviour, IResourceValueChangedEvent { }

HookSystem — «дай вмешаться до того, как случится». В отличие от EventBus, обработчик получает изменяемый контекст: может уменьшить урон, заменить награду или отменить действие. Обработчики идут цепочкой в порядке Order.

public class DamageResistanceComponent : PRMonoBehaviour, IHookListener<DamageHookEvent> { }

FlagsSystem — «можно ли сейчас?». Когда на один вопрос («можно ли прыгать») влияют несколько независимых источников — оглушение, катсцена, туториал. Каждый добавляет своё влияние, система сводит их к ответу.

flagResolver.Add(PlayerFlags.CanJump, this, false);

InvokePartial — «соберите со всех». Каждый модуль возвращает свой кусок, вызывающий получает объединённый результат.

IEnumerable<Modifier> modifiers = this.CollectPartialResult<Modifier>(context);

OverrideProperty — «замени реализацию». Интеграция подставляет свой сервис вместо стандартного до применения fallback: серверное время платформы вместо локального, облачное хранилище вместо PlayerPrefs.

[OverrideProperty(typeof(IServerTime), order: -100)]
private static void UsePlatformServerTime() => ServerTime = new PlatformServerTime();

BackgroundTask — «делай это раз в N секунд». Работа, у которой нет владельца на сцене и которая должна пережить её смену: автосохранение, офлайн-доход, отложенная аналитика. Задача сама переживает паузу так, как ей нужно, и умеет пропускать запуск, пока условия не готовы.

[AutoBackgroundTask]
public class PlaytimeTrackerTask : BackgroundTask
{
    public override Enumeration Key => BackgroundTaskKeyEnumerationProvider.PlaytimeTracker;
    public override float RepeatSeconds => 60f;

    protected override void OnExecute() { }
}

WatcherTask<T> — «сообщи, когда значение изменится». Отличие от EventBus в том, что источник ничего не отправляет: смену суток, состояние сети или изменённый на сервере баланс можно узнать только опросом. Наблюдатель опрашивает по расписанию и поднимает событие только в момент изменения.

Частые ошибки выбора

  • EventBus там, где нужен HookSystem. Если обработчику надо повлиять на результат, событие не подойдёт: оно уведомляет уже после того, как решение принято.
  • HookSystem там, где хватило бы EventBus. Перехват дороже и делает поток неочевидным: обработчик может незаметно отменить действие.
  • Свой флаг вместо FlagsSystem. Булево поле «нельзя прыгать» на компоненте ломается, когда источников запрета становится двое: кто снял — тот и разрешил, хотя второй ещё против.
  • MethodHook для реакции на игровое событие. Хуки стадий вызываются там, где код явно их запускает; для игровых событий есть EventBus.
  • partial void как точка расширения. У partial-метода ровно одна реализация на класс: вторая часть, которой понадобится та же точка, получит ошибку компиляции CS0757. Если участников может быть несколько — это MethodHook или InvokePartial.
  • Своя корутина вместо BackgroundTask. Для периодической работы без владельца на сцене корутина требует объекта-хозяина, теряется при смене сцены и не даёт ни счётчиков, ни защиты от череды ошибок.
  • Опрос в Update вместо WatcherTask<T>. Проверять раз в кадр то, что меняется раз в минуту, — лишняя работа; наблюдатель делает это по расписанию и сообщает только об изменении.

Расширение без вклинивания

Отдельно стоят механизмы, которые не перехватывают чужое поведение, а дополняют данные. Им не нужен ни один из шести способов выше:

Что добавить Как
Поле в сохранение partial class ProjectData + [MethodHook(Cloning)]
Новое окно partial class MonoWindowKeyEnumerationProvider + partial class PRWindowsContainer
Фоновая задача наследник BackgroundTask + [AutoBackgroundTask] + ключ partial-частью BackgroundTaskKeyEnumerationProvider
Путь к ресурсам модуля partial class ResourcePaths
Новый ключ, тип, слой наследник EnumerationProviderBase или partial-часть существующего

Правило простое: общие файлы фреймворка править не нужно — почти всё расширяется partial-частью рядом с модулем.

Структура репозитория

PRUnitySDK/
├── Core/                       # Ядро, сервисы, базовые модели и инструменты
├── Modules/                    # Опциональные игровые модули
├── Examples/                   # Примеры использования
├── Resources/                  # Runtime-ресурсы фреймворка (данные игры — в PRUnityData)
├── Utils/                      # Дополнительные утилиты
└── YG2.Integration/            # Интеграция с YG2

Дополнительная документация

Ядро и точки расширения

  • SDK — facade, инициализация и service resolver
  • ResourcePaths — канонические пути к runtime-ресурсам и правила расширения
  • Окна Database и Settings — секции с описанием и сбросом, поиск, заполнение каталогов, валидация definitions и наборы состава базы для разных игр
  • Attributes — method hooks, переопределение сервисов и расширение Inspector
  • Actions — переиспользуемые действия с единым контрактом проверки и выполнения
  • Conditions — переиспользуемые правила «когда это разрешено»; рядом с действиями, но отдельно от них: действие совершает поступок, условие только спрашивает. Оттуда же берут подпись для игрока: чего не хватает, чтобы условие выполнилось
  • EventBus — типизированная шина уведомлений о произошедшем
  • HookSystem — перехват действий с возможностью изменить или запретить их
  • FlagsSystem — согласование независимых решений компонентов без прямых зависимостей

Жизненный цикл и время

  • PRMonoBehaviour — базовый компонент с lifecycle-хуками, учитывающими логическую паузу
  • Coroutines — обёртки над корутинами с запуском, перезапуском и остановкой
  • Yields — CustomYieldInstruction, останавливающие корутины на логической паузе
  • PauseSystem — раздельные причины паузы и мониторы аниматоров и физических тел
  • PRTime — источник времени: реальное, игровое и учёт паузы
  • PRTimeScale — слои скорости времени, модификаторы с владельцами и драйверы для анимации и физики
  • BackgroundTasks — фоновые задачи по расписанию и наблюдение за значениями

Менеджеры

  • Обзор менеджеров — доступ, жизненный цикл, порядок и расширение контейнера
  • GameManager — загрузка и сохранение ProjectData/GameSettings, autosave и готовность данных
  • ProjectPropertiesManager — свойства long, float, DateTime, string и bool
  • ResourceManager — игровые ресурсы, баланс, списание и события изменений
  • OpenedItemsManager — открытые предметы и количество в ProjectData
  • SelectedItemsManager — что из имеющегося надето у каждого локального игрока
  • ReservedItemsManager — предметы, которые выдаются не покупкой: награды, подарки, кейсы
  • PRManagerContainer — hook-порядок создания runtime-менеджеров
  • SoundManager — музыка, UI-звуки, позиционные эффекты и наборы AudioSet
  • CursorManager — запросы Show/Hide курсора с приоритетом последнего обращения

Модели, сервисы и утилиты

  • Entity — сущности игрового мира: идентификаторы, описание, реестр, время жизни и пул
  • EntityStats — характеристики сущности: базовые значения, персональные модификаторы и расчёт итоговых
  • Ввод — состояние ввода игрока или бота и его маршрутизация по InputGuid
  • Фабрики MonoBehaviour — обычные prefab, singleton-компоненты, MonoWindow и Notifier
  • Trackers — игроки, сущности, камеры и UI-реестры
  • MonoWindow — модальные runtime-окна, фабрики и параметры открытия
  • Reward — модели наград, экземплярный сервис выдачи и проектные обработчики
  • Wallet — баланс, начисление и списание валюты поверх ResourceManager
  • Enumeration — расширяемый строковый идентификатор вместо enum
  • Services — NameService и сервис имени текущего игрока
  • Локализация — переводы в базе, ассетах и на префабах, живой перевод чисел, сбор и обмен через CSV
  • Utils — мелкие помощники: логирование, ресурсы, работа с материалами
  • GameDataStorage — storage-контракты и универсальный ProjectDataMap
  • Состояние объекта сцены — SaveableObjectState: объект появляется таким, каким его оставили
  • Extensions — extension-методы общего назначения для Unity и типов SDK
  • Quality — качество предметов: уровни редкости и их отображение
  • Utils — вспомогательные классы: время, отложенные вызовы, имена
  • Proxies — переадресация Unity-callback'ов с дочерних объектов родительским компонентам
  • Property modifiers — динамические характеристики, персональные модификаторы и GameRules
  • GameRules — глобальные границы характеристик, применяемые последними

Модули и интеграции

  • Modules — опциональные игровые модули: StateManager
  • DamageSystem — создание, модификация и применение урона через хуки; правила урона игры (DamageRules) из настроек проекта, со сцены и из кода
  • GameSessions — сессия карты и раунды с режимами и правилами; одиночная игра и локальный мультиплеер
  • HitBox — связь физических коллайдеров с DamageSystem
  • DOTweenEffects — связь DOTween с логической паузой и PRTimeScale
  • YG2 Integration — облачные сохранения, реклама и платформенные возможности Яндекс Игр

Текущие ограничения

  • Фреймворк распространяется как Unity Assets, а не как UPM-пакет.
  • Автоматический installer пока не создаёт настройки, слои, теги и prefab'ы.
  • Bootstrap по умолчанию предполагает сцены с индексами 0 и 1.
  • Часть каталогов и API всё ещё находится в процессе переноса и рефакторинга.
  • Не все модули имеют отдельную документацию и тестовое покрытие.
  • Версии нет: понять, на какой сборке фреймворка сделана игра, пока негде.

Репозиторий

github.com/prethink/PRUnitySDK

About

WIP

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages