如何用SwiftUI和RealityKit实现设备中心持续射线检测近距3D物体?
使用SwiftUI + RealityKit实现设备中心实时射线检测最近3D物体
你用的trackedRaycast是ARKit用于检测真实世界平面/特征点的API,没法直接命中RealityKit场景里的3D实体——这是核心问题。要实现检测场景中自定义3D物体的需求,得用RealityKit原生的射线查询接口,以下是完整实现步骤:
核心思路
- 给需要被检测的3D实体添加碰撞组件(CollisionComponent),确保能被射线命中
- 从设备屏幕中心生成RealityKit兼容的射线(将屏幕坐标转换为场景中的射线起点与方向)
- 每帧执行射线检测,筛选出距离最近的命中实体
完整代码实现
import SwiftUI import RealityKit struct ARViewContainer: UIViewRepresentable { @Binding var nearestEntityName: String? func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) // 添加测试用3D实体(立方体+球体) let cube = ModelEntity(mesh: .generateBox(size: 0.2), materials: [SimpleMaterial(color: .red, isMetallic: false)]) cube.position = [0.3, 0, -1] // 给实体添加碰撞组件,设置碰撞掩码 cube.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateBox(size: 0.2)], mode: .default) cube.name = "RedCube" let sphere = ModelEntity(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial(color: .blue, isMetallic: false)]) sphere.position = [-0.3, 0, -0.8] sphere.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateSphere(radius: 0.1)], mode: .default) sphere.name = "BlueSphere" let anchor = AnchorEntity(world: [0,0,0]) anchor.addChild(cube) anchor.addChild(sphere) arView.scene.addAnchor(anchor) // 监听场景更新事件,每帧执行射线检测 arView.scene.subscribe(to: SceneEvents.Update.self) { _ in detectNearestEntity(from: arView) } return arView } func updateUIView(_ uiView: ARView, context: Context) {} private func detectNearestEntity(from arView: ARView) { // 获取设备屏幕中心坐标 let screenCenter = CGPoint(x: arView.bounds.midX, y: arView.bounds.midY) // 将屏幕坐标转换为RealityKit场景中的射线 guard let raycastQuery = arView.makeRaycastQuery(from: screenCenter, allowing: .estimatedPlane, alignment: .any) else { return } // 执行RealityKit射线检测,查询所有可碰撞实体 let results = arView.scene.raycast(from: raycastQuery.origin, to: raycastQuery.origin + raycastQuery.direction * 10, query: .all, mask: .default, relativeTo: nil) // 筛选出距离最近的实体 if let nearestResult = results.first { nearestEntityName = nearestResult.entity.name } else { nearestEntityName = nil } } } struct ContentView: View { @State private var nearestEntityName: String? var body: some View { ZStack { ARViewContainer(nearestEntityName: $nearestEntityName) // 显示最近命中的实体名称 VStack { Spacer() Text(nearestEntityName ?? "未检测到物体") .padding() .background(Color.black.opacity(0.7)) .foregroundColor(.white) .cornerRadius(8) } .padding() } } } #Preview { ContentView() }
关键细节说明
- 碰撞组件:必须给每个需要被检测的实体添加
CollisionComponent,否则射线会直接穿过实体,无法命中 - 射线转换:
makeRaycastQuery方法将屏幕坐标转换为ARKit空间中的射线,我们复用其起点和方向,传入RealityKit的raycast方法 - 结果筛选:RealityKit的射线检测结果默认按距离从近到远排序,取第一个就是最近的实体
- 实时检测:通过
SceneEvents.Update订阅每帧更新,确保射线检测实时执行
内容的提问来源于stack exchange,提问作者Alessandro Bemporad
相关产品推荐
相关产品推荐

