SKSpriteNode 在 isUserInteractionEnabled false 时不让触摸通过

SKSpriteNode does not let touches pass through when isUserInteractionEnabled false

我正在尝试使用 SKSpriteNode 在 SpriteKit 中创建叠加层。但是,我希望触摸通过叠加层,所以我将 isUserInteractionEnabled 设置为 false。然而,当我这样做时,SKSpriteNode 似乎仍然吸收了所有的接触并且不允许底层节点做任何事情。

这是我正在谈论的例子:

class 

TouchableSprite: SKShapeNode {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("Touch began")
    }
}

class GameScene: SKScene {
    override func sceneDidLoad() {
        // Creat touchable
        let touchable = TouchableSprite(circleOfRadius: 100)
        touchable.zPosition = -1
        touchable.fillColor = SKColor.red
        touchable.isUserInteractionEnabled = true
        addChild(touchable)


        // Create overlay
        let overlayNode = SKSpriteNode(imageNamed: "Fade")
        overlayNode.zPosition = 1
        overlayNode.isUserInteractionEnabled = false
        addChild(overlayNode)
    }
}

我试过子类化 SKSpriteNode 并手动将触摸事件委托给适当的节点,但这会导致很多奇怪的行为。

如有任何帮助,我们将不胜感激。谢谢!

阅读您找到的实际来源:

/**
  Controls whether or not the node receives touch events
*/
open var isUserInteractionEnabled: Bool

但这是指触摸的内部节点方法。 默认情况下 isUserInteractionEnabledfalse 那么像你的覆盖层 SKSpriteNode 这样的子对象的触摸默认情况下是对主(或父)处理的简单触摸 class(对象在这里,存在,但如果你不执行任何操作,你只需触摸它)

要通过触摸浏览叠加层,您可以在 GameScene 中实现类似下面的代码。还请记住不要将 -1 用作 zPosition,因为这意味着它在您的场景下方。

P.S. :我在你的精灵中添加了一个 name 以将其召回 touchesBegan 但你可以创建全局变量给你的 GameScene:

override func sceneDidLoad() {
        // Creat touchable
        let touchable = TouchableSprite(circleOfRadius: 100)
        touchable.zPosition = 0
        touchable.fillColor = SKColor.red
        touchable.isUserInteractionEnabled = true
        touchable.name = "touchable"
        addChild(touchable)
        // Create overlay
        let overlayNode = SKSpriteNode(imageNamed: "fade")
        overlayNode.zPosition = 1
        overlayNode.name = "overlayNode"
        overlayNode.isUserInteractionEnabled = false
        addChild(overlayNode)
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("GameScene Touch began")
        for t in touches {
            let touchLocation = t.location(in: self)
            if let overlay = self.childNode(withName: "//overlayNode") as? SKSpriteNode {
                if overlay.contains(touchLocation) {
                    print("overlay touched")
                    if let touchable = self.childNode(withName: "//touchable") as? TouchableSprite {
                        if touchable.contains(touchLocation) {
                            touchable.touchesBegan(touches, with: event)
                        }
                    }
                }
            }
        }
    }
}

你需要小心 zPosition。有可能走到幕后,使事情变得复杂。

更糟糕的是触摸事件的顺序。我记得读过它是基于节点树,而不是 zPosition。禁用场景触摸,您应该会看到结果。

现在是这样说的:

The Hit-Testing Order Is the Reverse of Drawing Order In a scene, when SpriteKit processes touch or mouse events, it walks the scene to find the closest node that wants to accept the event. If that node doesn’t want the event, SpriteKit checks the next closest node, and so on. The order in which hit-testing is processed is essentially the reverse of drawing order.

For a node to be considered during hit-testing, its userInteractionEnabled property must be set to YES. The default value is NO for any node except a scene node. A node that wants to receive events needs to implement the appropriate responder methods from its parent class (UIResponder on iOS and NSResponder on OS X). This is one of the few places where you must implement platform-specific code in SpriteKit.

但这可能并非总是如此,因此您需要检查 8 、 9 和 10 之间的一致性