Разработка мобильной игры на SpriteKit (iOS)

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Разработка мобильной игры на SpriteKit (iOS)
Средняя
от 1 недели до 3 месяцев
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Разработка мобильной игры на SpriteKit (iOS)

SpriteKit — нативный 2D-фреймворк Apple, встроенный в iOS SDK с версии 7. Не требует сторонних зависимостей, хорошо интегрируется с GameplayKit для логики ИИ противников, работает поверх Metal и показывает стабильные 60 fps на iPhone SE второго поколения при разумной нагрузке. Для 2D-игр с умеренной сложностью это разумный выбор — особенно если команда уже пишет на Swift и не хочет тащить в проект Unity или Godot.

Архитектура игры: сцены, ноды и физика

Всё в SpriteKit — это дерево SKNode. SKScene — корневой контейнер, SKSpriteNode — отрисовываемый объект, SKEmitterNode — система частиц, SKLabelNode — текст. Типичная ошибка в первых проектах — создавать сцены как «монолит», мешая логику движения, отрисовку, звук и UI в одном файле. При 200 строках это уже нечитаемо.

Рабочая структура через компонентный подход с GKComponent из GameplayKit:

class EnemyNode: SKSpriteNode {
    var movementComponent: MovementComponent?
    var healthComponent: HealthComponent?
}

class MovementComponent: GKComponent {
    override func update(deltaTime seconds: TimeInterval) {
        guard let node = entity?.component(ofType: GKSKNodeComponent.self)?.node else { return }
        node.position.y -= CGFloat(150 * seconds)
    }
}

Это позволяет тестировать MovementComponent изолированно и переиспользовать между разными типами врагов.

Физический движок SpriteKit основан на Box2D. SKPhysicsBody есть трёх видов: circleOfRadius, rectangleOf(size:) и bodyWithTexture(_:alphaThreshold:size:) — последний генерирует полигональный коллайдер по пикселям текстуры. На практике bodyWithTexture с alphaThreshold: 0.5 удобен, но дорог: на сложных текстурах генерация тела занимает ощутимое время. Кешируем и переиспользуем:

extension SKPhysicsBody {
    private static var cache: [String: SKPhysicsBody] = [:]

    static func cached(texture: SKTexture, size: CGSize, key: String) -> SKPhysicsBody {
        if let cached = cache[key] {
            return cached.copy() as! SKPhysicsBody
        }
        let body = SKPhysicsBody(texture: texture, alphaThreshold: 0.5, size: size)
        cache[key] = body
        return body.copy() as! SKPhysicsBody
    }
}

Коллизии настраиваются через categoryBitMask и contactTestBitMask. Типичная проблема — пропуск коллизий при высокой скорости объекта («туннелирование»). Решение: usesPreciseCollisionDetection = true для быстрых тел, но это дороже по CPU. Альтернатива — SKPhysicsWorld.enumerateBodies(alongRayStart:end:using:) для ручных ray cast проверок в update(_:).

Атлас текстур и производительность

Draw call — главный враг производительности в SpriteKit. Каждая уникальная текстура — потенциально отдельный draw call. SKTextureAtlas группирует спрайты в один атлас:

let atlas = SKTextureAtlas(named: "Enemies")
let texture = atlas.textureNamed("enemy_run_01")

Xcode компилирует атлас автоматически из папки .spriteatlas. Правило: всё что рисуется одновременно — в один атлас. Проверить количество draw calls можно в Xcode через View → Debug → Statistics прямо в симуляторе при запущенной игре.

При SKSpriteNode размером 64×64 и текстурой 512×512 Metal выполняет downscale на GPU каждый кадр. Текстуры должны быть максимально близки к размеру отображения. Xcode Instruments → Metal System Trace покажет, если GPU перегружен ненужным масштабированием.

Анимация через SKAction.animate(with:timePerFrame:):

let frames = (1...8).map { atlas.textureNamed("run_\(String(format: "%02d", $0))") }
let animation = SKAction.animate(with: frames, timePerFrame: 1.0/12.0, resize: false, restore: false)
let loop = SKAction.repeatForever(animation)
character.run(loop, withKey: "running")

withKey: позволяет остановить или заменить анимацию позже через removeAction(forKey:).

Звук: AVAudioEngine вместо SKAction.playSoundFileNamed

SKAction.playSoundFileNamed(_:waitForCompletion:) — удобно для прототипа, но не годится для продакшена: нет контроля громкости, нет возможности поставить на паузу, файл декодируется при каждом вызове. Для игр используем AVAudioEngine с AVAudioPlayerNode:

class AudioManager {
    private let engine = AVAudioEngine()
    private var playerNodes: [String: AVAudioPlayerNode] = [:]
    private var audioFiles: [String: AVAudioFile] = [:]

    func preloadSound(named name: String) throws {
        let url = Bundle.main.url(forResource: name, withExtension: "wav")!
        audioFiles[name] = try AVAudioFile(forReading: url)
    }

    func playSound(named name: String) {
        guard let file = audioFiles[name] else { return }
        let node = AVAudioPlayerNode()
        engine.attach(node)
        engine.connect(node, to: engine.mainMixerNode, format: file.processingFormat)
        node.scheduleFile(file, at: nil)
        node.play()
    }
}

Предзагрузка звуков в фоне при старте сцены, не блокируя main thread.

GameplayKit: поведение врагов без велосипеда

GKStateMachine отлично подходит для AI-состояний врага:

class EnemyIdleState: GKState {
    override func isValidNextState(_ stateClass: AnyClass) -> Bool {
        stateClass == EnemyChaseState.self || stateClass == EnemyAttackState.self
    }
}

GKAgent2D с GKGoal позволяет реализовать pursuit, flee, flocking без ручного программирования векторной математики. Для процедурной генерации уровней — GKNoise и GKPerlinNoiseSource.

Типичные проблемы в продакшене

Просадка FPS при появлении многих врагов — чаще всего SKPhysicsBody у каждого с точными коллайдерами. Решение: упростить коллайдеры до circleOfRadius или rectangleOf, физику точного столкновения делать только для игрока.

Утечки памяти при смене сценSKScene не освобождается, если остались неотменённые SKAction с сильными ссылками на объекты. Всегда вызываем removeAllActions() в willMove(from:).

Текстуры не выгружаютсяSKTextureAtlas держится в памяти пока хоть один SKSpriteNode использует его текстуру. При смене уровня явно заменяем текстуры нодов на SKTexture() перед удалением сцены, потом вызываем removeFromParent().

Этапы работы

Аудит ТЗ: жанр, количество уровней, монетизация (IAP, реклама), целевые устройства, iOS-минимум.

Прототип: core gameplay loop за первую неделю — именно в этот момент понятно, стоит ли идти дальше с SpriteKit или нужен Unity.

Разработка: сцены, игровая механика, физика, AI, звук, UI (отдельная SKScene поверх игровой или UIKit-оверлей через SKView).

Интеграция Game Center: таблицы рекордов, достижения.

Тестирование на реальных устройствах: iPhone SE 2gen (слабое GPU), iPad Pro (большой экран, другой aspect ratio).

Публикация: App Store Connect, возрастной рейтинг, метаданные.

Ориентиры по срокам

Сложность игры Срок
Простая казуалка (1-3 механики, 5-10 уровней) 2–4 недели
Средний проект (10+ уровней, AI враги, IAP) 1,5–2 месяца
Полноценная игра с контентом 2–3 месяца

Сроки сильно зависят от объёма контента (графика, звук) — если ассеты готовы, разработка быстрее. Если нужно создавать с нуля — добавляем время на дизайн.