Collision detection between two entities not working

Hi,

I've tried to implement collision detection between the left index finger through a sphere and a simple 3D rectangle box. The sphere of my left index finger goes through the object, but no collision seems to take place. What am I missing?

Thank you very much for your consideration!

Below is my code;

App.swift

import SwiftUI

@main
private struct TrackingApp: App {
    
    public init() {
        ...
    }
    
    public var body: some Scene {
        WindowGroup {
            ContentView()
        }
        
        ImmersiveSpace(id: "AppSpace") { 
            ImmersiveView()
        }
    }
}

ImmersiveView.swift

import SwiftUI
import RealityKit

struct ImmersiveView: View {
    @State private var subscriptions: [EventSubscription] = []
    
    public var body: some View {
        RealityView { content in
            
            /* LEFT HAND */
            let leftHandIndexFingerEntity = AnchorEntity(.hand(.left, location: .
            let leftHandIndexFingerSphere = ModelEntity(mesh: .generateSphere(radius: 0.01), materials: [SimpleMaterial(color: .orange, isMetallic: false)])
            
            leftHandIndexFingerEntity.addChild(leftHandIndexFingerSphere)
            leftHandIndexFingerEntity.generateCollisionShapes(recursive: true)
            leftHandIndexFingerEntity.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateSphere(radius: 0.01)])
            leftHandIndexFingerEntity.name = "LeftHandIndexFinger"
            content.add(leftHandIndexFingerEntity)
            
            /* 3D RECTANGLE*/
            let width: Float = 0.7 
            let height: Float = 0.35
            let depth: Float = 0.005 

            let rectangleEntity = ModelEntity(mesh: .generateBox(size: [width, height, depth]), materials: [SimpleMaterial(color: .red.withAlphaComponent(0.5), isMetallic: false)])
            

           rectangleEntity.transform.rotation = simd_quatf(angle: -.pi / 2, axis: [1, 0, 0])
            
            let rectangleAnchor = AnchorEntity(world: [0.1, 0.85, -0.5]) 
            rectangleEntity.generateCollisionShapes(recursive: true) 
            rectangleEntity.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateBox(size: [width, height, depth])])
            rectangleEntity.name = "Rectangle" 

            rectangleAnchor.addChild(rectangleEntity)
            content.add(rectangleAnchor)
         
            /* Collision Handling */
            
            let subscription = content.subscribe(to: CollisionEvents.Began.self, on: rectangleEntity) { collisionEvent in
                           print("Collision detected between \(collisionEvent.entityA.name) and \(collisionEvent.entityB.name)")
                        }
            subscriptions.append(subscription)
            
        }
        
    }
}
Answered by Vision Pro Engineer in 802586022

Hi @XWDev

There are some issues I noticed with the code you posted, but we're here to help!

First, it appears that you are using an anchor attached to your left index finger to control the position of the orange ball, but you haven't started a SpatialTrackingSession yet. You will also need to add the NSHandsTrackingUsageDescription key to your Info.plist to declare that your app requires such information. Information such as the position of the player in the world, which is necessary to detect these collisions, will be unavailable until a session is started.

Additionally, be careful when working with anchors and physics. By default, AnchorEntity and its children exist in a separate physics simulation, and will not interact with entities outside the anchor hierarchy. This can be fixed by setting the physicsSimulation property on the AnchoringComponent to .none.

I also noticed you made the red rectangle a child of an AnchorEntity. This isn't necessary, you can just place a ModelEntity and set its world position once and it will stay put.

Put that all together and here's my modified version of your ImmersiveView with working collision callbacks:

import SwiftUI
import RealityKit

struct ImmersiveView: View {
    @State private var subscriptions: [EventSubscription] = []
    
    public var body: some View {
        RealityView { content in
            // minimum code necessary to start spatial tracking session for hand
            let configuration = SpatialTrackingSession.Configuration(tracking: [.hand])
            let spatialTrackingSession = SpatialTrackingSession.init()
            _ = await spatialTrackingSession.run(configuration)
            
            /* LEFT HAND */
            // just create the model entity and manually attach an anchoring component
//            let leftHandIndexFingerEntity = AnchorEntity(.hand(.left, location: .indexFingerTip))
            let leftHandIndexFingerSphere = ModelEntity(mesh: .generateSphere(radius: 0.01), materials: [SimpleMaterial(color: .orange, isMetallic: false)])
            
            leftHandIndexFingerSphere.generateCollisionShapes(recursive: true)
            // this is unnecessary after calling .generateCollisionShapes()
//            leftHandIndexFingerSphere.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateSphere(radius: 0.01)])
            leftHandIndexFingerSphere.name = "LeftHandIndexFinger"
            content.add(leftHandIndexFingerSphere)
            
            // just set the anchoring component on the sphere itself
            var leftHandAnchor = AnchoringComponent(.hand(.left, location: .indexFingerTip))
            // allow the entity to participate in the root physicsSimulation
            leftHandAnchor.physicsSimulation = .none
            leftHandIndexFingerSphere.components.set(leftHandAnchor)
            
            /* 3D RECTANGLE*/
            let width: Float = 0.7
            let height: Float = 0.35
            let depth: Float = 0.005

            let rectangleEntity = ModelEntity(mesh: .generateBox(size: [width, height, depth]), materials: [SimpleMaterial(color: .red.withAlphaComponent(0.5), isMetallic: false)])

            rectangleEntity.transform.rotation = simd_quatf(angle: -.pi / 2, axis: [1, 0, 0])
            
            // just set the position, no need to use anchors
            rectangleEntity.setPosition([0.1, 0.85, -0.5], relativeTo: nil)
            rectangleEntity.generateCollisionShapes(recursive: true)
//            rectangleEntity.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateBox(size: [width, height, depth])])
            rectangleEntity.name = "Rectangle"
//            rectangleAnchor.addChild(rectangleEntity)
            content.add(rectangleEntity)
         
            /* Collision Handling */
            
            let subscription = content.subscribe(to: CollisionEvents.Began.self, on: rectangleEntity) { collisionEvent in
                           print("Collision detected between \(collisionEvent.entityA.name) and \(collisionEvent.entityB.name)")
                        }
            subscriptions.append(subscription)
        }
    }
}

Let me know if that helps!

Accepted Answer

Hi @XWDev

There are some issues I noticed with the code you posted, but we're here to help!

First, it appears that you are using an anchor attached to your left index finger to control the position of the orange ball, but you haven't started a SpatialTrackingSession yet. You will also need to add the NSHandsTrackingUsageDescription key to your Info.plist to declare that your app requires such information. Information such as the position of the player in the world, which is necessary to detect these collisions, will be unavailable until a session is started.

Additionally, be careful when working with anchors and physics. By default, AnchorEntity and its children exist in a separate physics simulation, and will not interact with entities outside the anchor hierarchy. This can be fixed by setting the physicsSimulation property on the AnchoringComponent to .none.

I also noticed you made the red rectangle a child of an AnchorEntity. This isn't necessary, you can just place a ModelEntity and set its world position once and it will stay put.

Put that all together and here's my modified version of your ImmersiveView with working collision callbacks:

import SwiftUI
import RealityKit

struct ImmersiveView: View {
    @State private var subscriptions: [EventSubscription] = []
    
    public var body: some View {
        RealityView { content in
            // minimum code necessary to start spatial tracking session for hand
            let configuration = SpatialTrackingSession.Configuration(tracking: [.hand])
            let spatialTrackingSession = SpatialTrackingSession.init()
            _ = await spatialTrackingSession.run(configuration)
            
            /* LEFT HAND */
            // just create the model entity and manually attach an anchoring component
//            let leftHandIndexFingerEntity = AnchorEntity(.hand(.left, location: .indexFingerTip))
            let leftHandIndexFingerSphere = ModelEntity(mesh: .generateSphere(radius: 0.01), materials: [SimpleMaterial(color: .orange, isMetallic: false)])
            
            leftHandIndexFingerSphere.generateCollisionShapes(recursive: true)
            // this is unnecessary after calling .generateCollisionShapes()
//            leftHandIndexFingerSphere.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateSphere(radius: 0.01)])
            leftHandIndexFingerSphere.name = "LeftHandIndexFinger"
            content.add(leftHandIndexFingerSphere)
            
            // just set the anchoring component on the sphere itself
            var leftHandAnchor = AnchoringComponent(.hand(.left, location: .indexFingerTip))
            // allow the entity to participate in the root physicsSimulation
            leftHandAnchor.physicsSimulation = .none
            leftHandIndexFingerSphere.components.set(leftHandAnchor)
            
            /* 3D RECTANGLE*/
            let width: Float = 0.7
            let height: Float = 0.35
            let depth: Float = 0.005

            let rectangleEntity = ModelEntity(mesh: .generateBox(size: [width, height, depth]), materials: [SimpleMaterial(color: .red.withAlphaComponent(0.5), isMetallic: false)])

            rectangleEntity.transform.rotation = simd_quatf(angle: -.pi / 2, axis: [1, 0, 0])
            
            // just set the position, no need to use anchors
            rectangleEntity.setPosition([0.1, 0.85, -0.5], relativeTo: nil)
            rectangleEntity.generateCollisionShapes(recursive: true)
//            rectangleEntity.components[CollisionComponent.self] = CollisionComponent(shapes: [.generateBox(size: [width, height, depth])])
            rectangleEntity.name = "Rectangle"
//            rectangleAnchor.addChild(rectangleEntity)
            content.add(rectangleEntity)
         
            /* Collision Handling */
            
            let subscription = content.subscribe(to: CollisionEvents.Began.self, on: rectangleEntity) { collisionEvent in
                           print("Collision detected between \(collisionEvent.entityA.name) and \(collisionEvent.entityB.name)")
                        }
            subscriptions.append(subscription)
        }
    }
}

Let me know if that helps!

Collision detection between two entities not working
 
 
Q