Разработка мобильной игры на SceneKit (iOS)
SceneKit — нативный 3D-фреймворк Apple поверх Metal. Появился в iOS 8, значительно вырос в iOS 12–16. Это не Unity и не Unreal: нет визуального редактора pipeline, нет Asset Store, меньше готовых решений. Но это и не сырой Metal: физический движок, анимации, shader модификаторы, AR-интеграция через ARKit — всё входит в SDK без сторонних зависимостей. Для iOS-эксклюзивных 3D-игр средней сложности — вполне рабочий вариант, особенно если команда глубоко в Swift/Objective-C.
Сцена, ноды, рендеринг
Базовая единица — SCNNode. Сцена — дерево нодов: корень SCNScene.rootNode, к нодам прикреплены геометрия (SCNGeometry), свет (SCNLight), камера (SCNCamera), физическое тело (SCNPhysicsBody). Отличие от SpriteKit в том, что SceneKit работает в 3D-пространстве с координатами SCNVector3, а трансформации задаются через simd_float4x4 (или удобные свойства position, eulerAngles, simdTransform).
Загрузка сцены из .scn-файла (редактируется прямо в Xcode Scene Editor):
guard let scene = SCNScene(named: "GameLevel.scn") else {
fatalError("Scene not found")
}
let scnView = SCNView(frame: view.bounds)
scnView.scene = scene
scnView.allowsCameraControl = false
scnView.rendersContinuously = true // важно для анимаций
view.addSubview(scnView)
rendersContinuously = true — если не выставить, SCNView рендерит только при изменении сцены. Для игр с постоянным движением нужен постоянный рендер. Обратная сторона — потребление батареи. Для меню с редкими изменениями оставляем false.
Физика и коллизии в 3D
SCNPhysicsBody трёх типов: .static (неподвижные объекты, коллайдер не движется), .dynamic (под управлением физики), .kinematic (движется кодом, игнорирует силы, но участвует в коллизиях). Персонаж игрока обычно kinematic — мы двигаем его через код, но стены и пол его останавливают.
Форма коллайдера влияет на производительность сильнее, чем в 2D:
// Дорого: SCNPhysicsShape(geometry: complexMesh) — convex hull по 10k вершинам
// Нормально: bounding box или capsule для персонажа
let capsule = SCNCapsule(capRadius: 0.3, height: 1.8)
let physicsShape = SCNPhysicsShape(geometry: capsule, options: nil)
player.physicsBody = SCNPhysicsBody(type: .kinematic, shape: physicsShape)
Коллизии через SCNPhysicsContactDelegate:
func physicsWorld(_ world: SCNPhysicsWorld,
didBegin contact: SCNPhysicsContact) {
let nodeA = contact.nodeA
let nodeB = contact.nodeB
// Обрабатываем столкновение
}
categoryBitMask, collisionBitMask, contactTestBitMask — та же битмаск-система, что и в SpriteKit. Правило: устанавливай contactTestBitMask только для пар, которые реально нужно обрабатывать, иначе didBegin будет вызываться слишком часто.
Анимации: CAAnimation и SCNAnimationPlayer
SceneKit поддерживает анимации из .dae (Collada) и .usdz файлов. Для скелетальной анимации персонажа — импорт из .dae:
let idleScene = SCNScene(named: "character_idle.dae")!
// Извлекаем анимацию из сцены
if let animationKey = idleScene.rootNode.animationKeys.first,
let animation = idleScene.rootNode.animationPlayer(forKey: animationKey) {
characterNode.addAnimationPlayer(animation, forKey: "idle")
animation.play()
}
Переключение между анимациями (idle → run → jump) реализуем через SCNAnimationPlayer с blendInDuration для плавного перехода:
func transition(to key: String, blendDuration: CGFloat = 0.3) {
guard let player = characterNode.animationPlayer(forKey: key) else { return }
player.blendInDuration = blendDuration
player.play()
// Останавливаем текущую
currentAnimationKey.flatMap { characterNode.animationPlayer(forKey: $0) }?.stop(blendOutDuration: blendDuration)
currentAnimationKey = key
}
Без blendInDuration персонаж «прыгает» между позами — это первое, что замечает игрок.
Шейдеры и постэффекты
SceneKit позволяет подключать GLSL/Metal шейдеры через SCNMaterial.shaderModifiers. Четыре точки подключения: .geometry, .surface, .lightingModel, .fragment. Типичное применение — растворение объекта при смерти:
let dissolveShader = """
#pragma transparent
#pragma body
float threshold = u_dissolveAmount;
float noise = ... // шум по координатам
if (noise < threshold) discard_fragment();
_output.color.a = smoothstep(threshold - 0.05, threshold, noise);
"""
material.shaderModifiers = [.fragment: dissolveShader]
SCNTechnique — для постобработки всего экрана (bloom, outline, depth of field). Конфигурируется через plist-словарь с описанием passes и render targets. Сложнее, чем в Unity, но работает поверх Metal без дополнительных библиотек.
ARKit интеграция
SceneKit — первый и основной renderer для ARKit:
let arView = ARSCNView(frame: view.bounds)
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.horizontal, .vertical]
arView.session.run(config)
ARSCNView автоматически синхронизирует SCNScene с AR-координатным пространством. ARSCNViewDelegate позволяет реагировать на обнаружение плоскостей, добавлять якоря. Это основа для AR-игр — маркет ARKit + SceneKit хорошо закрыт примерами Apple.
Производительность: металл под капотом
SCNView по умолчанию использует Metal на iOS 9+. Несколько правил, которые реально влияют на fps:
Batching: SceneKit автоматически объединяет draw calls для нодов с одинаковым материалом. Поэтому «1000 деревьев с одним материалом» гораздо дешевле, чем «100 деревьев с 100 разными материалами». Используй SCNMaterial инстансы, не создавай новый объект материала для каждого нода.
Level of Detail через SCNLevelOfDetail:
let lods = [
SCNLevelOfDetail(geometry: mediumDetailMesh, worldSpaceDistance: 20),
SCNLevelOfDetail(geometry: lowDetailMesh, worldSpaceDistance: 50)
]
node.geometry?.levelsOfDetail = lods
Occlusion culling: SceneKit делает frustum culling автоматически, но occlusion culling (скрытие объектов за другими объектами) — нет. Для сложных сцен это нужно делать вручную через isHidden = true по результатам ray cast или логики уровня.
Инструмент для профилирования: Xcode → Metal System Trace + Render Graph (Instruments). Показывает количество draw calls, использование GPU, tile memory на Apple Silicon. Цель — не более 30–50 draw calls для игры с стабильными 60 fps на iPhone X.
Когда SceneKit — не лучший выбор
Мультиплатформа (iOS + Android + PC) — Unity или Godot. Сложная физика тел с деформацией — Unity с Havok. Массивные открытые миры — тоже Unity/Unreal. SceneKit хорош для iOS-эксклюзива с умеренной 3D-сложностью, AR-приложений и игр, где важна нативная интеграция (Game Center, CloudKit, Apple Silicon optimizations).
Этапы работы
Аудит ТЗ: тип игры, нужен ли AR, целевые устройства, iOS-минимум, ассеты (3D-модели, анимации).
Прототип игровой механики: движение персонажа, физика, базовая камера — 1–2 недели.
Core gameplay: уровни, враги / препятствия, UI (SwiftUI поверх SCNView или UIKit).
Аудио: AVAudioEngine для 3D-звука через AVAudio3DMixing.
Game Center, IAP (если нужно), ARKit (если AR-составляющая).
Полировка производительности, тестирование на слабых устройствах.
Ориентиры по срокам
| Тип проекта | Срок |
|---|---|
| Прототип / proof of concept | 2–3 недели |
| Казуальная 3D-игра без AR | 1,5–2 месяца |
| 3D-игра с AR + Game Center | 2–3 месяца |
| Сложный проект (открытый мир, мультиплеер) | Обсуждается отдельно |
Стоимость рассчитывается индивидуально после анализа ТЗ, наличия готовых ассетов и требований к платформам.







