Пишем игровую логику на C#. Часть 1/2
Всем привет. В связи с выходом моей игры SpaceLab на GreenLight я решил начать серию статей о разработке игры на C#/Unity. Она будет основываться на реальном опыте её разработки и немного отличаться от стандартных гайдов для новичков:
Во-первых, я не буду повторять документацию иными словами. Во-вторых, необходимо знание программирования, чтобы понять о чем я пишу.
К сожалению, эта статья не сможет вам помочь, если вы хотите создать свою казуальную игру используя лишь мышь.
Зато я шаг за шагом расскажу о создании движка, на котором будет работать игровая логика нашей экономической стратегии.
Для тех, кто любит спойлеры или просто хочет почитать код — в конце есть ссылка не репозиторий, где каждый пункт добавлен отдельным коммитом.
Кого заинтересовало узнать, что за игра — внизу есть видео и ссылка на бесплатное скачивание. Сразу предупрежу — у меня нету цели идеально применить огромное количество паттернов или описать подход к методологии TTD. В статье я стараюсь писать читабельный, поддерживаемый и безбажный код, как он писался бы в жизни. Возможно, людям имеющим огромный скилл в C# и написании игр данная статья покажется очевидной. Тем не менее, вопрос о том, как писать гейм-логику я слышал довольно часто и эта статья прекрасно подойдет и тем, кому интересно написание сервера и тем, кому интересно написание клиента на Unity.
Краткое описание GD, которого мы хотим достичь1. Игрок управляет кораблем. В корабле можно выстраивать комнаты, в комнатах можно добавлять в слоты модули.
2. Для постройки чего-либо необходимо потратить ресурсы и подождать время.
Через полгода разработки результат должен выглядеть как-то так)
План работы1. Настраиваем проекты 2. Создаем ядро — базовые сооружения 3. Добавляем и тестируем первые команды — построить строение и модуль 4. Выносим настройки строений и модулей в отдельный файл 5. Добавляем течение времени 6. Добавляем Constructible, строения теперь строятся некоторое время 7. Добавляем ресурсы, для постройки необходимы ресурсы 8. Добавляем цикл производства — модуль потребляет и выдает ресурсы
Статья получилась очень объемной, потому пришлось разделить ее на две части. В данной части мы сделаем первые пять пунктов, а во второй части закончим
1. Настраиваем проекты
На первых порах Unity Editor нам не понадобится — мы пишем ГеймЛогику. Открываем VS и создаем два проекта: GаmeLogic и LogicTests (Unit Tests Project). В первом мы будем писать собственно логику игры на чистом C# не используя namespace Unity, второй будет тестить нашу логику встроенной тест-тулзой. Добавим в GameLogic первый класс Core и напишем первый тест, чтобы проверить нашу связку:
2. Создаем ядро — базовые сооружения
Что ж, это указывает, что настроили мы корректно и можно переходить к программированию логики.
Итак, разберемся с нашим гейм-дизайном. У нас есть корабль (Ship), в нем комнаты (Room), в каждую комнату может быть построено строение (Building), а в каждом строении могут быть модули (Module). Конечно, Room и Building можно было бы объединить в одну сущность, но далее такое разделение нам только поможет.
Для всех этих сооружений я создам отдельный namespace Architecture и базовые классы. А так же enum для индексов комнат. Многие вещи, которые мы сейчас делаем — временные и необходимы, чтобы запустить первый тест гейм-логики.
3. Добавляем и тестируем первые команды — построить строение и модуль
Теперь мы сможем написать первую «фичу» — постройка строения и постройка модуля в нем. Все подобные действия я буду описывать отдельным классом, который будет наследоваться от класса Command:
И хотя сейчас даже такая маленькая структура излишня — чуть позже благодаря ей мы прикрутим необходимые нам события. А существование каждого атомарного действия в отдельной команде позволит нам их комбинировать. Напишем наши первые два действия:
Пришло время посмотреть, работает ли наш движок. В тестах создаем ядро, пробуем построить комнату, а в нее пытаемся построить модуль. Кроме этого стоит добавить проверку, что нельзя построить то, чего гейм-логика не должна позволять строить:
4. Выносим настройки строений и модулей в отдельный файл
К счастью, наши тесты прекрасно проходятся. Теперь нам необходима возможность линейно расширять количество строений и модулей — для этого необходимо сделать следующее:
- Создать конфигурацию для строений и модулей — " class BuildingConfig " и " class ModuleConfig ", именно они будут хранить все настройки наших сооружений.
- Building и Module при создании должны принимать соответствующие настройки
- Сделать фабрику для создания модулей и строений
- Добавить настройки для нескольких строений и модулей
- Адаптировать существующий код под новые входные данные
Как можно понять, теперь наш код нерабочий. Для того, чтобы не таскать каждый раз с собой конфиги создадим фабрику, которая будет выпускать наши сооружения зная только их тип. Я знаю, что название пока слишком общее, но мы всегда с легкостью можем его переименовать благодаря IDE, так же, как и разделить на две фабрики:
И хотя сейчас код корректен — при запуске наших тестов мы словим "Not implemented yet" . Для этого вернемся к нашей фабрике и реализуем несколько строений и модулей.
Я сразу добавил несколько строений и модулей, чтобы можно было покрыть тестами. И сразу скажу — да, хранить все эти настройки в фабрике нету никакого смысла. Они будут лежать отдельно в JSON файлах, по одному на структуру, парсится и передаваться в фабрику. К счастью, у нас движок даже не заметит этого изменения. Ну а пока нам не так критично вынести их в ЖСОНы, как запустить тесты и проверить все ли корректно работает. К счастью, да. Заодно допишем тесты, что нельзя построить модуль не в той комнате, например, Furnace в PowerPlant.
Увы, как вы можете догадаться, никто логику проверки не писал. Добавим условие валидации в команду постройки модуля и после этого успешно пройдем тест:
Что ж, теперь все корректно. Заодно добавим тесты на корректную работу лимитов и пойдем дальше.
5. Добавляем течение времени
Компьютеры дискретны. И все игры дискретны. Если говорить просто, то представим, что все игры — пошаговые. У большинства игр шаги пропускаются автоматически и 60 раз в секунду. Такие игры называются риалтайм. Я понимаю, что это очень грубо, но для реализации гейм-логики довольно удобно представлять, что ваша игра — пошаговая и мыслить такими категориями. А потом уже на клиенте можно запустить tween между двумя состояниями и юзеру будет красиво и игра будет работать быстро. Для начала введем понятие хода:
А также введем команду, которая позволяет переключать хода. Я сразу добавил команду, которая позволяет переключить несколько ходов — будет довольно удобно во время тестирования. В тестах одним выстрелом покроем сразу двух зайцев.
Забегая далеко вперед напишу, как сделать переключалку скоростей в игру, которая позволит нам запускаться с разной скоростью:
Теперь в Unity достаточно будет подвесится на любой Update и передавать дельта время в наш TimeWarp:
Продолжение следует.
В следующей статье мы закончим создание работоспособной основы для нашего движка, реализовав следующие пункты:
6. Добавляем Constructible, строения теперь строятся некоторое время 7. Добавляем ресурсы, для постройки необходимы ресурсы 8. Добавляем цикл производства — модуль потребляет и выдает ресурсы
Для тех, кто просто любит код — есть отдельный репозиторий на ГитХаб
Кроме этого, если вас интересуют вопросы по разработке SpaceLab — задавайте, отвечу на них в комментариях или в отдельной статье
Скачать для Windows, Linux, Mac бесплатно и без СМС можно со страницы SpaceLab на GreenLight