Detecting collisions in visionOS
Looking at ways to extend the Jenga game we built earlier, we need to know when the pieces collide with the table. We can use that to then play sounds or even add game logic. First though we need to detect collisions.
I found this guide to detecting collisions on Lickability, which explains nicely how to detect collisions between our game pieces and the table or floor.
We can extend the Jenga example to add collision detection events.
Adding collision events #
There are two parts to adding collision detection events. First we need to set up a variable to store event subscriptions. In ImmersiveView
, add a private @State
variable:
@State private var subscription: EventSubscription?
Then in the RealityKit
view we can subscribe to an event which will trigger any time something collides with the floor:
subscription = content.subscribe(to: CollisionEvents.Began.self, on: floor) { collisionEvent in
print("💥 Collision between \(collisionEvent.entityA.name) and \(collisionEvent.entityB.name)")
}
Running this, any time a piece falls off the table onto the floor we should see a print statement.
Colliding with table #
I wasn't able to work out how to enable this for the table we build in Reality Composer Pro. Instead, I created a method similar to generateFloor
.
Since we added methods to load materials from a Reality Kit Content scene, we can make use of these to load the table material and apply it to an entity created in code. Add the following to the SharedViewModel
:
func generateTable() -> ModelEntity {
let material: RealityKit.Material = loadMaterial(from: "Scene", named: "table") ?? UnlitMaterial()
let tableShape: MeshResource = .generateBox(width: 1.5, height: 0.1, depth: 1.5, cornerRadius: 0.1)
let table = ModelEntity(
mesh: tableShape,
materials: [material]
)
table.generateCollisionShapes(recursive: false)
table.components[PhysicsBodyComponent.self] = .init(
massProperties: .default,
mode: .static
)
return table
}
I made the table using a box primative, and added a cornerRadius
to make it smoother. It's set to generate collision shapes, and physics and behave similarly to the floor.
Back in ImmersiveView
we can use this to generate and display a table:
let table = viewModel.generateTable()
table.name = "table"
table.position.x = viewModel.startingPositionX + viewModel.pieceWidth
table.position.y = viewModel.startingPositionY
table.position.z = viewModel.startingPositionZ - viewModel.pieceWidth
We then can add code to subscribe to the table
collisions:
subscription = content.subscribe(to: CollisionEvents.Began.self, on: table) { collisionEvent in
print("💥 Collision between \(collisionEvent.entityA.name) and \(collisionEvent.entityB.name)")
}
We should now have a table generated in code that prints when collisions are detected:
- Previous: Loading material from Reality Composer Pro scene
- Next: Playing sounds