visionOS中Portal作为AnchorEntity子节点无法显示的问题排查
问题描述
我遵循WWDC2023中关于RealityKit的session制作了一个连接独立世界的Portal,并将其设为识别垂直墙面的AnchorEntity的子节点。但Portal实体无法显示,而Sun实体可以正常显示。我尝试将Portal直接放置在空间中时,它能完美显示。
以下是我的代码:
struct ImmersiveView: View { var addToAnchor = true var body: some View { RealityView { content in let world = makeWorld() let portal = makePortal(world) content.add(world) let anchor = AnchorEntity(.plane(.vertical, classification: .wall, minimumBounds: [1, 1])) content.add(anchor) content.subscribe(to: SceneEvents.AnchoredStateChanged.self, on: anchor, componentType: nil) { event in print("AnchoredStateChanged") } // the portal not shows anchor.addChild(portal) let sun = try! await Entity(named: "Sun", in: realityKitContentBundle) // the sun shows anchor.addChild(sun) } } @MainActor func makeWorld() -> Entity { let world = Entity() world.components[WorldComponent.self] = .init() let earth = try! Entity.load(named: "Earth", in: realityKitContentBundle) let sun = try! Entity.load(named: "Sun", in: realityKitContentBundle) earth.position = .init(x: -0.5, y: 1.5, z: -2) sun.position = .init(x: 0.5, y: 1.5, z: -2) world.addChild(earth) world.addChild(sun) let resource = try! TextureResource.load(named: "Starfield") var material = UnlitMaterial() material.color = .init(texture: .init(resource)) // Attach the material to a large sphere. let background = Entity() background.components.set(ModelComponent( mesh: .generateSphere(radius: 1000), materials: [material] )) // Ensure the texture image points inward at the viewer. background.scale *= .init(x: -1, y: 1, z: 1) //world.addChild(background) return world } @MainActor func makePortal(_ world: Entity) -> Entity { let portal = Entity() portal.components[ModelComponent.self] = .init(mesh: .generatePlane(width: 1, height: 1, cornerRadius: 0.5), materials: [PortalMaterial()]) portal.components[PortalComponent.self] = .init(target: world) return portal } }
问题分析与解决方案
核心问题
- Anchor锚定状态未稳定时添加Portal:
AnchorEntity(.plane)需要时间完成空间墙面识别,锚定成功前它的空间变换矩阵是临时状态。Portal依赖正确的空间变换实现特殊渲染逻辑,此时添加会导致渲染失败;而普通模型(如Sun)不受此限制。 - 异步加载代码顺序混乱:
try! await Entity(named: "Sun")是异步操作,会导致Sun的添加时机晚于Portal,此时Anchor可能已经完成锚定,所以Sun能正常显示,而Portal因早加在未锚定的Anchor上无法渲染。
修复代码
步骤1:修改RealityView为异步闭包,等待Anchor锚定后添加Portal
struct ImmersiveView: View { var addToAnchor = true var body: some View { // 将闭包改为async形式 RealityView { content async in let world = makeWorld() let portal = makePortal(world) content.add(world) let anchor = AnchorEntity(.plane(.vertical, classification: .wall, minimumBounds: [1, 1])) content.add(anchor) // 监听锚定状态,成功后再添加Portal content.subscribe(to: SceneEvents.AnchoredStateChanged.self, on: anchor) { event in if event.state == .anchored { anchor.addChild(portal) } } // 异步加载Sun,此时Anchor可能已锚定 let sun = try! await Entity(named: "Sun", in: realityKitContentBundle) anchor.addChild(sun) } } // 原makeWorld和makePortal方法保持不变 @MainActor func makeWorld() -> Entity { let world = Entity() world.components[WorldComponent.self] = .init() let earth = try! Entity.load(named: "Earth", in: realityKitContentBundle) let sun = try! Entity.load(named: "Sun", in: realityKitContentBundle) earth.position = .init(x: -0.5, y: 1.5, z: -2) sun.position = .init(x: 0.5, y: 1.5, z: -2) world.addChild(earth) world.addChild(sun) let resource = try! TextureResource.load(named: "Starfield") var material = UnlitMaterial() material.color = .init(texture: .init(resource)) let background = Entity() background.components.set(ModelComponent( mesh: .generateSphere(radius: 1000), materials: [material] )) background.scale *= .init(x: -1, y: 1, z: 1) return world } @MainActor func makePortal(_ world: Entity) -> Entity { let portal = Entity() portal.components[ModelComponent.self] = .init(mesh: .generatePlane(width: 1, height: 1, cornerRadius: 0.5), materials: [PortalMaterial()]) portal.components[PortalComponent.self] = .init(target: world) return portal } }
额外优化建议
- 替换
try!为do-catch处理资源加载失败,避免崩溃:do { let sun = try await Entity(named: "Sun", in: realityKitContentBundle) anchor.addChild(sun) } catch { print("加载Sun失败:\(error)") } - 可给Portal添加临时占位模型,在锚定完成前显示,提升用户体验。
内容的提问来源于stack exchange,提问作者tommy0607
相关产品推荐
相关产品推荐

