SceneKit для Swift: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

SceneKit для Swift: Полное руководство по 3D-графике

SceneKit — это мощный фреймворк для работы с 3D-графикой, разработанный компанией Apple. Он предоставляет разработчикам высокоуровневый API для создания, визуализации и анимации трёхмерных сцен на всех платформах Apple: iOS, macOS, tvOS и watchOS. В отличие от низкоуровневых графических API (Metal, OpenGL), SceneKit абстрагирует сложные математические вычисления и работу с GPU, позволяя сосредоточиться на логике приложения.

Зачем нужен SceneKit? Этот фреймворк идеально подходит для создания игр, интерактивных 3D-презентаций, визуализации данных, архитектурных макетов и образовательных приложений. Он поддерживает физический движок (симуляция гравитации, коллизий), систему частиц, скелетную анимацию, работу с тенями и светом, а также интеграцию с дополненной реальностью (ARKit). SceneKit позволяет загружать модели в форматах .dae (Collada), .scn (собственный формат Apple) и .usdz, что делает его отличным инструментом для импорта ассетов из Blender, Maya или 3ds Max.

Главное преимущество SceneKit перед конкурентами (например, Unity) — это нативная интеграция с экосистемой Apple. Вы можете использовать SwiftUI, UIKit или AppKit для создания интерфейса, а SceneKit — для отрисовки 3D-контента, при этом весь код будет написан на одном языке (Swift). Фреймворк автоматически управляет памятью и оптимизирует рендеринг под конкретное устройство, что особенно важно для мобильных приложений.

Установка и настройка

SceneKit является частью стандартного SDK Apple, поэтому отдельная установка через менеджеры пакетов (CocoaPods, Swift Package Manager) не требуется. Для начала работы достаточно импортировать фреймворк в ваш проект.

Команда для установки отсутствует, так как фреймворк предустановлен. Однако, если вы используете Swift Package Manager для зависимостей, убедитесь, что ваш проект настроен на iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 3.0+.

Для импорта в код используйте:

import SceneKit
import SceneKit.ModelIO // Для загрузки моделей через Model I/O

Если вы работаете с Xcode, просто добавьте SceneKit.framework в раздел "Frameworks, Libraries, and Embedded Content" вашего таргета.

Быстрый старт: минимальный рабочий пример

Создадим простейшую 3D-сцену с вращающимся кубом. Этот пример можно запустить в Playground или в приложении для iOS/macOS.

import SceneKit
import PlaygroundSupport

// 1. Создаем сцену
let scene = SCNScene()

// 2. Создаем камеру
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
scene.rootNode.addChildNode(cameraNode)

// 3. Создаем куб
let boxGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
let boxNode = SCNNode(geometry: boxGeometry)
boxNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene.rootNode.addChildNode(boxNode)

// 4. Добавляем анимацию вращения
let rotateAction = SCNAction.rotateBy(x: 0, y: CGFloat.pi * 2, z: 0, duration: 5.0)
let repeatAction = SCNAction.repeatForever(rotateAction)
boxNode.runAction(repeatAction)

// 5. Настраиваем отображение в Playground
let scnView = SCNView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
scnView.scene = scene
scnView.backgroundColor = UIColor.darkGray
scnView.autoenablesDefaultLighting = true

PlaygroundPage.current.liveView = scnView

Основные классы и методы SceneKit

Ниже представлены ключевые компоненты фреймворка с примерами использования.

1. SCNScene — контейнер для всей 3D-сцены

Сигнатура: class SCNScene : NSObject

Что делает: Является корневым объектом, содержащим все узлы (SCNNode), геометрию, источники света, камеры и физические тела. Сцена — это граф узлов.

let scene = SCNScene()
scene.background.contents = UIColor.black // Устанавливаем цвет фона

2. SCNNode — узел сцены

Сигнатура: class SCNNode : NSObject

Что делает: Представляет элемент в графе сцены. Узел может содержать геометрию, свет, камеру или быть пустым (для группировки). Узлы образуют иерархию: перемещение родительского узла влияет на все дочерние.

let parentNode = SCNNode()
parentNode.position = SCNVector3(0, 1, 0)
scene.rootNode.addChildNode(parentNode)

let childNode = SCNNode(geometry: SCNSphere(radius: 0.5))
childNode.position = SCNVector3(0, 0, 0)
parentNode.addChildNode(childNode)

3. SCNGeometry — геометрическая форма

Сигнатура: class SCNGeometry : NSObject

Что делает: Абстрактный класс для всех 3D-форм. SceneKit предоставляет встроенные примитивы: SCNBox, SCNSphere, SCNCylinder, SCNCone, SCNTorus, SCNPlane, SCNText. Можно создавать кастомную геометрию через SCNGeometrySource и SCNGeometryElement.

let sphere = SCNSphere(radius: 1.0)
sphere.segmentCount = 48 // Увеличиваем детализацию сферы
let sphereNode = SCNNode(geometry: sphere)

4. SCNMaterial — материал для геометрии

Сигнатура: class SCNMaterial : NSObject

Что делает: Определяет внешний вид поверхности: цвет, текстуру, прозрачность, отражение. Каждая грань геометрии может иметь свой материал (diffuse, specular, normal, emission и т.д.).

let material = SCNMaterial()
material.diffuse.contents = UIColor.blue
material.specular.contents = UIColor.white
material.shininess = 0.5

let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1)
box.materials = [material]

5. SCNLight — источник света

Сигнатура: class SCNLight : NSObject

Что делает: Освещает сцену. Поддерживаются типы: omni (всенаправленный), directional (направленный), spot (прожектор), ambient (окружающий). Свет может отбрасывать тени.

let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .directional
lightNode.light?.color = UIColor.white
lightNode.light?.castsShadow = true
lightNode.position = SCNVector3(2, 3, 4)
lightNode.eulerAngles = SCNVector3(-Float.pi/4, Float.pi/4, 0)
scene.rootNode.addChildNode(lightNode)

6. SCNCamera — камера

Сигнатура: class SCNCamera : NSObject

Что делает: Определяет точку обзора сцены. Можно настроить угол обзора (fieldOfView), глубину резкости (zNear, zFar), эффекты размытия и HDR.

let camera = SCNCamera()
camera.fieldOfView = 60
camera.zNear = 0.1
camera.zFar = 100
camera.wantsHDR = true

let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(0, 0, 10)

7. SCNAction — анимация действий

Сигнатура: class SCNAction : NSObject

Что делает: Позволяет анимировать свойства узла (позицию, вращение, масштаб) без написания циклов. Поддерживает последовательности, группы, повторения и easing-функции.

let moveAction = SCNAction.move(by: SCNVector3(0, 2, 0), duration: 1.0)
let fadeAction = SCNAction.fadeOut(duration: 0.5)
let sequence = SCNAction.sequence([moveAction, fadeAction])
node.runAction(sequence)

8. SCNPhysicsBody — физическое тело

Сигнатура: class SCNPhysicsBody : NSObject

Что делает: Добавляет узлу физические свойства: массу, трение, упругость. Узел начинает взаимодействовать с гравитацией и другими физическими телами. Типы: static, dynamic, kinematic.

let floor = SCNFloor()
let floorNode = SCNNode(geometry: floor)
floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)

let ball = SCNSphere(radius: 0.5)
let ballNode = SCNNode(geometry: ball)
ballNode.position = SCNVector3(0, 5, 0)
ballNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
ballNode.physicsBody?.mass = 1.0

scene.physicsWorld.gravity = SCNVector3(0, -9.8, 0)

9. SCNParticleSystem — система частиц

Сигнатура: class SCNParticleSystem : NSObject

Что делает: Создает эффекты частиц: дым, огонь, снег, искры. Можно настроить скорость, цвет, размер, время жизни частиц.

let particleSystem = SCNParticleSystem()
particleSystem.birthRate = 100
particleSystem.particleLifeSpan = 2.0
particleSystem.particleColor = UIColor.orange
particleSystem.emissionDuration = 5.0

let emitterNode = SCNNode()
emitterNode.addParticleSystem(particleSystem)
emitterNode.position = SCNVector3(0, 0, 0)

10. SCNTransaction — пакетное обновление анимации

Сигнатура: class SCNTransaction : NSObject

Что делает: Позволяет группировать изменения свойств и анимировать их как единый блок. Аналог UIView.animate для SceneKit.

SCNTransaction.begin()
SCNTransaction.animationDuration = 2.0
node.position = SCNVector3(5, 0, 0)
node.opacity = 0.5
SCNTransaction.commit()

Реальный практический пример: Интерактивная солнечная система

Создадим миниатюрную модель солнечной системы с вращающимися планетами, источником света и возможностью вращения камеры жестом.

import SceneKit
import UIKit

class SolarSystemViewController: UIViewController {
    var scnView: SCNView!
    var scene: SCNScene!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupScene()
        setupCamera()
        setupLight()
        createSun()
        createEarth()
        createMoon()
        addGestures()
    }
    
    func setupScene() {
        scene = SCNScene()
        scene.background.contents = UIImage(named: "stars_background")
        
        scnView = SCNView(frame: view.bounds)
        scnView.scene = scene
        scnView.allowsCameraControl = true // Встроенное управление камерой
        scnView.showsStatistics = true
        view.addSubview(scnView)
    }
    
    func setupCamera() {
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(0, 5, 15)
        cameraNode.look(at: SCNVector3(0, 0, 0))
        scene.rootNode.addChildNode(cameraNode)
    }
    
    func setupLight() {
        // Основной свет (Солнце)
        let sunLight = SCNNode()
        sunLight.light = SCNLight()
        sunLight.light?.type = .omni
        sunLight.light?.color = UIColor(red: 1.0, green: 0.8, blue: 0.2, alpha: 1.0)
        sunLight.light?.intensity = 1200
        sunLight.position = SCNVector3(0, 0, 0)
        scene.rootNode.addChildNode(sunLight)
        
        // Окружающий свет
        let ambientLight = SCNNode()
        ambientLight.light = SCNLight()
        ambientLight.light?.type = .ambient
        ambientLight.light?.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLight)
    }
    
    func createSun() {
        let sunGeometry = SCNSphere(radius: 1.5)
        let sunMaterial = SCNMaterial()
        sunMaterial.diffuse.contents = UIColor.orange
        sunMaterial.emission.contents = UIColor(red: 1.0, green: 0.5, blue: 0.0, alpha: 1.0)
        sunGeometry.materials = [sunMaterial]
        
        let sunNode = SCNNode(geometry: sunGeometry)
        sunNode.position = SCNVector3(0, 0, 0)
        scene.rootNode.addChildNode(sunNode)
        
        // Анимация вращения Солнца
        let rotateAction = SCNAction.rotateBy(x: 0, y: CGFloat.pi * 2, z: 0, duration: 20.0)
        sunNode.runAction(SCNAction.repeatForever(rotateAction))
    }
    
    func createEarth() {
        let earthGeometry = SCNSphere(radius: 0.6)
        let earthMaterial = SCNMaterial()
        earthMaterial.diffuse.contents = UIColor.blue
        earthMaterial.specular.contents = UIColor.white
        earthGeometry.materials = [earthMaterial]
        
        let earthNode = SCNNode(geometry: earthGeometry)
        earthNode.position = S                    

Рекомендаций