HitTest for SKSpriteNode
Asked Answered
T

1

6

This is my problem:

I work with SpriteKit and I want to ignore touch event when it occurs in some rectangle (prevent calling touchesBegan). The way how I would like to do it is something like "overriding the hitTestWithEvent of UIView". But I can't find any similar method for SKSpriteNode that could ignore event and prevent calling touchesBegan.

Sure

  • I could set isUserInteractionEnabled to false but it disables the whole sprite not just a part.
  • I could check location of touch in touchesBegan method, but this is to late - others sprites beneath at the same position will not receive this event anymore.
  • I also tried to use SKCropNode, but it just prevent displaying the sprite and events are handled even on the invisible area.

Does anybody have idea how to prevent part of sprite from handling an event?

Towroy answered 11/1, 2017 at 15:31 Comment(0)
D
2

The open func nodes(at p: CGPoint) -> [SKNode] can be useful to detect which nodes you have touched to a point. After that, you can exclude the CGRect zone for each node you want..

import SpriteKit
class GameScene: SKScene {
    var warningZone = CGRect(x: -80, y: -60, width: 100, height: 100)
    override func didMove(to view: SKView) {
        let nodeRed = SKSpriteNode.init(color: .red, size: CGSize(width:300,height:200))
        nodeRed.name = "nodeRed"
        addChild(nodeRed)
        nodeRed.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
        let nodeBlue = SKSpriteNode.init(color: .blue, size: CGSize(width:300,height:200))
        nodeBlue.name = "nodeBlue"
        addChild(nodeBlue)
        nodeBlue.position = CGPoint(x:self.frame.midX+100,y:self.frame.midY+20)
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in: self)
            let nodesTouched = self.nodes(at: location)
            for node in nodesTouched {
                guard let n = node.name else { return }
                print("Node touched: \(node.name) at point:\(location)")
                switch n {
                case "nodeRed":
                    // do your exclusions for the nodeRed
                    print("\(node.frame)")
                    if !warningZone.contains(location) {
                        print("you have touch a safe red zone")
                    }
                case "nodeBlue":
                    // do your exclusions for the nodeBlue
                    print("\(node.frame)")
                default:
                    break
                }
            }
        }
    }
}

Output (I've draw the warningZone with a white rectangle only to show you where is it..): enter image description here

Developer answered 11/1, 2017 at 18:11 Comment(2)
Thank you for your answer, but I think this is very naughty workaround of solution I seek. This approach requires to manually check every spripte in the scene :( Imagine scene with dozens or hundrets of sprites - how messy this switch would be. I'm looking for solution where you can enable user interaction of particular sprite and override it's hit test method. This approach allows you to subclass SKSpriteNode and handle touch adequately to the type of subclass inside subclass definition.Towroy
Well, what you propose it's pratically impossible because you could touch a node that not directly knows the node who you want to touch because you are inside that subclass, or it know his parent node but the parent don't know the right node.. in other words if you shoot on the heap, you must know all the heap: your touch in this case is like a bullet..Developer

© 2022 - 2024 — McMap. All rights reserved.