如何检测SKSpriteNode是否被触摸到

61

我想检测我的精灵节点是否被触摸,但我不知道从哪里开始。

let Pineapple = SKSpriteNode(imageNamed: "Pineappleimg")
Pineapple.userInteractionEnabled = true
Pineapple.position = CGPoint(x: CGRectGetMidX(self.frame) - 200, y: CGRectGetMidY(self.frame));
self.addChild(Pineapple)

这个问题可以作为一个例子:https://dev59.com/pHzaa4cB1Zd3GeqPSJtt - Emil
11个回答

79

先将SKSpriteNodename属性设置为字符串。

pineapple.name = "pineapple"
pineapple.userInteractionEnabled = false

然后在Scene中的touchesBegan函数中

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    let touch:UITouch = touches.anyObject()! as UITouch
    let positionInScene = touch.locationInNode(self)
    let touchedNode = self.nodeAtPoint(positionInScene)

    if let name = touchedNode.name
    {
        if name == "pineapple"
        {
            print("Touched")
        }
    }

}

这是其中一种方法。
你也可以继承 SKSpriteNode 并覆盖其中的 touchesBegan 方法。

class TouchableSpriteNode : SKSpriteNode
{
    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        print("touched")
    }
}

那么就这样做

let pineapple = TouchableSpriteNode(imageNamed: "Pineappleimg")
pineapple.userInteractionEnabled = true
pineapple.position = CGPoint(x: CGRectGetMidX(self.frame) - 200, y: CGRectGetMidY(self.frame));
self.addChild(pineapple)

无法在Swift 3中编译。 - Parth Sane
4
Swift4.1. 重写func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)方法{ let touch:UITouch = touches.first! as UITouch //获取触摸对象 let positionInScene = touch.location(in: self) //获取在当前场景下的触摸位置 let touchedNode = self.atPoint(positionInScene) //获取被触摸的节点 if let name = touchedNode.name //如果被触摸的节点有名称 { if name == "pineapple" //如果名称为"pineapple" { print("Touched") //打印"Touched" } } } - uplearned.com

21

如果你只想要一些可以点击的节点(例如游戏用户界面中的“继续”或“退出”标签),那么这可能是一个替代但非常简单的解决方案:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first!
    if myNode.containsPoint(touch.locationInNode(self)) {
        print("touched")
    }
}

这个如何区分Continue和Exit标签? - Confused
当然,这对于重叠或相交的标签不起作用,但touchesBegan()是为整个场景重写的,而不是标签的方法。 - Florian Blum
那么 touchesBegun() 只能存在于场景类中吗? - Confused
实际上,它是由UIResponder继承而来的:https://developer.apple.com/reference/uikit/uiresponder/1621142-touchesbegan ... 这是UIView的超类。SKScene包含一个从UIView继承的SKView :) - Florian Blum
我刚学到所有的SKNode都遵循UIResponder协议。这意味着几乎任何节点都可以在被触摸时请求调用。我的理解正确吗? - Confused
显示剩余3条评论

12

更新Swift版本至3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)和XCode版本至8.2.1 (8C1002):

Set类型的值没有成员anyObject

'locationInNode'已更名为'location(in:)'

'nodeAtPoint'已更名为'atPoint(_:)'

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in touches {
        let location = touch.location(in: self)
        let node : SKNode = self.atPoint(location)
        if node.name == "myNodeName" {
            print("Hello")
        }
    }
}

7

这将检测到在Xcode 9.2 Swift 4.0中的触摸事件。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
    let touch:UITouch = touches.first!
    let positionInScene = touch.location(in: self)
    let touchedNode = self.atPoint(positionInScene)

    if let name = touchedNode.name
    {
        if name == "playLbl"
        {
            print("playLbl Touched")
        }
    }

}

1
touches.first会不会错过一些节点重叠的使用情况,就像Florian在另一个答案中提到的那样 - ruffin

7

Swift 5 更新

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        for touch in touches {
            let location = touch.location(in: self)
            let touchedNode = self.nodes(at: location)
            for node in touchedNode {
                if node.name == "play_button" {
                    startGame()
                }
            }
        }
    }

6

实现 touchesBegan 方法,该方法在触摸开始时调用。或者你也可以在 touchesEnded 中实现。

override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
    let touch = touches.anyObject() as UITouch
    let location = touch.locationInNode(self)
    let nodes = self.nodesAtPoint(location)

    for node in nodes
    {
        if node.name == "youNodeName"
        {
            // Node tapped
            // Do something

            break
        }
    }
}

6

Swift 3 答案,将触摸功能嵌入到 SKSpriteNode 的子类中:

class SpriteSub: SKSpriteNode {

    init() {
        super.init(texture: nil, color: UIColor.red, size: CGSize(width: 50, height: 50))
        isUserInteractionEnabled = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    ...

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("touch!")
    }

}

Crashalot还在让我输入NSCoder init...我忘记了如何正确地覆盖这个东西x{。 - Fluidity
1
@Fluidity 是的,你也需要那个。 - Crashalot
1
@Fluidity 又更新了。另外,你编辑的 UIColor.clear() 不是 Swift 3 代码。 - Crashalot
Crashalot,我开始感觉自己像个白痴了,因为我知道我以前做过这个......let sprite = SpriteSub() 对我来说什么也没有,而自动完成除了coder:init之外什么都没有显示...... - Fluidity
1
@Fluidity 这是因为 UIColor.clear 是透明的。尝试使用 UIColor.red。 - Crashalot
显示剩余2条评论

4
使用以下代码来检测SKSpriteNode上的触摸:
if(nodeAtPoint(location) == node){



}

简直是最简单的答案!太谢谢了 :) - Jeff
1
这个可以工作,但前提是该节点不会与其他节点重叠,对吧? - Crashalot
对于比较类,你不应该使用==而是应该使用===,对吗? - mogelbuster
@mogelbuster 是的,如果你要进行身份相等性比较,似乎 === 是正确的。详情请见:https://stackoverflow.com/a/44222407/144088 - Crashalot

1

更新为Swift 3.0和XCode 7.3.1。我有一个SKShapeNode,我将其派生到一个新类中并将其插入到场景中。当我想要检测此对象时,我按以下方式检查:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    for touch in touches {

        let location = touch.locationInNode(self)
        let nodes = self.nodesAtPoint(location)

        for node in nodes
        {
            if node is SKNodeDerivedNode
            {
                NSLog("Touch a SKNodeDerivedNode")
                break
            }
        }
    }
}

1
如果您即使在对SKSpriteNode进行子类化后仍然无法使精灵工作,那么您最有可能忘记在初始化时添加node.isUserInteractionEnabled = true!这将允许调用touchesBegan(_:with:),因为现在您可以与节点交互。

例子:

node = MySprite(texture: texture, size: size)
node.isUserInteractionEnabled = true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接