如何在RealityKit游戏中同时实现2D HUD与3D内容的交互?
在SwiftUI容器内解决交互冲突
1. 自定义SpriteView的UIViewRepresentable
直接用SwiftUI原生SpriteView无法灵活控制事件传递,需要自定义UIViewRepresentable包装SKView,同时持有RealityView的引用:
import SwiftUI import SpriteKit import RealityKit struct SpriteKitHUDView: UIViewRepresentable { @Binding var scene: SKScene weak var realityView: ARView? func makeUIView(context: Context) -> SKView { let skView = SKView() skView.isUserInteractionEnabled = true skView.allowsTransparency = true skView.scene = scene return skView } func updateUIView(_ uiView: SKView, context: Context) { if uiView.scene !== scene { uiView.scene = scene } } }
2. 在SpriteKit Scene中控制事件转发
在你的SKScene子类里,重写触摸方法,只处理HUD按钮的交互,非按钮区域的事件直接转发给RealityView:
class HUDScene: SKScene { weak var realityView: ARView? override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let touchLoc = touch.location(in: self) // 检查是否点击到HUD按钮 if let button = atPoint(touchLoc) as? SKButtonNode { // 替换成你的按钮节点类型 button.triggerAction() return } // 非按钮区域,转发事件给RealityView realityView?.touchesBegan(touches, with: event) } // 按需重写其他触摸方法,保持事件同步 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { realityView?.touchesMoved(touches, with: event) } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { realityView?.touchesEnded(touches, with: event) } }
3. 在ContentView中关联视图
通过onAppear获取RealityView的引用,传递给HUDScene:
struct ContentView: View { @State private var hudScene = HUDScene(size: UIScreen.main.bounds.size) @State private var arView: ARView? var body: some View { ZStack { RealityView { content in // 添加你的3D内容 let box = ModelEntity(mesh: .generateBox(size: 0.5)) content.add(box) } .onAppear { // 获取底层ARView实例 arView = UIApplication.shared.windows.first?.rootViewController?.view.subviews.first(where: { $0 is ARView }) as? ARView hudScene.realityView = arView } SpriteKitHUDView(scene: $hudScene, realityView: arView) .ignoresSafeArea() } } }
脱离SwiftUI容器,直接用UIKit开发
如果完全不想依赖SwiftUI,直接基于UIKit构建ViewController,事件控制更直接:
import UIKit import RealityKit import SpriteKit class GameViewController: UIViewController { private var arView: ARView! private var skView: SKView! private var hudScene: HUDScene! override func viewDidLoad() { super.viewDidLoad() // 初始化RealityView arView = ARView(frame: view.bounds) view.addSubview(arView) // 添加3D内容 let box = ModelEntity(mesh: .generateBox(size: 0.5)) let anchor = AnchorEntity(world: .zero) anchor.addChild(box) arView.scene.addAnchor(anchor) // 初始化SpriteKit HUD hudScene = HUDScene(size: view.bounds.size) hudScene.realityView = arView skView = SKView(frame: view.bounds) skView.isUserInteractionEnabled = true skView.allowsTransparency = true skView.scene = hudScene view.addSubview(skView) } }
关键注意事项
- 开启SKView的
allowsTransparency = true,确保HUD透明区域不遮挡RealityView的视觉。 - 非交互的HUD节点(比如背景)设置
isUserInteractionEnabled = false,避免无意义拦截事件。 - 如需HUD完全覆盖时禁用RealityView交互,只需在HUDScene中添加一个全屏可交互背景节点,点击时不转发事件即可。
内容的提问来源于stack exchange,提问作者PKCLsoft
相关产品推荐
相关产品推荐

