macOS下通过鼠标输入正确移动SpriteKit场景中的SKNode
解决SCNView叠加SKScene时SKNode命中测试无效的问题
嘿,我之前做macOS项目时也碰到过一模一样的状况——把SpriteKit场景作为SceneKit视图的叠加层后,原来针对SCNView的手势命中测试完全识别不到SKNode。给你两个实用的解决方案,按需选择就行:
方案一:直接给SKScene绑定手势识别器
既然问题出在SCNView的命中测试不涵盖叠加的SKScene,那不如把SKNode的手势逻辑直接放到SKScene这边处理,这样命中测试就天然适配SKNode了:
// 在SKScene的配置代码里添加手势 func setupSKSceneGestures() { guard let skView = self.view else { return } let panGesture = NSPanGestureRecognizer(target: self, action: #selector(handleSKNodePan(_:))) skView.addGestureRecognizer(panGesture) } @objc private func handleSKNodePan(_ gesture: NSPanGestureRecognizer) { guard let skView = gesture.view as? SKView else { return } let touchPos = gesture.location(in: skView) // 直接对SKScene做命中测试 let hitNodes = skView.nodes(at: touchPos) guard let targetNode = hitNodes.first else { return } // 根据手势平移量移动节点 let translation = gesture.translation(in: skView) targetNode.position.x += translation.x targetNode.position.y += translation.y gesture.setTranslation(.zero, in: skView) }
这个方案最直接,因为SKView本身就提供了nodes(at:)方法专门检测SKNode的命中,不需要额外的坐标转换。
方案二:在SCNView的手势里兼容SKScene的命中测试
如果你的手势逻辑必须统一放在SCNView这边处理,那可以通过坐标转换,把SCNView里的触摸位置转到SKScene的坐标系中,再手动查询SKNode:
// 假设这是你绑定在SCNView上的手势方法 @objc private func handlePanGesture(_ gesture: NSPanGestureRecognizer) { let scnTouchPos = gesture.location(in: yourSCNView) // 先检查SCNNode的命中 let scnHitResults = yourSCNView.hitTest(scnTouchPos, options: nil) if !scnHitResults.isEmpty { // 原来的SCNNode移动逻辑 handleSCNNodeMovement(scnHitResults.first!, gesture: gesture) return } // 没命中SCNNode的话,转换坐标到SKScene guard let skView = yourSKScene.view else { return } let skTouchPos = yourSCNView.convert(scnTouchPos, to: skView) // 查询SKScene里的命中节点 let skHitNodes = skView.nodes(at: skTouchPos) if let targetSKNode = skHitNodes.first { // 处理SKNode的移动 let translation = gesture.translation(in: skView) targetSKNode.position.x += translation.x targetSKNode.position.y += translation.y gesture.setTranslation(.zero, in: skView) } }
这里要注意两个关键点:
- 确保SKScene的
isUserInteractionEnabled属性是true(默认就是true,除非你手动修改过) - 坐标转换时要确认SCNView和SKView的层级关系,
convert(_:to:)方法会帮你处理视图间的坐标映射
另外,如果你的SKNode是某个父节点的子节点,移动时要考虑相对坐标系的问题,比如用targetSKNode.convert(translation, from: skView)把视图坐标系的平移量转到节点的父坐标系中,避免位置偏移。
内容的提问来源于stack exchange,提问作者narner
相关产品推荐
相关产品推荐

