Лучшие рогалики не просто убивают вас. Они заставляют вас хотеть умереть.

Звучит странно, но любой, кто проиграл забег в полночь и начал новый в 12:03, знает это чувство. Смерть жалит. Ваше здоровье падает до нуля. Экран заливает осознание неудачи. И все же ваш палец уже занесен над кнопкой «play». Что-то в последних двадцати минутах было настолько важным, что вы не можете оставить игру на том моменте, где она закончилась.

Это цикл «еще один забег», и это не случайность. Это проектное решение. Если вы создаете рогалик или любую игру с перманентной смертью, ваша работа заключается в балансировке двух противоборствующих сил. Игрок должен проигрывать достаточно часто, чтобы чувствовать напряжение. Но он должен сохранять достаточно ресурсов, чтобы не терять надежду.

Валюта поражения

В моем последнем проекте, Neon Survivor, я стремился именно к такому напряжению. Когда игрок умирает, всё собранное за забег исчезает, за исключением одного: золота. Это золото зачисляется в банк автоматически. В меню игрок тратит его на постоянные улучшения. Затем он бросается в бой снова, став чуть сильнее, чем прежде.

Этот простой цикл держит на себе всю игру. Без него смерть — это точка. Игрок уходит, потому что последний забег не принес ему ничего. С ним смерть — это запятая. Забег превращается в сессию фарма. Потеря болезненна, но она также оплатила завтрашний день.

Трюк прост. Вы оставляете что-то игроку, даже когда он теряет всё остальное. Сложность заключается в том, чтобы то, что остается, имело значение, но не уничтожало вызов. Если улучшения слишком слабые, игроку становится всё равно. Если слишком сильные — игра проходит сама собой. В обоих случаях цикл рушится.

Два таймера, ноль путаницы

Чтобы построить это правильно, мне пришлось управлять двумя отдельными временными шкалами.

Run Clock сбрасывается каждый раз, когда вы нажимаете play. Он отслеживает здоровье, текущий счет, количество волн врагов и любые временные усиления, подобранные во время сессии. Когда персонаж умирает, этот таймер откатывается к нулю.

Meta Clock никогда не сбрасывается. Он хранит общее количество заработанного золота за все попытки, самую высокую достигнутую волну и все купленные постоянные улучшения. Этот таймер продолжает тикать, сколько бы раз вы ни обновляли страницу в браузере.

Смешайте их, и вы получите баги, которые трудно отследить и больно исправлять. Я видел, как разработчики случайно стирали прогресс игрока во время рутинного сброса сцены, потому что функция очистки затронула не то хранилище данных. Данные Meta Clock испаряются. Игрок возвращается к нулю золота и нулю улучшений. В этот момент отношения между вами и вашим игроком разрушены. Он не начинает новый забег; он начинает затаивать новую обиду.

Разделение таймеров — это не просто стилистический выбор. Это стратегия выживания.

Как Phaser v4 справляется с разделением

Я создавал Neon Survivor на Phaser v4, который предлагает два конкретных инструмента для решения этой проблемы.

Registry хранит живые данные в памяти текущей сессии. Это быстро. Это просто. Но они испаряются в тот момент, когда игрок обновляет страницу.

LocalStorage сохраняет данные в самом браузере. Они переживают закрытие вкладок, перезапуск браузера и отключение электричества. Однако он медленнее и менее надежен. Браузеры могут блокировать его, ограничивать скорость или очищать, если квоты хранилища заполнены.

Мой подход к проектированию был строгим. Registry является единственным источником истины во время игрового процесса. Игра читает из него, пишет в него и полностью ему доверяет. LocalStorage не выступает в роли соавтора. Он выступает в роли зеркала.

Вот как работает этот процесс. Игра записывает покупку улучшения в Registry. Один класс-менеджер следит за Registry. Когда это необходимо, этот менеджер зеркалирует данные из Registry в LocalStorage. Если браузер блокирует запись, игра не тормозит. Если хранилище дает сбой, текущая сессия всё равно проходит идеально. Игрок может потерять прогресс только в том случае, если закроет вкладку в ту же самую секунду, но сама сессия никогда не зависнет.

Такой паттерн предотвращает скрытую катастрофу. Если позволить каждой системе писать напрямую в LocalStorage, вы создадите зависимости от хрупкого API. Игрок с повышенными настройками конфиденциальности или устройством с малым объемом памяти может заметить замедление или зависание игры во время боя, потому что какая-то фоновая функция попыталась сохранить статистику. Сделав Registry единственным источником истины, вы сохраняете динамику боя и локализуете риски.

Дайте коду дышать

Я также использовал события, чтобы разделить системы. Когда забег заканчивается, GameScene не устраивает собственные похороны. Он не вызывает функцию сохранения. Он не импортирует утилиту для работы с хранилищем. Он просто генерирует событие «run-ended» с полезной нагрузкой из соответствующих данных.

Отдельный слушатель отвечает за ведение учета. Он получает событие, обновляет Meta Clock и дает команду менеджеру зеркалировать новые значения в LocalStorage.

Такое разделение окупается мгновенно. Я могу полностью переписать GameScene, заменить персонажа игрока, изменить угол камеры или даже сменить жанр с survival на bullet hell, не затрагивая систему сохранений. Системы независимы. Они взаимодействуют через события, а не через прямые вызовы функций. Это означает меньше конфликтов слияния, меньше багов и кодовую базу, которая не превратится в спагетти через полгода.

Улучшения, которые меняют правила игры

Наличие технического фундамента бесполезно, если награды ощущаются как скучная электронная таблица. Я потратил много времени на то, чтобы понять, каково на самом деле играть с этими улучшениями.

Некоторые улучшения безопасны. Дополнительная скорость передвижения. Бонус к здоровью. Ускоренная перезарядка. Они дают игроку право на ошибку. Они успокаивают. Они упрощают игру, не меняя ее правил.

Другие улучшения полностью переписывают правила. В Neon Survivor я добавил «Piercing Rounds». До этого улучшения пуля останавливалась при первом же попадании в противника. После улучшения она прошивает врагов насквозь, позволяя одним выстрелом зачищать целые ряды.

Разница колоссальна. Скорость и здоровье могут помочь вам выжить дольше, но «Piercing Rounds» меняет то, как вы позиционируете себя. Вы начинаете выстраивать врагов в линию. Вы перестаете кайтить по краям и начинаете прорубаться через центр. Пространство принятия решений в игре расширяется.

Хорошая прогрессия должна менять решения игрока, а не просто увеличивать его показатели. Если каждое улучшение — это просто процентный прирост, игрок перестает читать описания. Он кликает, улучшает и забывает. Если же улучшение заставляет его переосмыслить стратегию, он его запомнит. Он будет о нем говорить. Он вернется, чтобы увидеть, что еще может перевернуть игру с ног на голову.

Настоящая отдача

Цикл «еще один забег» — это не какая-то одна система. Это взаимосвязь между потерями и приобретениями, построенная на чистой архитектуре и значимых наградах.

Создайте две отдельные временные шкалы и оберегайте Meta Clock так, будто в нем сосредоточено доверие ваших игроков — потому что так оно и есть. Используйте инструменты вашего движка, чтобы обеспечить скорость работы с живыми данными и сохранность персистентных данных. Отвяжите сцены от хранилища, чтобы вы могли вносить изменения без страха. И когда будете проектировать улучшения, спросите себя: дают ли они игроку больше времени или больше интересных возможностей для выбора?

Сделайте это правильно, и ваши игроки не просто смирятся со смертью. Они будут на нее полагаться. Каждый забег станет взносом за следующий. Игра перестанет быть чередой перезапусков и превратится в единое, непрерывное восхождение.