细心的读者会发现在在Day1和Day2的示例中我们使用的都是WindowGroup
。
1 2 3 4 5 6 7 8 |
@main struct visionOSDemoApp: App { var body: some Scene { WindowGroup { ContentView() } } } |
WindowGroup
本身有很多的修饰符配置项,如通过defaultPosition
可指定窗口的初始位置,可传入的值有.bottomTrailing
、.bottomLeading
等。defaultSize
可用于配置初始窗口大小,通常使用width
和height
参数来指定,但在设置了windowStyle(.volumetric)
时,还可通过depth
来对volume
指定三维大小,通过使用in
参数可指定大小的单位。
本节我们来认识在visionOS开发中会经常用到的另一个概念ImmersiveSpace
。
沉浸式空间为内容提供了一个无界的区域,可在空间内控制内容的大小和摆放位置。在获取用户的授权后,我们还可以使用开启了沉浸空间的ARKit来将内容集成到周遭环境中。例如,可以使用ARKit场景重建来获取家具的网格(mesh)及其附近的对象,让内容可以与网格进行交互。
首先我们需要创建一个ViewModel.swift
文件用于进行内容的相关配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import SwiftUI import RealityKit import ARKit @MainActor class ViewModel: ObservableObject { private let session = ARKitSession() private let worldTracking = WorldTrackingProvider() private var contentEntity = Entity() func setupContentEntity() -> Entity { let box = ModelEntity(mesh: .generateBox(width: 0.5, height: 0.5, depth: 0.5)) contentEntity.addChild(box) return contentEntity } func runSession() async { print("WorldTrackingProvider.isSupported: \(WorldTrackingProvider.isSupported)") print("PlaneDetectionProvider.isSupported: \(PlaneDetectionProvider.isSupported)") print("SceneReconstructionProvider.isSupported: \(SceneReconstructionProvider.isSupported)") print("HandTrackingProvider.isSupported: \(HandTrackingProvider.isSupported)") Task { let authorizationResult = await session.requestAuthorization(for: [.worldSensing]) for (authorizationType, authorizationStatus) in authorizationResult { print("Authorization status for \(authorizationType): \(authorizationStatus)") switch authorizationStatus { case .allowed: break case .denied: // TODO break case .notDetermined: break @unknown default: break } } } Task { try await session.run([worldTracking]) for await update in worldTracking.anchorUpdates { switch update.event { case .added, .updated: print("Anchor position updated.") case .removed: print("Anchor position now unknown.") } } } } } |
在setupContentEntity
方法中,我们通过ModelEntity
创建了一个模型实体,其中对mesh
参数使用MeshResource.generateBox
创建了一个立方体,可使用参数的说明如下:
- mesh: 定义模型几何形状的网格。
- materials: 定义模型外观的材质资源。
- collisionShape: 定义合成碰撞开关的形状资源集合。
- mass: 按公斤计的模型质量。
另一个异步方法runSession
用于进行配置和授权的处理,其中包含两个Task
。
通常我们会创建一个ImmersiveView
来显示沉浸空间的效果,但本例我们都放到了入口文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import SwiftUI import RealityKit @main struct visionOSDemoApp: App { @StateObject var model = ViewModel() var body: some SwiftUI.Scene { ImmersiveSpace { RealityView { content in content.add(model.setupContentEntity()) } .task{ await model.runSession() } } } } |
注意因为这里导入了RealityKit
,所以为避免歧义我们使用了SwiftUI.Scene
,然后在主体内容中是一个RealityView
,其中添加了我们在ViewModel
中所创建的立方体,同时使用异步任务去执行授权部分的runSession()
方法。
代码部分就是这么多,但在运行应用前我们还要配置一下Info.plist
文件,我们需要将Preferred Default Scene Session Role
选项修改为Immersive Space Application Session Role
:
这时运行应用就会看到本文前面显示的效果,最后我们再来了解一个调试的工具,在代码区下方点击图标即可打开Visualizations弹窗,通过显示检测到表面、遮挡和锚点等来辅助我们的开始,我们的示例图片便是勾选了Surfaces
之后的效果。
示例代码:GitHub仓库
其它相关内容请见虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记