Все мои соцсети здесь: taplink.cc/catrepdev
К третьему этапу у меня уже была рабочая база: движение, враги, атака, здоровье, волны. То есть игра уже перестала быть просто технодемкой. Но я очень быстро понял одну вещь - просто бегать по арене и отбиваться от волн недостаточно. Да, это уже похоже на игру, но это еще не тот рогалик, который хочется запускать снова и снова. Для этого нужна прогрессия, выбор, новые ситуации и ощущение, что каждый забег развивается по-своему. Именно этим я и занялся дальше.
Первым большим шагом стали апгрейды между волнами. И вот это был реально важный момент. До этого каждая волна была просто следующим боем. А когда между ними появляется выбор улучшения, бой начинает работать уже не сам по себе, а как часть более большого цикла. Ты не просто выжил - ты получил шанс стать сильнее и изменить стиль следующего боя.
Для этого у меня появились PlayerStats, обновлённые скрипты здоровья и интерфейса, а также UpgradePanel и UpgradeUI. С точки зрения программирования это очень полезный шаг, потому что я начал работать уже не только с объектами на сцене, но и с системой выбора. То есть игра должна была не просто показать панель, а приостановить процесс, дать игроку варианты, дождаться решения, применить эффект и вернуть бой обратно. Это уже более сложная логика, чем просто "если нажал кнопку - ударил".
Здесь особенно важной стала работа с UI. Теперь интерфейс был нужен не только для отображения здоровья, но и для взаимодействия с игроком. Панель улучшений фактически стала отдельным игровым экраном внутри основного процесса. И это очень показательный момент: чем дальше двигаешься в геймдеве, тем больше понимаешь, что интерфейс - это не украшение, а часть механики. Через него игрок принимает решения, а значит он напрямую влияет на игровой цикл.
Следующим шагом я сделал случайные апгрейды. Вот здесь игра начала ощущаться уже намного ближе к жанру рогалика. Если раньше улучшение можно было выдать просто как фиксированную награду, то теперь появились рандом, вариативность и разные сборки. А это уже один из самых важных элементов рогалика - каждый новый забег может ощущаться немного по-разному.
На уровне кода тут появились enum UpgradeType, класс данных UpgradeData, список всех возможных улучшений и логика выбора нескольких случайных вариантов. Это был очень полезный шаг в понимании программирования, потому что я начал работать уже не просто с отдельными переменными, а с типами данных и структурой контента.
То есть я не хардкодил каждое улучшение отдельно в стиле "если кнопка 1, то плюс 10 к здоровью", а начал мыслить системно:
есть тип улучшения,
есть данные улучшения,
есть список возможных вариантов,
есть метод применения эффекта.
И вот это уже намного ближе к тому, как реально строятся игровые системы.
Особенно полезным здесь оказался switch. Это конструкция, которая позволяет по-разному реагировать на разные типы улучшений. Например, один апгрейд может увеличивать здоровье, другой - урон, третий - скорость, четвертый - кулдаун атаки. И вместо хаотичного набора условий логика начинает собираться в понятную структуру. Для меня это был важный момент, потому что игра впервые начала работать не только как сцена, но и как система прогрессии.
После апгрейдов я пошёл в сторону разнообразия врагов. Сначала появился второй тип врага, а потом - дальний враг, который уже не просто бежит в упор, а атакует с расстояния. И вот это очень сильно меняет бой. Пока все враги ведут себя одинаково, игрок довольно быстро считывает паттерн. Но как только у тебя на арене появляются разные типы противников, бой становится интереснее - нужно думать о приоритетах, дистанции и позиции.
С точки зрения Unity это был очень важный шаг в использовании prefab-подхода. У меня уже были разные заготовки врагов, и спавнер начал работать не с одним объектом, а с массивом GameObject[] enemyPrefabs. Это очень полезная идея: система спавна остается одной и той же, а контент внутри нее может меняться. То есть код становится более универсальным, а игра - богаче по содержанию.
Когда появился дальний враг, в проекте пошли еще и снаряды. Это уже новый уровень механик, потому что в игре добавляется не просто "объект идёт к игроку", а отдельная сущность, которая появляется, летит, сталкивается и наносит эффект. По сути, пуля или магический снаряд - это тоже самостоятельная игровая система. Для этого использовались отдельные скрипты вроде EnemyBullet и EnemyShooter. И для меня это был важный сдвиг: я начал видеть, как одна механика рождает другую. Появляется дальний враг - значит появляется выстрел. Появляется выстрел - значит нужна логика попадания. Появляется логика попадания - значит надо учитывать дистанцию, ритм боя и реакцию игрока.
Следующим крупным шагом стал dash, то есть рывок игрока. И вот тут управление сразу стало ощущаться гораздо живее. До этого движение было просто движением. А с рывком у игрока появилась дополнительная опция - быстро ворваться, уйти из опасной зоны или выскочить из окружения. Такие вещи очень сильно меняют динамику даже в простом прототипе.
В коде dash был интересен тем, что здесь уже появилась работа с состояниями. Были переменные вроде isDashing, dashTimer, dashCooldown, а также методы вроде StartDash() и отдельная логика движения во время рывка. Для новичка это важный шаг, потому что ты начинаешь понимать: поведение персонажа - это не всегда один непрерывный режим. Иногда у него есть отдельные состояния, и в каждом из них игра должна вести себя по-разному. Обычное движение - одно состояние. Рывок - другое. Ожидание отката - третье.
После этого я добавил неуязвимость во время dash. И вот это уже пример того, как маленькая механика превращается в полноценный инструмент. Если рывок просто ускоряет движение - это удобно. Но если во время него игрок еще и кратко не получает урон, тогда dash становится полноценной боевой механикой. Его уже можно использовать не только для перемещения, но и для грамотного уклонения.
Здесь в дело пошёл обновленный PlayerHealth с флагом isInvulnerable и методом SetInvulnerable(). Это хороший пример того, как одна система начинает взаимодействовать с другой. Логика dash находится в одном скрипте, а логика получения урона - в другом. Но во время рывка один скрипт временно сообщает другому: "сейчас урон применять нельзя". И вот именно такие связи между системами делают игру глубже.
Дальше я перешел к очень важной части - переходам между комнатами. И вот здесь проект наконец начал отходить от формата "одна арена с волнами" к структуре, более похожей на настоящий рогалик. Потому что рогалик - это не только бой, но и движение по цепочке комнат, выбор следующего шага, ощущение пути.
Для этого у меня появились отдельные сцены комнат, ExitPortal, настройки Build Settings, а также обновленный RoomManager, который после выполнения условий открывал путь дальше. С точки зрения Unity это был уже важный уровень понимания проекта: я начал работать не внутри одной сцены, а между сценами. То есть игра стала состоять не из одного замкнутого пространства, а из связанных между собой частей.
Но самое интересное началось, когда я сделал сохранение прогресса между комнатами. Потому что если игрок переходит дальше, а все его улучшения и параметры сбрасываются, то ощущение забега ломается. Чтобы это исправить, я использовал RunData и DontDestroyOnLoad. И это был реально очень важный шаг в понимании архитектуры.
DontDestroyOnLoad - одна из ключевых механик Unity для таких задач. Она позволяет сохранить объект при загрузке новой сцены. То есть появляется сущность, которая живет не внутри одной комнаты, а на протяжении всего забега. Это уже почти мышление на уровне менеджера сессии: есть текущий run, у него есть состояние, и это состояние должно существовать независимо от конкретной комнаты.
Потом я сделал случайный выбор следующей комнаты. И вот тут структура рогалика стала ощущаться еще сильнее. Потому что теперь переход дальше - это уже не просто "следующая сцена по порядку", а элемент неопределенности. А неопределенность для рогалика очень важна. Она делает прохождение менее линейным и дает ощущение, что каждое продвижение вперед может привести к новой ситуации.
Для этого в ExitPortal использовался массив строк с возможными сценами, например string[] possibleNextScenes, а затем случайный выбор одной из них. Это простой, но очень полезный пример того, как даже через базовые структуры данных можно добавить в игру ощущение разнообразия. Иногда не нужны сложные генераторы, чтобы проект стал интереснее - достаточно правильно использовать уже имеющиеся инструменты.
И вишенкой на этом этапе стали разные типы комнат. Когда я добавил RoomType, RoomDefinition и разделение на Combat, Reward, Rest и Elite, проект окончательно начал отходить от формата "одна и та же арена раз за разом". Появилась структура, где каждая следующая комната может нести разный смысл: где-то бой, где-то награда, где-то передышка, где-то сложный вызов. А это уже одна из самых вкусных частей рогалика - не просто сражаться, а двигаться по системе решений и состояний.
С точки зрения программирования это тоже был очень сильный этап. Я на практике потрогал enum для описания типов комнат и апгрейдов, serializable data-классы для хранения данных, списки и массивы для выбора контента, switch для применения разных эффектов, состояния персонажа вроде dash и неуязвимости, межсценовую логику через DontDestroyOnLoad, случайный выбор следующего шага и менеджеры, которые управляют не одной механикой, а целым этапом игры.
И вот это, наверное, главный итог третьего дня. На первом этапе у меня появился живой прототип. На втором - боёвка и волны. А на третьем проект начал получать то, что делает рогалик рогаликом: прогрессию, вариативность, новые роли врагов, мобильность игрока, переходы между комнатами и структуру забега.
Именно в этот момент я впервые почувствовал, что делаю уже не просто учебную сцену в Unity, а собираю настоящую систему, которую можно дальше расширять, балансить, усложнять и постепенно превращать в полноценную игру.