Search code examples
swiftuirealitykitvisionos

How to make an entity move in a RealityView so that collisions can be detected


I'm trying to detect when two entities collide. The following code shows a very basic set-up. How do I get the upperSphere to move? If I set its physicsBody.mode to .dynamic it moves due to gravity (and the collision is reported), but when in kinematic mode it doesn't respond to the impulse:

struct CollisionView: View {

    @State var subscriptions: [EventSubscription] = []
    
    var body: some View {
        RealityView { content in
            let upperSphere = ModelEntity(mesh: .generateSphere(radius: 0.04))
            let lowerSphere = ModelEntity(mesh: .generateSphere(radius: 0.04))
            
            upperSphere.position = [0, 2, -2]
            upperSphere.physicsBody = .init()
            upperSphere.physicsBody?.mode = .kinematic
            upperSphere.physicsMotion = PhysicsMotionComponent()
            upperSphere.generateCollisionShapes(recursive: false)
            
            lowerSphere.position = [0, 1, -2]
            lowerSphere.physicsBody = .init()
            lowerSphere.physicsBody?.mode = .static
            lowerSphere.generateCollisionShapes(recursive: false)
            
            let sub = content.subscribe(to: CollisionEvents.Began.self, on: nil) { _ in print("Collision!") }
            subscriptions.append(sub)
            
            content.add(upperSphere)
            content.add(lowerSphere)
            
            Task {
                try? await Task.sleep(for: .seconds(2))
                print("Impulse applied")
                upperSphere.applyLinearImpulse([0, -1, 0], relativeTo: nil)
            }
        }
    }
}

Furthermore, if I set up the physicsMotion with a velocity: upperSphere.physicsMotion?.linearVelocity = [0,-1,0]

It moves down, but no collision is registered.


Solution

  • You just need to switch ball's physics body mode from .static to .dynamic. Kinematic mode is used only when the user needs to manually control a physics body movement.

    import SwiftUI
    import RealityKit
    
    struct CollisionView : View {
        @State var subscriptions: [EventSubscription] = []
        let upperBall = ModelEntity(mesh: .generateSphere(radius: 0.12))
        let lowerBall = ModelEntity(mesh: .generateSphere(radius: 0.12))
        
        var body: some View {
            RealityView { rvc in
                upperBall.position = [0,2,-2]
                upperBall.physicsBody = .init()
                upperBall.physicsBody?.mode = .static
                upperBall.generateCollisionShapes(recursive: false)
                
                lowerBall.position = [0,1,-2]
                lowerBall.physicsBody = .init()
                lowerBall.physicsBody?.mode = .static
                lowerBall.generateCollisionShapes(recursive: false)
                rvc.add(upperBall)
                rvc.add(lowerBall)
                
                let sub = rvc.subscribe(to: CollisionEvents.Began.self) { _ in
                    print("Collision detected")
                }
                subscriptions.append(sub)
                
                Task {
                    try await Task.sleep(for: .seconds(1))
                    upperBall.physicsBody?.mode = .dynamic
                }
            }
        }
    }
    

    @main struct ColliderApp : App {
        var body: some Scene {
            ImmersiveSpace() {
                CollisionView()
            }
        }
    }