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)
}
}
}
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!