2023 EssentialsSpatial Computing
WWDC23 · 28 min · Essentials / Spatial Computing
Build spatial experiences with RealityKit
Discover how RealityKit can bring your apps into a new dimension. Get started with RealityKit entities, components, and systems, and learn how you can add 3D models and effects to your app on visionOS. We’ll also take you through the RealityView API and demonstrate how to add 3D objects to windows, volumes, and spaces to make your apps more immersive. And we’ll explore combining RealityKit with spatial input, animation, and spatial audio.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 13 snippets
Model3D
import SwiftUI
import RealityKit
struct GlobeModule: View {
var body: some View {
Model3D(named: "Globe") { model in
model
.resizable()
.scaledToFit()
} placeholder: {
ProgressView()
}
}
} Volumetric window
import SwiftUI
import RealityKit
// Define a volumetric window.
struct WorldApp: App {
var body: some SwiftUI.Scene {
// ...
WindowGroup(id: "planet-earth") {
Model3D(named: "Globe")
}
.windowStyle(.volumetric)
.defaultSize(width: 0.8, height: 0.8, depth: 0.8, in: .meters)
}
} ImmersiveSpace
import SwiftUI
import RealityKit
// Define a immersive space.
struct WorldApp: App {
var body: some SwiftUI.Scene {
// ...
ImmersiveSpace(id: "objects-in-orbit") {
RealityView { content in
// ...
}
}
}
} RealityView
import SwiftUI
import RealityKit
struct Orbit: View {
let earth: Entity
var body: some View {
RealityView { content in
content.add(earth)
}
}
} RealityView asynchronous loading and entity positioning
import SwiftUI
import RealityKit
struct Orbit: View {
var body: some View {
RealityView { content in
async let earth = ModelEntity(named: "Earth")
async let moon = ModelEntity(named: "Moon")
if let earth = try? await earth, let moon = try? await moon {
content.add(earth)
content.add(moon)
moon.position = [0.5, 0, 0]
}
}
}
} Earth rotation
import SwiftUI
import RealityKit
struct RotatedModel: View {
var entity: Entity
var rotation: Rotation3D
var body: some View {
RealityView { content in
content.add(entity)
} update: { content in
entity.orientation = .init(rotation)
}
}
} Converting co-ordinate spaces
import SwiftUI
import RealityKit
struct ResizableModel: View {
var body: some View {
GeometryReader3D { geometry in
RealityView { content in
if let earth = try? await ModelEntity(named: "Earth") {
let bounds = content.convert(geometry.frame(in: .local),
from: .local, to: content)
let minExtent = bounds.extents.min()
earth.scale = [minExtent, minExtent, minExtent]
}
}
}
}
} Play an animation
import SwiftUI
import RealityKit
struct AnimatedModel: View {
var subscription: EventSubscription?
var body: some View {
RealityView { content in
if let moon = try? await Entity(named: "Moon"),
let animation = moon.availableAnimations.first {
moon.playAnimation(animation)
content.add(moon)
}
subscription = content.subscribe(to: AnimationEvents.PlaybackCompleted.self) {
// ...
}
}
}
} Adding a drag gesture
import SwiftUI
import RealityKit
struct DraggableModel: View {
var earth: Entity
var body: some View {
RealityView { content in
content.add(earth)
}
.gesture(DragGesture()
.targetedToEntity(earth)
.onChanged { value in
earth.position = value.convert(value.location3D,
from: .local, to: earth.parent!)
})
}
} Playing a transform animation
// Playing a transform animation
let orbit = OrbitAnimation(name: "Orbit",
duration: 30,
axis: [0, 1, 0],
startTransform: moon.transform,
bindTarget: .transform,
repeatMode: .repeat)
if let animation = try? AnimationResource.generate(with: orbit) {
moon.playAnimation(animation)
} Adding audio
// Create an empty entity to act as an audio source.
let audioSource = Entity()
// Configure the audio source to project sound out in a tight beam.
audioSource.spatialAudio = SpatialAudioComponent(directivity: .beam(focus: 0.75))
// Change the orientation of the audio source (rotate 180º around the Y axis).
audioSource.orientation = .init(angle: .pi, axis: [0, 1, 0])
// Add the audio source to a parent entity, and play a looping sound on it.
if let audio = try? await AudioFileResource(named: "SatelliteLoop",
configuration: .init(shouldLoop: true)) {
satellite.addChild(audioSource)
audioSource.playAudio(audio)
} Defining a custom component
import RealityKit
// Components are data attached to an Entity.
struct TraceComponent: Component {
var mesh = TraceMesh()
}
// Entities contain components, identified by the component’s type.
func updateTrace(for entity: Entity) {
var component = entity.components[TraceComponent.self] ?? TraceComponent()
component.update()
entity.components[TraceComponent.self] = component
}
// Codable components can be added to entities in Reality Composer Pro.
struct PointOfInterestComponent: Component, Codable {
var name = ""
} Defining a system
import SwiftUI
import RealityKit
// Systems supply logic and behavior.
struct TraceSystem: System {
static let query = EntityQuery(where: .has(TraceComponent.self))
init(scene: Scene) {
// ...
}
func update(context: SceneUpdateContext) {
// Systems often act on all entities matching certain conditions.
for entity in context.entities(Self.query, updatingSystemWhen: .rendering) {
addCurrentPositionToTrace(entity)
}
}
}
// Systems run on all RealityKit content in your app once registered.
struct MyApp: App {
init() {
TraceSystem.registerSystem()
}
} Resources
Related sessions
-
21 min -
24 min -
21 min -
14 min -
28 min -
26 min -
24 min -
20 min -
31 min -
32 min -
34 min -
26 min