-
Work with Reality Composer Pro content in Xcode
Learn how to bring content from Reality Composer Pro to life in Xcode. We'll show you how to load 3D scenes into Xcode, integrate your content with your code, and add interactivity to your app. We'll also share best practices and tips for using these tools together in your development workflow.
To get the most out of this session, we recommend first watching “Meet Reality Composer Pro” and “Explore materials in Reality Composer Pro" to learn more about creating 3D scenes.Capítulos
- 0:00 - Introduction
- 2:37 - Load 3D content
- 6:27 - Components
- 12:00 - User Interface
- 27:51 - Play audio
- 30:18 - Material properties
- 33:25 - Wrap-up
Recursos
Vídeos relacionados
WWDC23
- Build spatial experiences with RealityKit
- Develop your first immersive app
- Enhance your spatial computing app with RealityKit
- Explore materials in Reality Composer Pro
- Meet Reality Composer Pro
WWDC21
-
Buscar neste vídeo...
-
-
3:12 - Loading an entity
RealityView { content in do { let entity = try await Entity(named: "DioramaAssembled", in: realityKitContentBundle) content.add(entity) } catch { // Handle error } } -
6:39 - Adding a component
let component = MyComponent() entity.components.set(component) -
12:21 - Attachments data flow
RealityView { _, _ in // load entities from your Reality Composer Pro package bundle } update: { content, attachments in if let attachmentEntity = attachments.entity(for: "🐠") { content.add(attachmentEntity) } } attachments: { Button { ... } .background(.green) .tag("🐠") } -
15:48 - Adding attachments
let myEntity = Entity() RealityView { content, _ in if let entity = try? await Entity(named: "MyScene", in: realityKitContentBundle) { content.add(entity) } } update: { content, attachments in if let attachmentEntity = attachments.entity(for: "🐠") { content.add(attachmentEntity) } content.add(myEntity) } attachments: { Button { ... } .background(.green) .tag("🐠") } -
20:43 - Adding point of interest attachment entities
static let markersQuery = EntityQuery(where: .has(PointOfInterestComponent.self)) @State var attachmentsProvider = AttachmentsProvider() rootEntity.scene?.performQuery(Self.markersQuery).forEach { entity in guard let pointOfInterest = entity.components[PointOfInterestComponent.self] else { return } let attachmentTag: ObjectIdentifier = entity.id let view = LearnMoreView(name: pointOfInterest.name, description: pointOfInterest.description) .tag(attachmentTag) attachmentsProvider.attachments[attachmentTag] = AnyView(view) } -
21:40 - AttachmentsProvider
@Observable final class AttachmentsProvider { var attachments: [ObjectIdentifier: AnyView] = [:] var sortedTagViewPairs: [(tag: ObjectIdentifier, view: AnyView)] { ... } } ... @State var attachmentsProvider = AttachmentsProvider() RealityView { _, _ in } update: { _, _ in } attachments: { ForEach(attachmentsProvider.sortedTagViewPairs, id: \.tag) { pair in pair.view } } -
22:31 - Design-time and Run-time components
// Design-time component public struct PointOfInterestComponent: Component, Codable { public var region: Region = .yosemite public var name: String = "Ribbon Beach" public var description: String? } // Run-time component public struct PointOfInterestRuntimeComponent: Component { public let attachmentTag: ObjectIdentifier } -
25:38 - Adding a run-time component for each design-time component
static let markersQuery = EntityQuery(where: .has(PointOfInterestComponent.self)) @State var attachmentsProvider = AttachmentsProvider() rootEntity.scene?.performQuery(Self.markersQuery).forEach { entity in guard let pointOfInterest = entity.components[PointOfInterestComponent.self] else { return } let attachmentTag: ObjectIdentifier = entity.id let view = LearnMoreView(name: pointOfInterest.name, description: pointOfInterest.description) .tag(attachmentTag) attachmentsProvider.attachments[attachmentTag] = AnyView(view) let runtimeComponent = PointOfInterestRuntimeComponent(attachmentTag: attachmentTag) entity.components.set(runtimeComponent) } -
26:19 - Adding and positioning the attachment entities
static let runtimeQuery = EntityQuery(where: .has(PointOfInterestRuntimeComponent.self)) RealityView { _, _ in } update: { content, attachments in x rootEntity.scene?.performQuery(Self.runtimeQuery).forEach { entity in guard let component = entity.components[PointOfInterestRuntimeComponent.self], let attachmentEntity = attachments.entity(for: component.attachmentTag) else { return } content.add(attachmentEntity) attachmentEntity.setPosition([0, 0.5, 0], relativeTo: entity) } } attachments: { ForEach(attachmentsProvider.sortedTagViewPairs, id: \.tag) { pair in pair.view } } -
28:55 - Audio Playback
func playOceanSound() { guard let entity = entity.findEntity(named: "OceanEmitter"), let resource = try? AudioFileResource(named: "/Root/Resources/Ocean_Sounds_wav", from: "DioramaAssembled.usda", in: RealityContent.realityContentBundle) else { return } let audioPlaybackController = entity.prepareAudio(resource) audioPlaybackController.play() } -
31:02 - Terrain material transition using the slider
@State private var sliderValue: Float = 0.0 Slider(value: $sliderValue, in: (0.0)...(1.0)) .onChange(of: sliderValue) { _, _ in guard let terrain = rootEntity.findEntity(named: "DioramaTerrain"), var modelComponent = terrain.components[ModelComponent.self], var shaderGraphMaterial = modelComponent.materials.first as? ShaderGraphMaterial else { return } do { try shaderGraphMaterial.setParameter(name: "Progress", value: .float(sliderValue)) modelComponent.materials = [shaderGraphMaterial] terrain.components.set(modelComponent) } catch { } } } -
31:57 - Audio transition using the slider
@State private var sliderValue: Float = 0.0 static let audioQuery = EntityQuery(where: .has(RegionSpecificComponent.self) && .has(AmbientAudioComponent.self)) Slider(value: $sliderValue, in: (0.0)...(1.0)) .onChange(of: sliderValue) { _, _ in // ... Change the terrain material property ... rootEntity?.scene?.performQuery(Self.audioQuery).forEach({ audioEmitter in guard var audioComponent = audioEmitter.components[AmbientAudioComponent.self], let regionComponent = audioEmitter.components[RegionSpecificComponent.self] else { return } let gain = regionComponent.region.gain(forSliderValue: sliderValue) audioComponent.gain = gain audioEmitter.components.set(audioComponent) }) } }
-