我有一个非常基本的ViewController
SCNView
,其overlaySKScene
。
我遇到的问题是,如果首次检测到,我不希望在基础self.scene
(下例中的gameScene
)中检测到点按在叠加场景中的SpriteKit节点中。
使用以下内容,两个场景都会报告即使在SKOverlay场景节点actionButtonNode
上发生了点击也发生了点击。
的ViewController
import UIKit
import SceneKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
sceneView = self.view as? SCNView
gameScene = GameScene()
let view = sceneView
view!.scene = gameScene
view!.delegate = gameScene as? SCNSceneRendererDelegate
view!.overlaySKScene = OverlayScene(size: self.view.frame.size)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(_:)))
tapGesture.cancelsTouchesInView = false
view!.addGestureRecognizer(tapGesture)
}
@objc func handleTap(_ sender:UITapGestureRecognizer) {
let projectedOrigin = self.sceneView!.projectPoint(SCNVector3Zero)
let taplocation = sender.location(in: self.view!)
let opts = [ SCNHitTestOption.searchMode:1, SCNHitTestOption.ignoreHiddenNodes:0 ]
let hitList = self.sceneView!.hitTest(taplocation, options: opts)
if hitList.count > 0 {
for hit in hitList {
print("hit:", hit)
}
}
}
}
OverlayScene
import UIKit
import SpriteKit
class OverlayScene: SKScene {
var actionButtonNode: SKSpriteNode!
override init(size: CGSize) {
super.init(size: size)
self.backgroundColor = UIColor.clear
self.actionButtonNode = SKSpriteNode()
self.actionButtonNode.size = CGSize(width: size.width / 2, height: 60)
self.actionButtonNode.position = CGPoint(x: size.width / 2, y: 80)
self.actionButtonNode.color = UIColor.blue
self.addChild(self.actionButtonNode)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let location = touch?.location(in: self)
if self.actionButtonNode.contains(location!) {
print("actionButtonNode touch")
}
}
}
答案 0 :(得分:1)
我没有为您提供代码的奇特解决方案,但有两种可能的解决方法:
一个。通过处理主viewcontroller中的所有触摸来避免这种情况。我也使用SceneKit和SpriteKit叠加来实现这一点,将逻辑保留在叠加场景之外,并仅将其用于预期目标(图形叠加,而不是常规的交互式SpriteKit场景)。您可以测试是否在Scenekit视图控制器中命中了动作按钮或任何其他按钮。如果没有按下按钮,则3D场景被击中。
湾将bool添加到主要的Scenekit视图控制器isHandledInOverlay,并在触摸按钮时将其设置在SpriteKit场景中。在主要的Scenekit视图控制器中检查handletap中的bool并返回,如果为true则不执行任何操作。
根据评论进行修改: 我只是建议移动这段代码:
if self.actionButtonNode.contains(location!) {
print("actionButtonNode touch")
}
到ViewController中的handleTap函数。你必须创建一个包含SKOverlayScene的var,而后者又将actionButtonNode(以及任何其他按钮)作为属性,因此在handleTap中你会得到类似的结果:
if self.theOverlay.actionButtonNode.contains(taplocation) {
print("actionButtonNode touch")
} else if self.theOverlay.action2ButtonNode.contains(taplocation) {
print("action2ButtonNode touch")
} else if self.theOverlay.action3ButtonNode.contains(taplocation) {
print("action3ButtonNode touch")
} else {
//no buttons hit in 2D overlay, let's do 3D hittest:
let opts = [ SCNHitTestOption.searchMode:1, SCNHitTestOption.ignoreHiddenNodes:0 ]
let hitList = self.sceneView!.hitTest(taplocation, options: opts)
if hitList.count > 0 {
for hit in hitList {
print("hit:", hit)
}
}
}
首先,您将2D点按位置与按钮的2D坐标进行比较以查看是否点击了按钮,如果没有,则使用2D点击位置查看3D场景中的对象是否被点击了热门考试。
编辑:只是查看我自己的(obj c)代码并注意到我在主视图控制器中翻转Y坐标以获取SKOverlay中的坐标:
CGPoint tappedSKLoc = CGPointMake(location.x, self.menuOverlayView.view.frame.size.height-location.y);
您可能必须使用您提供给按钮节点的contains函数的位置来执行此操作。
答案 1 :(得分:1)
犹豫要发帖,因为我不是专家,如果我正确理解Xartec,我认为&#34;我以类似的方式解决了它。我还需要在scenekit中使用屏幕边缘平移 - 花了一些时间来计算出那个。
我创建了handleTap和handlePan(识别器状态为begin,changed和end)。
我打电话给CheckMenuTap,循环浏览所有活动的SpriteKit按钮,如果其中一个按钮被点击,则返回true。
如果不是,那么我处理hitTest并查看是否有节点命中。如果我不想要它们,那么这是我发现避免在SpriteKit和SceneKit上受到打击的唯一方法 - 希望有所帮助。
@IBAction func handleTap(识别器:UITapGestureRecognizer) { let location:CGPoint = recognizer.location(in:scnView)
if(windowController.checkMenuTap(vLocation: location) == true)
{
return
}
let hitResults: [SCNHitTestResult] = scnView.hitTest(location, options: hitTestOptions)
if(data.gameStateManager.gameState == .run)
{
for vHit in hitResults
{
//print("Hit: \(vHit.node.name)")
if(vHit.node.name?.prefix(5) == "Panel")
{
if(data.gamePaused == true) { return }
windowController.gameControl.selectPanel(vPanel: vHit.node.name!)
return
}
}
}
}