如何检测哪个SKSpriteNode被触摸了?

6
我发现一个类似的问题,链接在这里。但我想检测并识别用户触摸的是哪个Sprite,而我不知道如何做到这一点。这是我的变量:
var sprites: [[SKSpriteNode]] = [[SKSpriteNode(imageNamed: "a"), SKSpriteNode(imageNamed: "b")], [SKSpriteNode(imageNamed: "c"),SKSpriteNode(imageNamed: "d")]]

这个想法是找出spriteNode,然后用另一个sprite替换它或者改变颜色,但我不知道如何在这个spriteNodes矩阵中做到这一点,我猜第一步是识别sprite。

你好!0.0 @rakeshbs - AnaMM
首先,不要像那样创建精灵。你必须设置它们的名称。稍后,在touchesBegan中,您可以使用nodeAtPointnodesAtPoint来检查哪个节点被触摸,并根据它们的名称采取适当的操作。如果您需要一些代码,请告诉我... - Whirlwind
是的,这就是我想要的。您知道如何使用rakeshbs的代码获取用户触摸的精灵名称吗?我的意思是,从touchesprite类将名称发送到我的场景,以更改精灵的颜色,因为我无法做到这一点。谢谢!@Whirlwind - AnaMM
这一切都在那里解释了。他展示了两种方式:1)触摸检测在游戏场景内处理。2)触摸检测直接在SKNode的子类的touchesBegan中处理。你正在尝试实现这两个例子中的哪一个? - Whirlwind
我看到了。好的,我很快就会回答。 - Whirlwind
显示剩余5条评论
2个回答

13
即使我看不出为什么需要这样做,你所尝试的事情可以使用委托模式(delegation pattern)来实现。基本上,你将告诉你的代理(场景或者任何你设置为代理的东西)去为你做某些事情,并且你可以直接从按钮的touchesBegan方法中执行这个操作。同时,你也会把按钮的名称传递给场景。
要实现这个功能,首先你需要定义一个名为ButtonDelegate的协议。该协议定义了一个要求,即任何符合条件的类都必须实现一个叫做printButtonsName(_:)的方法:
protocol ButtonDelegate:class {

   func printButtonsName(name:String?)
}
这是在你的 GameScene 类中实现的方法,但是要从按钮的 touchesBegan 中调用它。同时,这个方法将用于将按钮的名称传递给其代理(场景),这样你就可以知道哪个按钮被点击了。
接下来是按钮类本身。 Button 可以长这样:
class Button : SKSpriteNode{

    weak var delegate:ButtonDelegate?

    init(name:String){
        super.init(texture: nil, color: .purpleColor(), size: CGSize(width: 50, height: 50))
        self.name = name
        self.userInteractionEnabled = true
    }

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


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

        delegate?.printButtonsName(self.name)
    }  
}

这里的重要部分是 userInteractionEnabled = true,这意味着按钮将接受触摸。另一个重要的事情是 delegate 属性。如前所述,按钮将将场景设置为其代理。当我们创建一些按钮时,将稍后完成将场景设置为按钮的代理的步骤。为了让您更容易理解,可以将代理想象成一位为老板工作的员工 :) 老板(即按钮)告诉他的员工(即场景)为他做某事(打印他的名字)。

好的,让我们确保场景符合 ButtonDelegate 协议...这为什么很重要?这很重要,因为工人(场景)必须遵循老板(按钮)的命令。通过符合此协议,工人与老板签订了一份合同,确认他知道如何做好自己的工作并会听从老板的命令 :)

class GameScene: SKScene, ButtonDelegate {


    override func didMoveToView(view: SKView) {

        let play = Button(name:"play")
        play.delegate = self
        let stop = Button(name:"stop")
        stop.delegate = self

        play.position = CGPoint(x: frame.midX - 50.0, y: frame.midY)
        stop.position = CGPoint(x: frame.midX + 50.0, y: frame.midY)

        addChild(play)
        addChild(stop)
    }

    func printButtonsName(name: String?) {

        if let buttonName = name {
            print("Pressed button : \(buttonName) ")
        }

        //Use switch here to take appropriate actions based on button's name (if you like)
    }
}

就是这样。当您点击播放按钮时,按钮本身的touchesBegan将被调用,然后按钮将使用场景类内定义的方法告诉其代理打印其名称。


哇!!!谢谢你!你不知道我有多感激你的帮助!你太棒了!谢谢!:D - AnaMM
为什么需要在委托中调用一个函数?为什么按钮不能自己执行所需的操作,一旦它意识到被触摸了呢? - Confused
@confused 嗯,你可以这样做,但这取决于按下按钮时应该发生什么。您可能希望向场景添加/删除节点或进行转换。或从场景中删除节点,在这些情况下,委托将非常有用。您可以使用块,并将代码块传递给按钮实例,但我更喜欢委托责任的方式。您并没有真正定义“如何”按钮应该“执行活动本身”的含义,因此我无法进一步评论。 - Whirlwind
“do activity itself” 意味着在按钮的代码中覆盖 touchesBegan 函数。 - Confused
1
我同意委托,因为在问题中它指出:“这个想法是识别spriteNode,然后将其替换为其他sprite或更改颜色。”如果您想更改sprite,则委托将进行调用而不是sprite touched。他没有说要更改纹理。 - Skyler Lauren
显示剩余2条评论

1

首先,你需要另一种创建精灵的方法,以下是其中一种方式:

let spriteA = SKSpriteNode(imageNamed: "a")
scene.addChild(spriteA)
let spriteB = SKSPriteNode(imageNamed: "b")
scene.addChild(spriteB)
...and so on...

现在我们需要为精灵设置一个名称,以便稍后知道哪个节点被点击了。要为精灵添加名称,只需执行以下操作:
spriteNode.name = "name of the sprite"

把这段代码放在上面的示例中,会看起来像这样:
let spriteA = SKSpriteNode(imageNamed: "a")
spriteA.name = "a"
scene.addChild(spriteA)
let spriteB = SKSPriteNode(imageNamed: "b")
spriteB.name = "b"
scene.addChild(spriteB)
...and so on...

将以下代码放入您的SKScene子类中以检测触摸事件:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first as UITouch!
    let touchLocation = touch.locationInNode(self)
    let targetNode = nodeAtPoint(touchLocation) as! SKSpriteNode
}

targetNode是您点击的节点。

如果您想获取精灵的名称,可以使用targetNode.name


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