如何检测节点上的触摸

how to detect touch on node

我有一个应用程序,它每 1 秒在屏幕上生成一个球。现在,我希望用户触摸那些使它们消失的球(removeFromParent())。据我所知,我必须通过 touchesBegan 设置触摸功能,我这样做了,这是我的代码:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch: AnyObject in touches{
        let positionOfTouch = touch.location(in: self)
        enumerateChildNodes(withName: "BALL") { (node: SKNode, nil) in
            if positionOfTouch == node.position {
                print("just touched the ball")
            }
            else{
                print("error")
            }
        }
    }

问题是,当我触摸屏幕/球时,控制台打印 error 而不是 just touched the ball,这意味着我的代码不起作用。此外,控制台将错误消息打印为我认为的球数。我不明白我做错了什么以及如何真正设置此功能。 这是我的 createBall 函数,它从我的 BallNode class(类型 SKShapeNode)实现:

    func createBall(){
    let ball = BallNode(radius: 65)
    print(ball.Name)
    print(ball._subName!)

    ball.position.y = ((frame.size.height) - 200)
    let ballXPosition = arc4random_uniform(UInt32(frame.size.width)) // set the ball a randon position from the top of the screen
    ball.position.x = CGFloat(ballXPosition)

    ball.physicsBody?.categoryBitMask = PhysicsCategory.ball // ball's category bitMask
    ball.physicsBody?.collisionBitMask = PhysicsCategory.ball // prevent objects from intersecting
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.topBorder // when need to know if two objects touch each other

    addChild(ball)

}

你能帮我吗?因为我是 swift 的新手,所以我也想获得有关此触摸检测的一些解释(以及一般的触摸 - 苹果文档很差)。

您正在比较精确 接触点与节点的精确 位置,它们不太可能相同。

if positionOfTouch == node.position {

相反,您需要测试用户的触摸是否足够接近球的位置。

一种选择是使用 SKNodecontains 函数,它将为您处理。

if node.contains(positionOfTouch) {

旁注:您可能希望使用 SKSpriteNode 而不是 SKShapeNode,因为 SKShapeNode 在 SpriteKit 中的性能不佳。

每次您触摸屏幕时,您都会循环浏览所有球,看看您是否正在触摸其中一个球。如果屏幕上有 50 个球,它会全部穿过它们以查看您是否正在触摸 1。这不是确定您是否正在触摸 1 的有效方法。

有很多方法可以做到这一点,但我会做的是处理球内的触球 class。这样你就不必弄清楚你是否正在触摸一个球以及它可能是哪一个。

Explanation of protocol (to the best of my ability) this may seem a little much right now, but the faster you learn and understand protocols that better off you will be (IMO).

In this example we will use a protocol to setup a delegate of the BallNode class. A protocol is a set user defined "rules" that must be followed by any class that you designate compliant to that protocol. In my example I state that for a class to be compliant to the BallNodeDelegate protocol it must contain the didClick func. When you add the BallNodeDelegate after GameScene you are stating that this class will be compliant to that protocol. So if in GameScene you did not have the didClick func it will cause an error. All this is put in place so that you have an easy way to communicate between your BallNode instances and your GameScene class (without having to pass around references to your GameScene to each BallNode). Each BallNode then has a delegate (GameScene) which you can pass back the information to.

在你的 BallNode 里面 class 确保你有 isUserInteraction = true

在 BallNode 外部 class 创建一个协议,将触摸信息发送回 GameScene

protocol BallNodeDelegate: class {
    func didClick(ball: BallNode)
}

在您的 BallNode 中创建一个委托变量 class

weak var delegate: BallNodeDelegate!

移动触摸开始给你BallNode class

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

    self.delegate?.didClick(ball: self)
}

在 GameScene 中添加对 BallNode 协议的遵守

class GameScene: SKScene, BallNodeDelegate

在 GameScene 中创建 Ball 时确保设置它的委托

let ball = BallNode()
ball.delegate = self

在GameScene中添加嵌套。处理点击的函数

func didClick(ball: BallNode) {
    print("clicked ball")
}

查看 SKNode 中定义的 nodes(at:CGPoint) 以检索触摸位置的节点列表。不过,您需要使用 convertPoint(fromView) 在视图坐标和场景坐标之间进行转换。文档 here and here.