"Из Грязи в Князи". Тестирование и разрыв связей в юните через ID контейнеры

     В данной статье описан путь создания Unity приложение с возможностью безболезненного расширения. Не все описанные методы стоит применять во время участия в "GameJam" в частности внедрение ID контейнеров, не всегда рационально занятие, поскольку требует дополнительных абстракций, на продувание которых в рамках геймджема может не оказаться времени.

    Приведенный ниже код будет с каждой итерацией усложняться, таким образом показывая рост квалификации 

Начало:

    Описание "Игры":   ГГ это куб которого преследует шары.

Первым делом создадим скрипт (рис 1.) для нашего героя, в котором будут считываться нажатые клавиши.

                                                                Рис.1 Hero.cs
 Далее создадим скрипт (рис 2.) для нашего шара который будет догонять игрока. Игрок передается как объект, с целью получения текших координат.  


                                                                    Рис.2 Bot.cs

    На рисунке 3 представлена демонстрация работы двух скриптов.


                                                  Рис.3 Демонстрация работы двух скриптов.

    Далее увеличим количество NPC для последующих демонстраций Рис. 4. без использования фабрик или DI 


                                                     Рис.4 Увеличиваем количество NPC.

 Расширение:

    После добавления такого большого количества NPC стало понятно, что силы не равны, поэтому был создан второй герой который умел "отгонять" NPC на позицию (0,0,0). С реализацией можно ознакомиться  на рисунке 5. Теперь у героя появился метод GetPosition, благодаря которому он может  не только сообщать верные координаты, но также и ошибочные.


                                                                Рис.5 HeroAssasin.cs
    Проблема в замене нового персонажа в том что теперь нам необходимо менять логику работы бота Рис. 6, поскольку она была сильно связана с реализацией. 
 Рис.6 new Bot.cs

    Замете что мы передаем в класс HerroAssasin, а не объект. При этом в в инспекторе мы передаем объект из которого достается автоматически ссылка на скрипт.  (Рис. 7) с демонстрацией можно ознакомиться на рисунке 8.
    
Рис. 7. Работа в инспекторе.

Рис. 8. демонстрация реализации HeroAssasin.

 Расширение 2:

    Долго думая мы поняли что хотели бы иметь возможность выбора за какого персонажа играть, а быть может мы хотим добавить еще одного, а итоговый скрипт как был завязан на реализации так и остается. 
    Пока наши герои структурно  схожи мы можем наследовать их от базового с виртуальными методами как показано на рисунке 9. Код стал уже боле отвязанным от реализаций, но не до конца. 


Рисунок 9.

    Каждый раз наследоваться от общего класса не удобно, поскольку в гигантском ворохе наследования очень легко запутаться. Именно в момент когда наследование становиться чуть сложнее чем в примере выше стоит перейти на интерфейсы. К сожалению в юните вы не можете передать через инспектор что либо в интерфейс. И именно в этот момент на помощь приходит Extenject. https://github.com/modesttree/Zenject/releases/tag/9.2.0 

  

Extenject 

Extenject реализует инверсию управления.

Следующая диаграмма показывает, как обычно работают сильно связанные компоненты:

ClassA напрямую зависит от ServiceA/ServiceB. Это обременяет независимое тестирование ClassA необходимостью заботиться о деталях реализации этих двух служб.

Внедрение зависимостей (DI — Dependency Injection) — это подход к реализации инверсии управления. На следующем рисунке показан предыдущий пример с использованием внедрения зависимостей:

Внедрение зависимостей используется как строитель (Builder) для генерации нашего ClassA, проверки необходимых зависимостей и их автоматического предоставления. ClassA не заботит то, какой конкретно класс, реализующий требуемый интерфейс, используется, пока таковой имеется в наличии.

 Тестирование в unity 

В юните есть хорошее встроенное тестирование. Ниже на картинках показано как его настроить.