visionOS空间计算实战开发教程Day 6 拖拽和点击

在之前的学习中我们在空间中添加了3D模型,但在初始摆放后就无法再对其进行移动或做出修改。本节我们在​​Day 5​​显示和隐藏的基础上让我们模型可以实现拖拽效果,同时对纯色的立方体实现点击随机换色的功能。

首先是入口文件,无需做出改变,

复制代码
import SwiftUI

@main
struct visionOSDemoApp: App {
    var body: some Scene {
        WindowGroup() {
            ContentView()
        }
        ImmersiveSpace(id: "ImmersiveSpace") {
            ImmersiveView()
        }
    }
}

接着是​​ViewModel.swift​​文件,这里是核心逻辑:

复制代码
import SwiftUI
import RealityKit

class 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 RealityKit

struct ImmersiveView: View {
    @State var model = ViewModel()
    @State var cube = Entity()
        
    var body: some View {
        RealityView { content in
            content.add(model.setupContentEntity())
            cube = model.addCube()
        }
        .gesture(
            DragGesture()
                .targetedToEntity(cube)
                .onChanged { value in
                    cube.position = value.convert(value.location3D, from: .local, to: cube.parent!)
                }
        )
        .gesture(
            SpatialTapGesture()
                .targetedToEntity(cube)
                .onEnded { value in
                    model.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 RealityKit

struct ContentView: View {
    @State var showImmsersiveSpace = false
    
    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    
    var body: some View {
        NavigationStack {
            VStack {
                Toggle("Show ImmersiveSpace", isOn: $showImmsersiveSpace)
                    .toggleStyle(.button)
            }
            .padding()
        }
        .onChange(of: showImmsersiveSpace) { _, newValue in
            Task {
                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/

相关推荐
人类群星闪耀时2 天前
5G + AR:让增强现实真正“实时交互”起来
5g·ar·交互
球球和皮皮2 天前
Babylon.js学习之路《一、初识 Babylon.js:什么是 3D 开发与 WebGL 的完美结合?》
javascript·3d·前端框架·ar·vr
Blossom.1183 天前
虚拟现实(VR)与增强现实(AR)在教育领域的应用:开启沉浸式学习新时代
人工智能·深度学习·学习·机器学习·ar·制造·vr
jz_ddk6 天前
[学习]RTKLib详解:ppp.c与ppp_ar.c
c语言·学习·ar
LitchiCheng6 天前
复刻低成本机械臂 SO-ARM100 上位机控制调试
人工智能·机器人·ar
EQ-雪梨蛋花汤9 天前
【方案分享】基于Three.js和Stencil Buffer的AR实物遮挡方案,支持不规则动态区域(AR地下设施、AR虚实遮挡)
javascript·ar·restful
R²AIN SUITE11 天前
空间计算:开启人机交互新纪元的下一代技术范式
人工智能·ai·空间计算
EQ-雪梨蛋花汤11 天前
【Unity AR开发插件】一、高效热更新:Unity AR 插件结合 HybridCLR 与 ARFoundation 的开源仓库分享
unity·开源·ar
斯裕科技13 天前
从高端制造到民生场景:天元轻量化软件的“破局”之路
ar·xr·制造·vr·虚拟现实·增强现实
广州华锐视点15 天前
【广州华锐视点】AR 远程协同:突破时空限制的利器
ar