Приветствую! Хочу поделиться опытом создания своей дебютной мобильной игры в жанре roguelite для iOS. Мой pet-проект неожиданно для меня самого перерос рамки экспериментов и успешно вышел в App Store. Ниже — подробный рассказ о концепции и технической реализации проекта.
История создания
В процессе обучения iOS-разработке я пришел к выводу, что лучший способ закрепить навыки — это реализовать серию полноценных продуктов. Одним из них стала игра LootShrine — ссылка на страницу в App Store.
Это пошаговый «рогалик», сфокусированный на управлении инвентарем и тактических сражениях. В игре представлено несколько режимов, включая бесконечный и сюжетный забег по стоэтажной башне. Геймплей завязан на принятии решений: каждый найденный предмет можно либо использовать, либо поглотить для получения временного усиления, либо принести в жертву святилищу ради перманентного прогресса. Идейным вдохновителем выступила игра Megaloot — на мобильном рынке мне не удалось найти аналогов с подобным темпом, поэтому я решил восполнить этот пробел.
Стек и архитектурные решения
Главным ограничением стало отсутствие собственного бэкенда. Я сделал осознанный выбор в пользу нативных сервисов Apple, чтобы не переусложнять разработку и избежать дополнительных расходов на серверную инфраструктуру. Весь функционал — от системы сохранений до мультиплеерных механик — реализован через CloudKit и Game Center.
В основе проекта лежит чистый SwiftUI (iOS 17+/macOS 14+) без сторонних UI-фреймворков. Внешние зависимости сведены к минимуму: RevenueCat для платежей и TelemetryDeck для аналитики. Последний выбор был обусловлен политикой privacy-first: сбор ведется исключительно анонимно, что избавляет от проблем с разрешениями в App Store и повышает доверие пользователей.
Архитектура построена на базе Redux-подобного подхода. Вместо стандартного MVVM я выбрал этот путь по ряду причин:
-
Единый источник истины. Все данные игры хранятся в структуре GameState. Это обеспечивает синхронность состояния между десятками игровых экранов.
-
Декларативные действия. Enum GameAction инкапсулирует каждое возможное событие: от атаки противника до покупки артефакта.
-
Чистые функции. Редьюсеры представляют собой функции типа (State, Action) → State, что идеально подходит для пошаговой логики, исключая скрытые побочные эффекты.
-
Использование @Observable. Нативный механизм Observation из iOS 17 значительно снизил объем шаблонного кода и позволил реализовать точечное обновление интерфейса.
-
View как слой отображения. Вьюхи лишь транслируют действия в стор, не влияя на состояние напрямую.
-
Корневой редьюсер берет на себя роль диспетчера, делегируя задачи узкоспециализированным модулям (Battle, Shop, Shrine и т.д.). Такая модульность позволяет эффективно поддерживать растущую кодовую базу.
Графика: работа с атласами
Пиксель-арт рендерится через SwiftUI путем вырезки фрагментов из текстурных атласов. Это позволяет избежать нагрузки от тысяч мелких файлов. Атрибут interpolation(.none) гарантирует четкость пикселей, а сам рендеринг осуществляется через смещение координат. Использование атласов стало основным методом отображения графики.
Генерация контента
Для создания ассетов я комбинировал возможности ChatGPT и Aseprite. Нейросеть генерировала концепты, которые я дорабатывал вручную: приводил к единому формату, чистил и собирал в атласы. Качественный результат требует именно такой «ручной» доводки, поскольку автоматизированная генерация дает лишь базовый шаблон.
Проблематика SpriteKit
Изначально все эффекты рисовались средствами SwiftUI, но их жизненный цикл оказался слишком нестабильным для таймингов анимаций. Поэтому я интегрировал прозрачную SKScene поверх основного интерфейса. Это обеспечило предсказуемое воспроизведение эффектов через SKAction. Недостатком решения стала необходимость синхронизации координат между двумя слоями, что является главным вектором для будущего рефакторинга.
Инфраструктура CloudKit и Game Center
Game Center берет на себя соревновательный элемент: более 70 таблиц лидеров и 75 достижений. Предусмотрена офлайн-очередь для передачи данных, чтобы прогресс не терялся при разрыве соединения.
CloudKit обеспечивает бесшовную облачную синхронизацию, кросс-девайсную работу сейвов, а также функционал «призраков» (challenge-забеги с друзьями) и «Зал павших», где пользователи могут соревноваться с результатами других игроков в асинхронном режиме.
Уроки разработки
Работа над проектом стала отличной школой: я осознал пределы SwiftUI в анимациях, научился эволюционировать схемы данных без нарушения обратной совместимости (через использование Optional-полей) и выстроил рабочую экосистему на базе нативного стека Apple и AI-помощников вроде Claude.
Буду рад вопросам — делитесь мнением или заглядывайте в игру по ссылке в начале текста!
P. S. Ниже представлены скриншоты для ознакомления.


