Skip to content

RimuruDev/CuteDI-Sandbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AbyssMoth_CuteDI_Logo

CuteDI

Π›Ρ‘Π³ΠΊΠΈΠΉ DI-Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ для Unity. Основа β€” ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ:

  • Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΎΠ² ΠΏΠΎ Ρ‚ΠΈΠΏΡƒ, инстансу ΠΈ Ρ„Π°Π±Ρ€ΠΈΠΊΠ΅,
  • Ρ‚Π΅Π³ΠΎΠ² для ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ,
  • ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΠΉ Ρ‡Π΅Ρ€Π΅Π· конструктор (Π² Ρ‚ΠΎΠΌ числС с Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Π°ΠΌΠΈ [InjectionConstructor] ΠΈ [Tag]),
  • ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ΠΈ сцСны,
  • Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΎΠ² для MonoBehaviour (созданиС GO, ΠΏΡ€Π΅Ρ„Π°Π±Ρ‹, Π·Π°Ρ…Π²Π°Ρ‚ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… инстансов).

Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ сдСлан ΠΏΡ€Π΅ΠΆΠ΄Π΅ всСго для Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½Π΅Π³ΠΎ использования Π² AbyssMoth Studios. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π½Π° свой страх ΠΈ риск; API ΠΌΠΎΠΆΠ΅Ρ‚ ΡΠ²ΠΎΠ»ΡŽΡ†ΠΈΠΎΠ½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ.


Установка

  1. Π˜ΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠΉΡ‚Π΅ ΠΏΠ°ΠΊΠ΅Ρ‚. Папка ΠΏΠ»Π°Π³ΠΈΠ½Π° окаТСтся здСсь: Assets/Plugins/RimuruDev/CuteDI

  2. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€: Assets/Plugins/RimuruDev/CuteDI/Example β€” Ρ‚Π°ΠΌ Π³ΠΎΡ‚ΠΎΠ²Ρ‹Π΅ сцСны Boot, MainMenu, Gameplay, Other ΠΈ дСмонстрация всСх Π²ΠΈΠ΄ΠΎΠ² Π±ΠΈΠ½Π΄ΠΈΠ½Π³Π°.


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

1) ΠŸΡ€ΠΎΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€

ΠŸΡ€ΠΎΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Π²Ρ‹ создаётС сами Π² Ρ‚ΠΎΡ‡ΠΊΠ΅ Π²Ρ…ΠΎΠ΄Π° (ΠΊΠ°ΠΊ Π² ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅):

using UnityEngine;

namespace AbyssMoth.CuteDI.Example
{
    public sealed class Bootstrapper : MonoBehaviour
    {
        private IProjectContext projectContext;
        private IGameNavigation navigation;

        private void Awake()
        {
            projectContext = new ProjectContext();
            projectContext.Register();
            projectContext.Resolve();

            navigation = projectContext.Container.Resolve<IGameNavigation>();

            DontDestroyOnLoad(gameObject);
        }

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.Alpha1)) navigation.GoToMainMenu();
            if (Input.GetKeyDown(KeyCode.Alpha2)) navigation.GoToGameplay();
            if (Input.GetKeyDown(KeyCode.Alpha3)) navigation.GoToOther();
        }

        private void OnDestroy() => projectContext?.Release();
    }
}

ProjectContext Π²Π½ΡƒΡ‚Ρ€ΠΈ создаёт DIContainer, Π΄Π΅Π»Π°Π΅Ρ‚ Π±Π°Π·ΠΎΠ²Ρ‹Π΅ рСгистрации (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ICoroutineRunner, IGameNavigation), ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Π²ΠΎ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ Ρ‡Π΅Ρ€Π΅Π· DiProvider.SetProject(...). Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ Π΄Π°Ρ‘Ρ‚ ΠΏΠΎΠ»Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ Π½Π°Π΄ Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ находится Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°.


2) Π‘Ρ†Π΅Π½ΠΎΠ²ΠΎΠΉ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€

На ΠΊΠ°ΠΆΠ΄ΠΎΠΉ сцСнС ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅ SceneContext (Π΅ΡΡ‚ΡŒ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ SceneContextBootstrap, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ контСкста). Π’ SceneContext ΠΌΠΎΠΆΠ½ΠΎ Π½Π°Π·Π½Π°Ρ‡ΠΈΡ‚ΡŒ ScriptableObject-инсталлСры β€” ΠΎΠ½ΠΈ Π²Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‚ΡΡ Π² Awake ΠΈ Π½Π°ΠΏΠΎΠ»Π½ΡΡŽΡ‚ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ сцСны.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ простого инсталлСра мСню:

using UnityEngine;

namespace AbyssMoth.CuteDI.Example
{
    [CreateAssetMenu(fileName = "MainMenuInstaller", menuName = "DI/Scene Installers/MainMenu")]
    public sealed class MainMenuInstaller : SceneInstallerSO
    {
        [SerializeField] private GameObject menuRootPrefab;

        public override void Compose(in DIContainer scene, in DIContainer project)
        {
            scene.RegisterInstance(this).AsSingle().NonLazy();
            scene.RegisterType<IMenuService, MenuService>().AsSingle().NonLazy();
            scene.Register(c => new MenuViewModel(c.Resolve<IMenuService>())).AsSingle().NonLazy();

            if (menuRootPrefab)
                scene.BindPrefab<IMenuRoot, MenuRoot>(menuRootPrefab, isUI: true);
        }
    }
}

Π’ΠΈΠ΄Ρ‹ Π±ΠΈΠ½Π΄ΠΈΠ½Π³Π°

1) По Ρ‚ΠΈΠΏΡƒ (рСкомСндуСтся)

Π’Π΄ΠΎΡ…Π½ΠΎΠ²Π»Π΅Π½ΠΎ Reflex. Π”Π΅Π»Π°Π΅Ρ‚ ΠΊΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ‚ΠΎΡ€Π½ΡƒΡŽ ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΡŽ автоматичСски.

scene.RegisterType<IEnemySpawner, EnemySpawner>().AsSingle().NonLazy();
scene.RegisterType(typeof(IAnalytics), typeof(DummyAnalytics)).AsSingle().NonLazy();

2) По инстансу

scene.RegisterInstance(this).AsSingle().NonLazy();
scene.RegisterInstance(configSo).AsSingle().NonLazy();

3) По Ρ„Π°Π±Ρ€ΠΈΠΊΠ΅

scene.Register(c => new GameplayController(
        c.Resolve<IEnemySpawner>(),
        project.Resolve<IGameNavigation>()))
     .AsSingle()
     .NonLazy();

4) Π’Π΅Π³ΠΈ ΠΈ нСсколько Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ

ΠšΠ»ΡŽΡ‡ Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π΅ β€” (tag, serviceType). Для ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Ρ‚Π΅Π³ΠΈ:

scene.RegisterType<IClock, UtcClock>("utc").AsSingle().NonLazy();
scene.RegisterType<IClock, GameClock>("game").AsSingle().NonLazy();

Π˜Π½ΡŠΠ΅ΠΊΡ†ΠΈΡ Π½ΡƒΠΆΠ½ΠΎΠΉ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ‡Π΅Ρ€Π΅Π· [Tag]:

public sealed class AttrConsumer
{
    public IStorage File { get; }
    public IClock Clock { get; }

    [InjectionConstructor]
    public AttrConsumer([Tag("file")] IStorage file, [Tag("utc")] IClock clock)
    {
        File = file;
        Clock = clock;
    }
}

Π‘Π±ΠΎΡ€ всСх Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ интСрфСйса:

scene.RegisterType<IProcessor, ProcA>("proc_a").AsSingle();
scene.RegisterType<IProcessor, ProcB>("proc_b").AsSingle();

scene.Register(c => new ProcessorAggregator(c.ResolveAll<IProcessor>().ToArray()))
     .AsSingle()
     .NonLazy();

5) Π—Π°ΠΌΠ΅Π½Π° (replace)

МоТно Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ привязку (ΡƒΠ΄ΠΎΠ±Π½ΠΎ для ΠΌΠΎΠΊΠΎΠ² Π² тСстовой сцСнС):

scene.RegisterType<IReplaceSample, ReplaceA>().AsSingle();
scene.Replace<IReplaceSample>(c => new ReplaceB()).AsSingle().NonLazy();

6) MonoBehaviour-Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΈ

Π£Ρ‚ΠΈΠ»ΠΈΡ‚Ρ‹ DiUnity β€” для сцСплСния ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° ΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²:

// Новый пустой GO с ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠΌ
scene.BindNewGo<IFoo, FooMono>("FooGo", tag: "foo_go");

// Π˜Π½ΡΡ‚Π°Π½Ρ ΠΈΠ· ΠΏΡ€Π΅Ρ„Π°Π±Π° (UI ΠΈΠ»ΠΈ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉ)
scene.BindPrefab<IFoo, FooMono>(widgetPrefab, parent, isUI: true, tag: "foo_widget");

// Волько ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ (Self) Π±Π΅Π· интСрфСйса:
scene.BindPrefabSelf<HUD>(hudPrefab, parent);

Π’Π°ΠΆΠ½ΠΎ: Ссли Π±ΠΈΠ½Π΄ΠΈΡ‚Π΅ нСсколько экзСмпляров ΠΎΠ΄Π½ΠΎΠ³ΠΎ MonoBehaviour, Π΄Π°Π²Π°ΠΉΡ‚Π΅ Ρ€Π°Π·Π½Ρ‹Π΅ Ρ‚Π΅Π³ΠΈ, ΠΈΠ½Π°Ρ‡Π΅ Π±ΡƒΠ΄Π΅Ρ‚ коллизия ΠΊΠ»ΡŽΡ‡Π΅ΠΉ.


Π˜Π½ΡŠΠ΅ΠΊΡ†ΠΈΠΈ (конструкторы)

  • Если Ρƒ класса ΠΎΠ΄ΠΈΠ½ конструктор β€” ΠΎΠ½ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ.
  • Если конструкторов нСсколько β€” ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠΎΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ Π½ΡƒΠΆΠ½Ρ‹ΠΉ [InjectionConstructor].
  • Для Π²Ρ‹Π±ΠΎΡ€Π° ΠΏΠΎ Ρ‚Π΅Π³Ρƒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ [Tag("...")] Ρƒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°.

Π Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ зависимостСй

var nav = project.Resolve<IGameNavigation>();
var ok  = scene.TryResolve<IFoo>(out var foo, tag: "foo_widget");
var has = scene.HasRegistration<IEnemySpawner>();
var all = scene.ResolveAll<IProcessor>(); // Π°Π³Ρ€Π΅Π³ΠΈΡ€ΡƒΠ΅Ρ‚ Ρ‡Π΅Ρ€Π΅Π· рСгистр/родитСля

DiProvider: Тизнь ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ²

  • DiProvider.Project β€” Ρ…Ρ€Π°Π½ΠΈΡ‚ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ (Π²Ρ‹ создаётС Π΅Π³ΠΎ сами Π² Bootstrapper / ProjectContext).

  • ΠŸΡ€ΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ΅ сцСны SceneContext Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ DiProvider.SceneContextBuilder(parent, sceneName, containerName) β€” создаётся сцСновой ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€, ΠΊΡΡˆΠΈΡ€ΡƒΠ΅Ρ‚ΡΡ ΠΏΠΎ handle сцСны ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»Π΅ΠΌ Project.

  • На SceneManager.sceneUnloaded ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ сцСны Dispose()-ится ΠΈ снимаСтся с кэша.

  • ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ²:

    • Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠΉ сцСны: DiProvider.GetCurrentSceneContainer()
    • ΠΏΠΎ ссылкС Π½Π° сцСну: scene.GetSceneContainer()
    • ΠΏΠΎ ΠΈΠΌΠ΅Π½ΠΈ: DiProvider.GetSceneContainerBySceneName("Gameplay")

МоТно ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ сцСновой ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ (Ссли строитС своё ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ): Π²Ρ‹Π·ΠΎΠ²ΠΈΡ‚Π΅ DiProvider.SceneContextBuilder(projectContainer, sceneName) Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚, ΠΊΠΎΠ³Π΄Π° сцСна ΡƒΠΆΠ΅ Π²Π°Π»ΠΈΠ΄Π½Π° (ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ послС LoadSceneAsync ΠΈ ΠΏΠ΅Ρ€Π΅Π΄ ΠΏΠ΅Ρ€Π²Ρ‹ΠΌ ΠΊΠ°Π΄Ρ€ΠΎΠΌ).


ΠžΡ‚Π»Π°Π΄ΠΊΠ°

  • Π˜Π½ΡΠΏΠ΅ΠΊΡ‚ΠΎΡ€ SceneContext Π² Play Mode ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ рССстр Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΎΠ² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° сцСны.
  • Π˜Π½ΡΠΏΠ΅ΠΊΡ‚ΠΎΡ€ инсталлСров (SceneInstallerSO) ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ прСдпросмотр Ρ‡Π΅Ρ€Π΅Π· PreviewHints().
  • Окно Tools β†’ CuteDI β†’ DI Debugger β€” быстрый просмотр содСрТимого ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ² (ΠΏΡ€ΠΎΠ΅ΠΊΡ‚/сцСна/Prefab Stage), Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ ΠΏΠΎ Ρ‚ΠΈΠΏΡƒ/Ρ‚Π΅Π³Ρƒ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Β«Π²ΠΈΡ‚Ρ€ΠΈΠ½Ρ‹Β» всСх Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΎΠ²

Π’ сцСнС Other Π»Π΅ΠΆΠΈΡ‚ AllBindingsInstaller β€” дСмонстрация:

  • RegisterType / RegisterInstance / Register(factory)
  • Ρ‚Π΅Π³ΠΈ ΠΈ [Tag]
  • ResolveAll<T>()
  • Replace<T>()
  • BindNewGo / BindPrefab / BindPrefabSelf

Плюс Π² ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ SceneHintUI (большиС подсказки Π½Π° экранС) с клавишами:

  • [1] β†’ MainMenu
  • [2] β†’ Gameplay
  • [3] β†’ Other

ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΡ ΠΈ Π·Π°ΠΌΠ΅Ρ‚ΠΊΠΈ

  • ΠšΠ»ΡŽΡ‡ рСгистрации β€” (tag, serviceType). Для Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ ΠΎΠ΄Π½ΠΎΠ³ΠΎ интСрфСйса ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Ρ‚Π΅Π³ΠΈ.
  • BindOnGo Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ сцСновый экзСмпляр. Для ассСтов ΠΈ ΠΏΡ€Π΅Ρ„Π°Π±ΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ BindPrefab*.
  • ΠšΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ сцСны удаляСтся ΠΏΡ€ΠΈ Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΠ΅ сцСны (Dispose()); ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ ΠΆΠΈΠ²Ρ‘Ρ‚ Π΄ΠΎ ΠΊΠΎΠ½Ρ†Π° прилоТСния.
  • Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ Π½Π΅ потокобСзопасСн; ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π² основном ΠΏΠΎΡ‚ΠΎΠΊΠ΅ Unity.

ЛицСнзия

MIT. ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΡΡ‚ΠΈ β€” Π² LICENSE.