在之前的学习中我们在空间中添加了3D模型,但在初始摆放后就无法再对其进行移动或做出修改。本节我们在Day 5显示和隐藏的基础上让我们模型可以实现拖拽效果,同时对纯色的立方体实现点击随机换色的功能。
首先是入口文件,无需做出改变,
import SwiftUI@main
struct visionOSDemoApp: App {var body: some Scene {WindowGroup() {ContentView()}ImmersiveSpace(id: "ImmersiveSpace") {ImmersiveView()}}
}
接着是ViewModel.swift
文件,这里是核心逻辑:
import SwiftUI
import RealityKitclass ViewModel: ObservableObject {private var contentEntity = Entity()private let colors: [SimpleMaterial.Color] = [.gray, .red, .orange, .yellow, .green, .blue, .purple, .systemPink]func setupContentEntity() -> Entity {return contentEntity}func addCube() -> Entity {let entity = ModelEntity(mesh: .generateBox(size: 0.5, cornerRadius: 0),materials: [SimpleMaterial(color: .red, isMetallic: false)],collisionShape: .generateBox(size: SIMD3<Float>(repeating: 0.5)),mass: 0.0)entity.components.set(InputTargetComponent(allowedInputTypes: .indirect))entity.position = SIMD3(x: 0, y: 1, z: -2)contentEntity.addChild(entity)return entity}func changeToRandomColor(entity: Entity) {guard let _entity = entity as? ModelEntity else { return }_entity.model?.materials = [SimpleMaterial(color: colors.randomElement()!, isMetallic: false)]}
}
这里增加了一个colors
常量,用于设置一个包含多种颜色数组,以便进行随机颜色修改。颜色是通过SimpleMaterial
的color
参数进行随机更换(randomElement
),然后赋值给.materials
属性,这部分逻辑位于changeToRandomColor
中。
在addCube
的逻辑中,我们还是常规地生成一个entity
并返回。
接着来到ImmersiveView
:
import SwiftUI
import RealityKitstruct ImmersiveView: View {@State var model = ViewModel()@State var cube = Entity()var body: some View {RealityView { content incontent.add(model.setupContentEntity())cube = model.addCube()}.gesture(DragGesture().targetedToEntity(cube).onChanged { value incube.position = value.convert(value.location3D, from: .local, to: cube.parent!)}).gesture(SpatialTapGesture().targetedToEntity(cube).onEnded { value inmodel.changeToRandomColor(entity: cube)})}
}
在这个视图中我们声明了cube
变量,以便后续的拖拽和随机颜色修改操作。所以在初始化视图时将添加的立方体赋值给cube
。然后分别通过DragGesture
和SpatialTapGesture
来实现拖拽和点击的逻辑。
这里需要说明一个本例中生成的立方体在完成拖放,隐藏后再次展开后会生成一个新的供拖放和修改颜色的模型,这时老的模型就无法再进行拖放了。如果读者只希望操作同一个模型,可以对addCube
添加了一个name
参数,并添加一个getTargetEntity
方法来获取该对象,当然也可以通过预设名称来切换不同的模型进行操作,示例修改如下:
func getTargeEntity(name: String) -> Entity? {return contentEntity.children.first { $0.name == name }}func addCube(name: String) -> Entity {if let entity = getTargeEntity(name: name) {return entity}
...entity.name = name
...
对应的ImmersiveView
也要修改为类似cube = model.addCube("Cube1")
。
ContentView.swift
的代码与Day 5一致:
import SwiftUI
import RealityKitstruct ContentView: View {@State var showImmsersiveSpace = false@Environment(\.openImmersiveSpace) var openImmersiveSpace@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpacevar body: some View {NavigationStack {VStack {Toggle("Show ImmersiveSpace", isOn: $showImmsersiveSpace).toggleStyle(.button)}.padding()}.onChange(of: showImmsersiveSpace) { _, newValue inTask {if newValue {await openImmersiveSpace(id: "ImmersiveSpace")} else {await dismissImmersiveSpace()}}}}
}
运行应用点击盒子会随机改变颜色,拖拽盒子会跟随鼠标的位置移动。
示例代码:GitHub仓库https://github.com/alanhou/ARDemo/tree/main/visionOS/Day6
其它相关内容请见虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记https://alanhou.org/augmented-reality/