HitTest for Spritekit and Scenekit

I have something that seems pretty simple ViewControllerwith SCNViewone that has one overlaySKScene.

The problem is that I do not want the tap to be detected in the base self.scene( gameScenein the example below) if it was first detected in the SpriteKit node in the overlay scene.

In this case, both scenes report that a blow occurred, even if a node click occurred on the SKOverlay scene 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")
    }
  }
}
+4
source share
2 answers

I don't have a fancy solution with code for you, but two possibilities are possible:

. , . SceneKit SpriteKit, , ( , SpriteKit). , action Scenekit. , 3D- .

. bool Scenekit, isHandledInOverlay SpriteKit, . Scenekit bool handletap return , .

: :

if self.actionButtonNode.contains(location!) {
  print("actionButtonNode touch")
}

handleTap ViewController. var, SKOverlayScene, , , 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 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- (-), , , , , , 3D- , .

Edit: (obj c) , Y , SKOverlay:

CGPoint tappedSKLoc = CGPointMake(location.x, self.menuOverlayView.view.frame.size.height-location.y);

, , , .

+1

, , Xartec, "", . - , , .

handleTap handlePan ( , ).

CheckMenuTap, SpriteKit true, .

NOT true, hitTest , node. , , SpriteKit SceneKit, - , .

@IBAction func handleTap (: UITapGestureRecognizer)   {       let location: CGPoint = .location(: 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
            }
        }
    }
}
+1

Source: https://habr.com/ru/post/1693120/


All Articles