Thanks to @wildpeaks' answer, I have implemented this idea that works for my project (iOS13+ API, RealityKit ARView), here's the code snippet without the business logics:
// Check if the entity is in screen every 0.5s
func startTrackingAnchorEntities() {
anchorCheckTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] timer in
self?.checkForNewAnchors(timer: timer)
}
}
private func checkForNewAnchors(timer: Timer) {
// Check if there is an anchor, you may do additional checks here for the right entity(names, children, etc.)
guard let entity = self.scene.anchors.first {
print("❌ Missing required objects - entity: \(self.scene.anchors.first != nil)")
return
}
// Get current active camera
let cameraTransform = self.cameraTransform
print("📱 Camera Transform - Position: \(cameraTransform.translation), Rotation: \(cameraTransform.rotation)")
// Prevent checking with the initial frame camera positions that often just passes
if (cameraTransform.translation == SIMD3<Float>(0,0,0)) {
print("⚠️ Camera at origin (0,0,0), skipping...")
return
}
// Convert world position to camera space
let cameraAnchorEntity = AnchorEntity(world: Transform(scale: .one, rotation: cameraTransform.rotation, translation: cameraTransform.translation).matrix)
// Get the entity's position
let entityPosition = entity.position(relativeTo: cameraAnchorEntity)
print("🔍 Entity relative position: \(bubblePosition)")
// IMPORTANT! Get world position for projection, else the projected point becomes super big
let worldPosition = entity.convert(position: .zero, to: nil)
// Project bubble position to screen space
guard let projectedPoint = self.project(worldPosition) else {
print("❌ Failed to project entity position to screen space")
return
}
print("📍 Projected point on screen: \(projectedPoint)")
print("📱 Screen bounds: \(self.bounds)")
print("🌍 World position used for projection: \(worldPosition)")
// Check if the projected point is within screen bounds
guard self.bounds.contains(projectedPoint) else {
print("⚠️ Entity outside screen bounds")
return
}
print("✅ Entity visible! Scene position: \(entity.scenePosition), Camera position: \(cameraTransform.translation)")
// Stop the timers after detecting the visible bubble
timer.invalidate()
anchorCheckTimer = nil
// Do whatever you need to do afterwards
}
What's new and what I noticed are:
The idea to use this function is to call it in your ARView
's set up function. For my use case, I first load an ARWorldMap
then I call this function, it runs fine in parallel without interfering with the relocalization progress for those whom may concern.
the .z
thing @JoeySlomowitz mentioned does still persists when I'm working on this issue. So I removed it and it seems to still work like a charm.
I used ARView.cameraTransform
which is a newer way to get active camera position in addition to session.currentFrame.camera
You may find documentation about it here.
The tricky part is the relative position space, make sure everything is relative to the same position space.