【问题标题】:HitTest for both Spritekit and ScenekitSpritekit 和 Scenekit 的 HitTest
【发布时间】:2018-02-10 16:36:32
【问题描述】:

我有一个看起来很基本的ViewController 和一个SCNView,它有一个overlaySKScene

我遇到的问题是,如果在 SpriteKit 节点中首次检测到点击,我希望在底层 self.scene(下例中为 gameScene)中检测到点击在叠加场景中。

通过以下内容,两个场景都报告发生了点击,即使点击发生在 SKOverlay 场景节点 actionButtonNode 上。

视图控制器

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)
      }
    }
  }
}

叠加场景

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")
    }
  }
}

【问题讨论】:

    标签: ios swift scenekit


    【解决方案1】:

    我没有适合您的代码解决方案,但有两种可能的解决方法:

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

    b.将布尔值添加到主 Scenekit 视图控制器 isHandledInOverlay 并在按钮被触摸时将其设置在 SpriteKit 场景中。在主 Scenekit 视图控制器中检查 handletap 中的 bool 并返回,如果为真则什么也不做。

    基于 cmets 编辑: 我只是建议移动这段代码:

    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);
    

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

    【讨论】:

    • 感谢 Xartec 一直在帮助我快速完成 :),对于您的观点 a),我没有太多运气这样做,因为当我执行命中测试时,它的工作方式似乎有所不同(这是真的吗?)遍历点击,它只是场景包元素。
    • 是的,您仍然需要手动检查点击是否在按钮内,就像您在 spritekit 场景中所做的那样,而是在主视图控制器内(以及 之前 进行命中测试仅当没有触摸 spritekit 按钮时才在场景包中)。
    • Xartec,如果你有一个代码示例说明你的意思,如果它有效,我可以奖励你
    • 用一些代码更新了我的答案。我建议保持赏金开放,不管你是否得到更好/其他的答案。
    • 添加了另一个重要部分,翻转 Y 坐标,否则它不会在 SCNView 中的点击和 spriteit 叠加层之间对齐。
    【解决方案2】:

    因为我不是专家而犹豫发布,如果我正确理解 Xartec,我“认为”我以类似的方式解决了它。我还需要 Scenekit 中的屏幕边缘平移 - 花了一段时间才弄清楚。

    我创建了 handleTap 和 handlePan(识别器状态为 begin、changed 和 end)。

    我调用 CheckMenuTap 循环遍历我所有活动的 SpriteKit 按钮,如果其中一个按钮被点击则返回 true。

    如果不为真,则处理 hitTest 并查看是否得到节点命中。如果我不想要 SpriteKit 和 SceneKit,那是我发现的唯一方法,希望能有所帮助。

    @IBAction func handleTap(识别器:UITapGestureRecognizer) { 让位置:CGPoint =识别器。位置(在: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
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-09-07
      • 2015-12-25
      • 2018-06-21
      • 2018-03-03
      • 2017-04-12
      • 2018-01-09
      • 2018-02-07
      • 2020-07-12
      • 1970-01-01
      相关资源
      最近更新 更多