В этот раз расскажу о процессе создания основного игрового цикла. Напомню, что в качестве движка выбран Godot, а программировать я буду на C#.
Итак, в силу того что проект микроскопический, никаких чистых архитектур и красивого кода я не планирую. В этот раз обойдемся одной стейт-машиной для игрового цикла и несколькими синглтон-контроллерами. Из основных контроллеров - контроллер уровня, контроллер мобов, контроллер игрока, контроллер снарядов.
Основной игровой цикл в данном случае будет выглядеть так:
В старой версии игры нет меню и паузы, ограничимся точкой входа сразу в комнату. Для первоначальной настройки игры потребуется стартовый стейт, который перетечет в первую комнату без мобов. Первая комната должна показать игроку инструкцию с управлением и предоставить ему ходы в следующие комнаты. Перемещение между комнатами будет вызывать создание нового экземпляра Room стейта, а при его создании будет определяться сколько и каких мобов будет добавлено на карту.
Карта в новой версии будет извлечена в виде оверлея поверх игровой комнаты, блокируя ввод но не ставя игру на паузу. В итоге, игрок сможет открывать карту только когда на уровне ему никто не мешает. Однако, чтобы не создавать новый экземпляр комнаты по выходу из карты, стейт карты будет принимать в себя параметром стейт комнаты, на которую нужно переключиться.
Окей. Достаточно теории, переходим к практике. Создаем стейт машину и основу для контроллеров. На объект игрока добавляем его контроллер и кидаем на сцену. Заставляем контроллер уровня грузить самый первый уровень как основной, он у нас будет вступительным. В контроллер снарядов добавляем собственно ссылки на два типа снарядов - игрока и врагов, и даем возможность игроку спавнить их по нажатию на стрелки. В итоге получаем готовый функционал движения между уровнями.
Да, кстати. Игра начинается с основной сцены "game", структура сцены на данный момент такая:
В корне лежит 2D-нода для мира и обычная нода для game_manager, который несет в себе скрипт менеджера и стейт машины игры. Level, mobs, projectiles, player - объекты с соответствующими контроллерами.
Далее на сцену кидаем точки выхода дверей, рисуем заставку с управлением для первой комнаты и блокируем управление в первой комнате до вывода заставки. Далее, при прикосновении к триггеру двери, делаем вызовы нового стейта комнаты с загрузкой случайной комнаты из списка. Перемещаем игрока к противоположной двери, следуя за направлением его движения.
На заметку: о событиях, триггеры сообщают в условную шину событий. Но в этот раз, свою собственную шину я не стал делать, а воспользовался системой ввода Godot, создав собственный экземпляр события триггера и отправляя его в систему ввода. В итоге мои события от триггеров приходят во все _Input в игре. В чуть большей игре это не стоит делать, так как переизбыток событий может забить канал ввода. Лучшем решением будет создание собственной шины событий с разными темами/каналами/подписками.
В следующий раз расскажу о работе над врагами и над боссом.