You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 10:22:22