Swift SKSpriteNode:检测点击/双击/长按

6
我正在尝试创建SKSpriteNode的子类,该子类可以检测用户交互(轻按、双击和长按),然后转交给委托。这似乎是一个相对常见的需求,但有以下问题:
  1. 代码无法检测到双击而不触发单击。
  2. 我没有找到一种方法来检测长按/长按操作。
我是不是在错误的方向上努力?我无法帮助感到SpriteKit应该有标准方法来处理如此基本的事情。我知道有UIGestureRecognizer,但它似乎与特定的SKNodes而不是UIView不太兼容。
这是我目前拥有的。基于Apple的示例,我将所有非设备特定代码放在主类文件中:
import SpriteKit

enum ActionType: Int {
    case Single
    case Double
    case Hold
}

protocol EnvironmentElementDelegate {
    func handleActionOnElement(element: EnvironmentElement, actionType: ActionType)
}

class EnvironmentElement: SKSpriteNode {

    let delegate: EnvironmentElementDelegate!

    init(imageNamed: String, elementNamed: String, delegate: EnvironmentElementDelegate) {
        self.delegate = delegate
        let texture = SKTexture(imageNamed: imageNamed)
        super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
        self.name = elementNamed
        userInteractionEnabled = true
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

将所有iOS特定的代码放在一个单独的扩展文件中:

import SpriteKit

extension EnvironmentElement {

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            if (touch.tapCount >= 2) {
                NSObject.cancelPreviousPerformRequestsWithTarget(self)
            }
        }
    }

    override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            if (touch.tapCount == 1) {
                delegate.handleActionOnElement(self, actionType: ActionType.Single)
                // Unable to find Swift equivalent to this: [self performSelector:@selector(onFlip) withObject:nil afterDelay:0.3];
            } else {
                delegate.handleActionOnElement(self, actionType: ActionType.Double)
            }
        }
    }
}

通常情况下,当我需要一个能够检测用户点击的对象时,我会使用SKNode,它的效果更好,而使用SKSpriteNode则会遇到很多问题。至于iOS特定的代码,我会将其添加到类本身中,这样也更有效。 - Totka
感谢您的回复Totka,但是您在SKNode中也不会遇到同样的问题吗?如何检测双击和长按而不是单击?我的担忧是,也许我的代码远离识别这些基本用户输入的最佳实践。 - TheNeil
1
我建议您使用内置的UIGestureRecognizers,因为它们支持长按和双击(带有单击拒绝)。 - 0x141E
谢谢。虽然我之前提到过,UIGestureRecognizers似乎与SpriteKit节点不兼容:没有添加它们的功能。这正是我对自己的设计不太有信心的原因。我一直在阅读更多关于MVC的内容,也许我应该在视图层面上解决如何添加识别器,然后从那里检测哪个节点被点击了? - TheNeil
你好。感谢您在流行的uiscrollview问题上添加了[ios]标签,但由于这是一个太小的编辑,您很可能会很快面临其他审阅者的拒绝。最好还要检查整篇文章是否有改进之处,或者等待2000个声望以进行大规模编辑。 - Cœur
除了使用touch.tapCount,你可以按照之前的建议使用UIGestureRecognizer。你没有GUI来完成这个任务,必须在代码中实现。这里有一个很好的例子,展示了如何转换触摸点,然后你的游戏就可以找到被触摸的节点了。http://spritekitlessons.com/gesture-recognizer-examples-with-swift-and-sprite-kit/ - John
2个回答

5

您可以在 touchesBegan: 中简单使用 touch.tapCount


它运作良好。只需注意,这不会阻止第一个触摸事件的触发,例如 tapCount==1 触摸仍将在 touchesBegantouchesEnded 上触发。因此,您仍然需要计算延迟识别它是真正的单击还是双击,可能需要使用 Timer。 - Juguang

3

我以前从未遇到过检测双击的问题,但如果确实需要,我会这样做:

1- 在类初始化时添加一个名为alreadyTouch的本地变量,并将其设置为false

2- 在touchesBegin内部,如果alreadyTouchfalse,则将其设置为true,否则执行接下来的操作

3- 设置一个0.2秒的本地Timer,并在设置alreadyTouch = true时启动此计时器,以检测触摸是否足够快速。将此计时器也加入if else条件中。

希望这可以帮助您。


嗨Totka,你能否提供一些代码示例,比如如何运行本地计时器? - Efren
抱歉,我丢失了那个项目,或者更确切地说,丢失了存储该项目的硬盘。 - Totka

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