簡體   English   中英

Spritekit和Scenekit的HitTest

[英]HitTest for both Spritekit and Scenekit

我有什么似乎是一個非常基本的ViewController具有SCNView它有一個overlaySKScene

我遇到的問題是,如果首先在覆蓋場景中的SpriteKit節點中檢測到它,我希望在底層的self.scene (下gameScene中的gameScene )中檢測到tap。

使用以下內容,即使在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)
      }
    }
  }
}

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

我沒有為您提供代碼的奇特解決方案,但有兩種可能的解決方法:

一個。 通過處理主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函數的位置來執行此操作。

猶豫要發帖,因為我不是專家,如果我正確理解Xartec,我“想”我以類似的方式解決了它。 我還需要在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
            }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM