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
1 Answer
Reset to default 0Problem 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.