Spritekit和Scenekit的HitTest

JP *_*shy 5 ios scenekit swift

我有一个似乎很基本的东西ViewControllerSCNView其中有一个overlaySKScene

我遇到的问题是,如果不首先在覆盖场景的SpriteKit节点中检测到该轻敲,则不希望在基础self.scenegameScene在下面的示例中)中检测到该轻敲。

使用以下内容,两个场景都报告即使点击发生在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)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

叠加场景

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")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Xar*_*tec 1

我没有\xe2\x80\x99t 有适合你的代码的奇特解决方案,但有两种可能的解决方法:

\n\n

A。通过处理主视图控制器中的所有触摸来避免这种情况。我也使用 SceneKit 和 SpriteKit 覆盖来做到这一点,将逻辑保留在覆盖场景之外,并仅将其用于其预期目的(图形覆盖,而不是常规的交互式 SpriteKit 场景)。您可以测试是否在 Scenekit 视图控制器中点击了操作按钮或任何其他按钮。如果没有按下按钮,则按下 3D 场景。

\n\n

b. 添加一个 bool 到主 Scenekit 视图控制器 isHandledInOverlay 并在按钮被触摸时将其设置在 SpriteKit 场景中。在主 Scenekit 视图控制器中检查 handletap 中的 bool 并返回,如果 true 则不执行任何操作。

\n\n

根据评论进行编辑:\n我只是建议移动这段代码:

\n\n
if self.actionButtonNode.contains(location!) {\n  print("actionButtonNode touch")\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

到ViewController中的handleTap函数。您必须创建一个 var 来保存 SKOverlayScene,而 SKOverlayScene 又具有 actionButtonNode(以及任何其他按钮)作为属性,因此在 handleTap 中您最终会得到类似以下内容的内容:

\n\n
if self.theOverlay.actionButtonNode.contains(taplocation) {\n  print("actionButtonNode touch")\n} else if self.theOverlay.action2ButtonNode.contains(taplocation) {\n  print("action2ButtonNode touch")\n} else if self.theOverlay.action3ButtonNode.contains(taplocation) {\n  print("action3ButtonNode touch")\n} else {\n    //no buttons hit in 2D overlay, let\'s do 3D hittest:\n    let opts = [ SCNHitTestOption.searchMode:1, SCNHitTestOption.ignoreHiddenNodes:0 ]\n    let hitList = self.sceneView!.hitTest(taplocation, options: opts)\n    if hitList.count > 0 {\n        for hit in hitList {\n           print("hit:", hit)\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,首先将 2D 点击位置与按钮的 2D 坐标进行比较,查看它们是否被点击,如果没有,则使用 2D 点击位置通过点击测试查看 3D 场景中的对象是否被点击。

\n\n

编辑:只是查看我自己的(obj c)代码,注意到我翻转了主视图控制器中的 Y 坐标以获取 SKOverlay 中的坐标:

\n\n
CGPoint tappedSKLoc = CGPointMake(location.x, self.menuOverlayView.view.frame.size.height-location.y);\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可能必须使用提供给按钮节点的 contains 函数的位置来执行此操作。

\n