最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Don't understand iOS RealityKit's collision detection - Stack Overflow

programmeradmin3浏览0评论

I am trying to detect collisions between two RealityKit entities, but the results are unexpected.

I thus created a minimal example. In the iOS app below, 3 entities are set up:
A board, a fixed box and a moving box.
The board and the 1st box don't move, while the 2nd box moves towards the 1st box.
All 3 entities have a collisionShape.
Because Physics simulation is not used, both boxes have their collision mode set to .trigger.
Both boxes belong to the same CollisionGroup, and their collision filter is set to this group.

A CollisionManager subscribes to the begin of collision events. If a collision is detected, the collision is logged.

When the app is started, the 2nd box is moving towards the 1st one until both are at the same position. No collision is logged. I expected that the collision between both boxes would be detected.

If setting of the collision filter of a box is out commented, the following is logged when the app starts (the many RealityKit logs are left out):

collision began between board and Fixed box
collision began between Fixed box and board
collision began between board and Moving box
collision began between Moving box and board

Obviously, collision can be detected, but a collision between both boxes is not. But why?

Here is my code:

//
//  CollisionApp.swift
//  Collision
//
//  Created by Reinhard Männer on 31.03.25.
//

import SwiftUI

@main
struct CollisionApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}  

//
//  CollisionManager.swift
//  Collision
//
//  Created by Reinhard Männer on 31.03.25.
//

import Combine
import Foundation
import RealityKit

class CollisionManager {
    var movementTimer: Timer?
    private var updateVehicleCollisionSubscription: Cancellable?
    
    func startVehicleCollisionTrigger(scene: RealityKit.Scene) {
        updateVehicleCollisionSubscription = scene.subscribe(to: CollisionEvents.Began.self) { event in
            let a = event.entityA
            let b = event.entityB
            print("collision began between \(a.name) and \(b.name)")
        }
    }

    func startMovement(entity: Entity) {
        movementTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
            if entity.position.x > 0 {
                entity.position.x -= 0.02
            } else {
                timer.invalidate()
                self.movementTimer = nil
            }
        }
    }
}

//
//  ContentView.swift
//  Collision
//
//  Created by Reinhard Männer on 31.03.25.
//

import RealityKit
import SwiftUI

struct ContentView: View {
    let collisionManager = CollisionManager()
    let boardHeight: Float = 0.1
    let boxHeight: Float = 0.3
    
    var body: some View {
        var movingBox: Entity?
        
        RealityView { content in
            let board = makeBoard()
            content.add(board)
            
            let box1 = makeBox(name: "Fixed box")
            board.addChild(box1)
            
            movingBox = makeBox(name: "Moving box")
            movingBox?.position.x = 0.8
            board.addChild(movingBox!)
        }
        update: { content in
            let scene = content.entities.first?.scene
            collisionManager.startVehicleCollisionTrigger(scene: scene!)
            collisionManager.startMovement(entity: movingBox!)
        }
    }
    
    func makeBoard() -> ModelEntity {
        let mesh = MeshResource.generateBox(width: 2.0, height: boardHeight, depth: 1.0)
        var material = UnlitMaterial(); material.color.tint = .red
        let boardEntity = ModelEntity(mesh: mesh, materials: [material])
        boardEntity.name = "board"
        boardEntity.generateCollisionShapes(recursive: false)

        boardEntity.transform.translation = [0, 0, -3]
        return boardEntity
    }
    
    func makeBox(name: String) -> ModelEntity {
        let mesh = MeshResource.generateBox(width: 0.2, height: boxHeight, depth: 0.3)
        var material = UnlitMaterial(); material.color.tint = .green
        let boxEntity = ModelEntity(mesh: mesh, materials: [material])
        boxEntity.name = name
        
        // To put the box onto the board, move it up by half height of the board and half height of the box
        let y_up = boardHeight/2.0 + boxHeight/2.0
        boxEntity.position = SIMD3<Float>(0, y_up, 0)
        
        // Allow to detect collisions between boxes only and to trigger code execution.
        boxEntity.generateCollisionShapes(recursive: false)
        boxEntity.collision!.mode = .trigger
        let collisionGroup = CollisionGroup(rawValue: 1 << 0)
        let filter = CollisionFilter(group: collisionGroup, mask: collisionGroup)
        boxEntity.collision!.filter = filter
        
        return boxEntity
    }

}

I am trying to detect collisions between two RealityKit entities, but the results are unexpected.

I thus created a minimal example. In the iOS app below, 3 entities are set up:
A board, a fixed box and a moving box.
The board and the 1st box don't move, while the 2nd box moves towards the 1st box.
All 3 entities have a collisionShape.
Because Physics simulation is not used, both boxes have their collision mode set to .trigger.
Both boxes belong to the same CollisionGroup, and their collision filter is set to this group.

A CollisionManager subscribes to the begin of collision events. If a collision is detected, the collision is logged.

When the app is started, the 2nd box is moving towards the 1st one until both are at the same position. No collision is logged. I expected that the collision between both boxes would be detected.

If setting of the collision filter of a box is out commented, the following is logged when the app starts (the many RealityKit logs are left out):

collision began between board and Fixed box
collision began between Fixed box and board
collision began between board and Moving box
collision began between Moving box and board

Obviously, collision can be detected, but a collision between both boxes is not. But why?

Here is my code:

//
//  CollisionApp.swift
//  Collision
//
//  Created by Reinhard Männer on 31.03.25.
//

import SwiftUI

@main
struct CollisionApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}  

//
//  CollisionManager.swift
//  Collision
//
//  Created by Reinhard Männer on 31.03.25.
//

import Combine
import Foundation
import RealityKit

class CollisionManager {
    var movementTimer: Timer?
    private var updateVehicleCollisionSubscription: Cancellable?
    
    func startVehicleCollisionTrigger(scene: RealityKit.Scene) {
        updateVehicleCollisionSubscription = scene.subscribe(to: CollisionEvents.Began.self) { event in
            let a = event.entityA
            let b = event.entityB
            print("collision began between \(a.name) and \(b.name)")
        }
    }

    func startMovement(entity: Entity) {
        movementTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
            if entity.position.x > 0 {
                entity.position.x -= 0.02
            } else {
                timer.invalidate()
                self.movementTimer = nil
            }
        }
    }
}

//
//  ContentView.swift
//  Collision
//
//  Created by Reinhard Männer on 31.03.25.
//

import RealityKit
import SwiftUI

struct ContentView: View {
    let collisionManager = CollisionManager()
    let boardHeight: Float = 0.1
    let boxHeight: Float = 0.3
    
    var body: some View {
        var movingBox: Entity?
        
        RealityView { content in
            let board = makeBoard()
            content.add(board)
            
            let box1 = makeBox(name: "Fixed box")
            board.addChild(box1)
            
            movingBox = makeBox(name: "Moving box")
            movingBox?.position.x = 0.8
            board.addChild(movingBox!)
        }
        update: { content in
            let scene = content.entities.first?.scene
            collisionManager.startVehicleCollisionTrigger(scene: scene!)
            collisionManager.startMovement(entity: movingBox!)
        }
    }
    
    func makeBoard() -> ModelEntity {
        let mesh = MeshResource.generateBox(width: 2.0, height: boardHeight, depth: 1.0)
        var material = UnlitMaterial(); material.color.tint = .red
        let boardEntity = ModelEntity(mesh: mesh, materials: [material])
        boardEntity.name = "board"
        boardEntity.generateCollisionShapes(recursive: false)

        boardEntity.transform.translation = [0, 0, -3]
        return boardEntity
    }
    
    func makeBox(name: String) -> ModelEntity {
        let mesh = MeshResource.generateBox(width: 0.2, height: boxHeight, depth: 0.3)
        var material = UnlitMaterial(); material.color.tint = .green
        let boxEntity = ModelEntity(mesh: mesh, materials: [material])
        boxEntity.name = name
        
        // To put the box onto the board, move it up by half height of the board and half height of the box
        let y_up = boardHeight/2.0 + boxHeight/2.0
        boxEntity.position = SIMD3<Float>(0, y_up, 0)
        
        // Allow to detect collisions between boxes only and to trigger code execution.
        boxEntity.generateCollisionShapes(recursive: false)
        boxEntity.collision!.mode = .trigger
        let collisionGroup = CollisionGroup(rawValue: 1 << 0)
        let filter = CollisionFilter(group: collisionGroup, mask: collisionGroup)
        boxEntity.collision!.filter = filter
        
        return boxEntity
    }

}
Share Improve this question asked Mar 31 at 11:59 Reinhard MännerReinhard Männer 15.3k5 gold badges62 silver badges137 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Problem solved:

If one out comments boxEntity.collision!.mode = .trigger in func makeBox, the boxes trigger a collision detection.

This is understandable, since the docu for Collision detection says:

Turn an entity into a trigger by adding a CollisionComponent to it and setting its mode to CollisionComponent.Mode.trigger.

RealityKit ignores an entity’s collision component mode if the entity also has a PhysicsBodyComponent. An entity can be a rigid body, or a trigger, but not both at the same time.

And the docu for the trigger mode:

Triggers are not simulated, so if the Entity also contains PhysicsBodyComponent, trigger is ignored.

And finally the docu for the PhysicsBodyComponent:

Model entities have a physics body component by default.

发布评论

评论列表(0)

  1. 暂无评论