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

RealityKit/ARKit:两物体专属碰撞设置及启动碰撞异常问题

问题1:启动后圆柱体持续触发碰撞的修复

原因分析

  1. 重复创建实体与订阅:每次点击屏幕时,无论场景是否已放置,都会重复加载圆柱体模型、配置碰撞组件并订阅碰撞事件——这些未被添加到场景的隐形圆柱体会互相碰撞,或与AR检测到的平面触发碰撞。
  2. 无碰撞源过滤:碰撞事件订阅未限制触发实体,任何物体(包括AR平面)与圆柱体碰撞都会触发变色逻辑。

修复代码

修改handleTap方法,将实体创建逻辑仅放在场景首次放置的分支内,并在碰撞事件中过滤触发源为球体:

@objc func handleTap(recognizer: UITapGestureRecognizer) {
    guard let view = self.view, let focusEntity = self.focusEntity else { return }
    let tapLocation = recognizer.location(in: self.view)

    // 仅在场景未放置时执行实体创建逻辑
    if !sceneIsPlaced {
        let anchor = AnchorEntity()
        let importModel = try! Entity.load(named: "cilinder")
        importModel.position = focusEntity.position
        importModel.scale = SIMD3(repeating: 0.5)

        // 配置圆柱体
        let kolomMiddleModel = importModel.findEntity(named: "cilinder")!.children[0] as! (ModelEntity & HasPhysicsBody & HasCollision)
        let materialKolomMiddle = SimpleMaterial(color: .yellow, isMetallic: true)
        kolomMiddleModel.model?.materials = [materialKolomMiddle]
        kolomMiddleModel.generateCollisionShapes(recursive: false)
        kolomMiddleModel.components.set(PhysicsBodyComponent(massProperties: .default, material: .default, mode: .dynamic))

        // 配置球体
        let materialsBall = SimpleMaterial(color: .red, isMetallic: true)
        let ballModel = ModelEntity(mesh: .generateSphere(radius: 0.1), materials: [materialsBall]) as (Entity & HasPhysicsBody & HasCollision)
        ballModel.position = [-0.1, -1.0,-0.1]
        ballModel.generateCollisionShapes(recursive: true)
        ballModel.name = "ballModel"

        // 配置灯光
        let directionalLight = DirectionalLight()
        directionalLight.light.color = .white
        directionalLight.light.intensity = 4000
        directionalLight.light.isRealWorldProxy = true
        directionalLight.shadow?.maximumDistance = 1.5
        directionalLight.shadow?.depthBias = 7.0
        directionalLight.orientation = simd_quatf(angle: .pi/1.5, axis: [0,1,0])
        let lightAnchor = AnchorEntity(world: [0, 0, 2.5])
        lightAnchor.addChild(directionalLight)

        // 订阅碰撞事件,仅响应球体触发的碰撞
        view.scene.subscribe(to: CollisionEvents.Began.self, on: kolomMiddleModel) { event in
            if event.entityB.name == "ballModel" {
                print("Collision with ball detected!")
                let material = SimpleMaterial(color: .red, isMetallic: true)
                kolomMiddleModel.model?.materials = [material]
            }
        }.store(in: &self.subscriptions)

        // 添加所有实体到场景
        anchor.addChild(importModel)
        anchor.addChild(ballModel)
        anchor.addChild(lightAnchor)
        view.scene.addAnchor(anchor)
        sceneIsPlaced = true
    } else {
        // 场景已放置时,仅处理球体移动逻辑
        if let locationEntity = view.entity(at: tapLocation) as? ModelEntity, locationEntity.name == "ballModel" {
            locationEntity.physicsBody = .init()
            locationEntity.physicsBody?.mode = .kinematic
            locationEntity.physicsMotion = PhysicsMotionComponent(linearVelocity: [0, 0, -0.5], angularVelocity: [1, 3, 5])
        }
    }
}

额外优化:可以通过碰撞过滤排除AR平面的碰撞,避免圆柱体与地面平面触发碰撞事件。


问题2:设置碰撞过滤实现指定物体碰撞

实现思路

使用RealityKit的CollisionFilter,通过**碰撞类别(category)和碰撞掩码(mask)**定义哪些实体之间可以触发碰撞:

  1. 为球体、目标圆柱体、其他圆柱体分配不同的碰撞类别位值。
  2. 配置实体的CollisionComponent,仅让球体与目标圆柱体的掩码互相包含。

修复代码

  1. 定义碰撞类别常量:
// 在Coordinator类内定义
private let ballCategory: CollisionCategory = .init(rawValue: 1 << 0)
private let targetCylinderCategory: CollisionCategory = .init(rawValue: 1 << 1)
private let otherCylinderCategory: CollisionCategory = .init(rawValue: 1 << 2)
  1. 配置球体的碰撞过滤:
// 创建球体时添加
ballModel.collision = CollisionComponent(
    shapes: [.generateSphere(radius: 0.1)],
    filter: CollisionFilter(
        category: ballCategory,
        mask: targetCylinderCategory  // 仅与目标圆柱体碰撞
    )
)
  1. 配置目标圆柱体的碰撞过滤:
// 配置目标圆柱体时添加
kolomMiddleModel.collision = CollisionComponent(
    shapes: kolomMiddleModel.generateCollisionShapes(recursive: false),
    filter: CollisionFilter(
        category: targetCylinderCategory,
        mask: ballCategory  // 仅与球体碰撞
    )
)
  1. 后续添加其他圆柱体时,配置为不与球体碰撞:
// 示例:创建其他圆柱体
let otherCylinder = ModelEntity(mesh: .generateCylinder(height: 0.5, radius: 0.1), materials: [SimpleMaterial(color: .blue, isMetallic: true)]) as (ModelEntity & HasPhysicsBody & HasCollision)
otherCylinder.generateCollisionShapes(recursive: false)
otherCylinder.components.set(PhysicsBodyComponent(massProperties: .default, material: .default, mode: .dynamic))
otherCylinder.collision = CollisionComponent(
    shapes: otherCylinder.generateCollisionShapes(recursive: false),
    filter: CollisionFilter(
        category: otherCylinderCategory,
        mask: .all & ~ballCategory  // 不与球体碰撞,可与其他物体碰撞
    )
)
// 添加到场景
anchor.addChild(otherCylinder)

这样设置后,只有标记为targetCylinderCategory的圆柱体才会与球体触发碰撞事件,其他圆柱体不会响应球体的碰撞。


内容的提问来源于stack exchange,提问作者saro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:10:20