Друзья, это начало нового цикла статей про создание игры жанра dungeon crawler с использованием фреймворка LeoECS Lite, и его задача – помочь вам быстро разобраться, как на практике применить LeoECS Lite для разработки игр на Unity и решить некоторые виды проблем. LeoECS Lite - новая, более легковесная версия фреймворка LeoECS, о котором отдельно написаны туториалы. Пусть слово "Lite" не вводит вас в заблуждение – она подчеркивает именно легковесность фреймворка, а не простоту использования. Хотя сам по себе он простой, он может быть непривычным для тех, кто привык к API классической версии. Чтобы добавить/удалить компонент у сущности, вам необходимо сначала получить доступ к пулу компонентов. Dependency Injection через рефлекшн убран из ядра, поэтому вы не можете сразу пользоваться ссылками на мир, фильтры или новые пулы в экземплярах систем - нужно или запрашивать их в рантайме, или кешировать заранее. Сущности теперь в чистом виде представляют собой обычные int'ы. Чтобы сохранить их где-то и иметь возможность проверить, не уничтожена ли энтити, нужно паковать их через мир. Про остальные изменения можно прочитать в README репозитория. С другой стороны, легковесность фреймворка заключается в том, что простым стало именно ядро. Оно стало модульным - большая часть фишек переехала в расширения, которые можно подключать опционально. С их помощью можно сделать приятный API, напоминающий классику, и вот какие мы будем использовать при создании игры: ecslite-di, ecslite-extended-systems, ecslite-unityeditor, ecslite-unity-ugui. Итак, давайте перейдем к практике и начнем разработку с создания старптап-класса. Для тех, кто знаком с LeoECS, изменений здесь не так уж и много. Мы будем использовать простой MonoBehaviour экземпляр класса SceneData, в котором будут храниться данные, связанные со сценой, а также класс Configuration, который является экземпляром Scriptable Object'а. Первым делом нам нужна возможность создать карту клеток. Мы можем создать простой MonoBehaviour класс, который будет добавлен к префабу клетки. Благодаря нему мы сможем располагать клетки в сцене с определенным интервалом. При этом они будут магнититься к нужным местам, вычисляя правильные координаты. Как видите, мы также сделали удобное отображение клеток в окне редактора с Gizmos. Нам нужно будет проинициализировать карту в самом начале, создав сущности для каждой клетки, на основе данных с уровня. То есть, левел дизайнер делает карту, размещает клетки, а мы в начале игры создаем сущности клеток и добавляем к ним компоненты. Давайте вернемся к нашему классу SceneData и немного поменяем его: Благодаря этому контекстному меню мы сможем заранее найти и сохранить все клетки в массив. Конечно, можно делать это и на старте игры, но зачем его замедлять, если можно потратить пару минут времени на кеширование и ускорение старта? Для того, чтобы хранить сетку, лучше создать отдельный сервис. Также создадим структуру Int2 для хранения двух целых чисел. Она будет более простая, чем штатный Vector2Int. Можно, конечно, хранить все данные в компоненте на какой-то сущности, но удобнее будет создать сервис с API для добавления и получения клеток. Теперь давайте создадим инит-систему, которая будет заполнять данные сервиса. Отлично, мы соорудили сервис карты и собрали в нем данные о клетках на сцене. Теперь давайте создадим отдельный сервис TimeService, - некую абстракцию от времени Unity - данные которого будем заполнять в начале каждого кадра. Не забудьте обновить стартап, добавив туда создание экземпляров сервисов и новых систем: Теперь мы можем заняться спауном игрока. Как вы заметили, к сущности игрока добавлены компоненты Unit и ControlledByPlayer. Почему именно так? Дело в том, что в нашей игре и игрок, и монстры будут двигаться по одинаковым правилам. Логика (код) перемещения по клеткам будет одинаковый для всех юнитов. Единственное, что будет отличаться - источник команд. Юнит игрока будет принимать команды от пользовательского ввода, враги - от ИИ. И даже если вы в силу неопытности этого не заметили бы и написали отдельно код и для игрока, и для врагов, повторяющиеся строки натолкнут вас на мысль о том, что имеет смысл вынести их в отдельный блок кода. И с ECS это все будет проще и быстрее отрефакторить, так как каждая сущность - набор компонентов. Давайте сделаем аж три способа управления персонажем: через клавиатуру, через кнопки и через мышь. Но все по порядку. Направления вперед-назад будут использоваться для движения, влево-вправо для поворотов. Начнем с простого. Управление через клавиатуру. Создадим отдельную систему для этого. Чтобы сохранить строки и иметь возможность быстро менять их везде, создадим статический класс Idents. Теперь займемся настройкой UI. Создадим 4 кнопки и повесим на них нужные компоненты для обработки событий UI. На корневой объект UI нужно будет добавить компонент EcsUguiEmitter. Давайте проинициализируем его в коде. В EcsLite рекомендуется использовать отдельный мир для короткоживущих энтити-ивентов, так как каждый мир имеет размер maxEntitiesCount * poolsCount. То есть, если у вас в мире 100 тысяч сущностей для юнитов, и вы вдруг создаете одну сущность-ивент с компонентом "Click", то для этого компонента будет создан пул с огромным размером, что в конечном итоге приведет к нерациональному распределению памяти. Добавим название мира для событий и названия кнопок в класс Idents. Теперь создадим систему, которая будет ловить события с кнопок. Осталось лишь создать систему свайпов. Создадим отдельный полноэкранный невидимый виджет для этого. Не забудьте, что нужно поместить его под кнопки, иначе клики в них не пойдут. И новую систему: Теперь создадим системы для движения и поворотов. Компонент Animating - это маркер, говорящий о том, что юнит сейчас занят, и никто не может принимать команды. Теперь система твининга между клетками: Теперь система для начала поворотов: И теперь система поворотов: И не забудьте добавить все системы в стартап. Отлично, теперь наш герой движется и поворачивается! В следующей части мы разберем более сложные механики и перейдем к разработке поведения врагов.  Туториал подготовлен в соавторстве с Владимиром Роттердамским   Ссылка на репозиторий с проектом