touchesBegan与UITapGestureRecognizer的区别

5
我正在使用Swift制作一个SpriteKit游戏。我想知道在手势方面哪种方式最好?
目前,我有一个override func touchesBegan处理所有的轻触操作和一个UILongPressGestureRecognizer处理长按操作。
你知道,轻触会按下按钮并使英雄跳跃。长按会让英雄躲避。
由于某些原因,我的长按函数并不总是被调用(有时你可以按10次并且停止被调用(不被识别),其他时候可能是5次,它会变化),这导致我昨天花了一整天尝试新事物和调查,从而引出了这个问题。
是使用touchesBegan更好,还是将所有的触摸调用移动到由UITapGestureRecognizer处理的新函数中更好?
我已经将所有东西从touchesBegan移动到UITapGestureRecognizer,但它似乎非常缓慢。但我可能实现得不对?
这是如何设置recognisers的:
func setupRecognizers() {
    let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    view!.addGestureRecognizer(tapRecognizer)

    let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: Selector("handleLongPress:"))
    longTapRecognizer.minimumPressDuration = 0.2
    view!.addGestureRecognizer(longTapRecognizer)

}

以下是处理手势的函数:

func handleTap(recognizer: UIGestureRecognizer) {
    //currently all handled in touchesBegan
}

func handleLongPress(recognizer: UIGestureRecognizer) {
    print("1 --------> longPress Called.... ", recognizer.state.rawValue, gameState)
    if gameState == .Play {
       //do stuff
       //duck Hero
    } else {
       //switch gameState
    }
}

这是处理触摸/点击的功能函数:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
   /* Called when a touch begins */

for touch in touches {
     let location = touch.locationInNode(self)

     //do stuff

     switch gameState {
        case .MainMenu:
        break
        ... //more states
        }
   }
   super.touchesBegan(touches, withEvent: event)
}

如果我把所有的内容从touchesBegans移动到一个tapRecogniser(上面的空函数),我还需要实现这个功能,以转换触摸位置坐标:

func handleTap(recognizer: UIGestureRecognizer) {
    let location = convertPointFromView(CGPoint(x: recognizer.locationInView(nil).x, y: recognizer.locationInView(nil).y))
    print("Converted Coords: ", location)

    //then do all touchesBegan stuff
 }

我尝试了两种方法,但后者似乎非常缓慢和迟钝。也许我忘记实现一些推荐的东西了?

我的长按手势似乎并不总是被调用,这之间可能存在冲突吗?


1
我不会在SpriteKit游戏中使用任何与UIKit相关的东西。如果你想测试触摸的持续时间,只需使用update方法计时即可。尽量在游戏中使用SpriteKit的工具。任何与UIKit相关的内容(如游戏菜单或控件),我会放在UIViewController中。 - hamobi
所有东西,包括菜单,都是在gameScene中创建并通过游戏状态(inGame、Menu、gameOver等)进行区分的。那么您建议在touchesBegan中加入一个计时器,然后根据if timer > longPressValue duck, else jump?的逻辑判断是否跳跃?或者SpriteKit有不同的tapGestures吗? - Reanimation
它没有触摸手势...你只需要捕捉你按住手指的时间,并在你松开手指时重置计时器。就是那样的事情。 - hamobi
我会试一下,这可能解决为什么长按不总是被调用并保持在touchesBegan内部的问题...可能会更好。 - Reanimation
当然,如果您需要我编写一些代码,请告诉我。 - hamobi
@hamobi 如果您想在下面的答案中写一个示例,那将非常酷。我相信您的实现会比我的更有效 :) 谢谢。 - Reanimation
1个回答

3

如果您按住红色方块两秒钟,将会收到一条消息,当您松开时,消息将消失。您可能需要添加一些布尔值来确保您的角色在2秒按钮按住后不会重复执行某些动作。希望这足以让您开始了。

import SpriteKit

class GameScene: SKScene {

    // time values
    var delta = NSTimeInterval(0)
    var last_update_time = NSTimeInterval(0)

    var longTouchTimer = NSTimeInterval(0)
    var touched = false
    var testLabel = SKLabelNode(text: "you have touched for awhile now bro")

    let touchSprite = SKSpriteNode(color: SKColor.redColor(), size: CGSizeMake(100, 100))

    override func didMoveToView(view: SKView) {
        touchSprite.position = CGPointMake(self.size.width/2, self.size.height/2)
        addChild(touchSprite)

        testLabel.hidden = true
        testLabel.position = touchSprite.position
        testLabel.position.y += 100
        testLabel.fontSize = 20
        addChild(testLabel)
    }


    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)

            if touchSprite.containsPoint(location) {
                touched = true
            }
        }
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch: AnyObject in touches {
            let location = touch.locationInNode(self)

            if !touchSprite.containsPoint(location) {
                touched = false
                longTouchTimer = 0
            }
        }
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        touched = false
        longTouchTimer = 0
    }

    override func update(currentTime: NSTimeInterval) {
        if last_update_time == 0.0 {
            delta = 0
        } else {
            delta = currentTime - last_update_time
        }

        last_update_time = currentTime

        if touched {
            longTouchTimer += delta
        }

        if longTouchTimer >= 2 {
            testLabel.hidden = false
        } else {
            testLabel.hidden = true
        }
    }
}

哦,太棒了。谢谢你。我现在会尝试一下。感谢您的发布。 - Reanimation
@hamobi 很棒的写作。只需使用 touches 方法启动和停止计时器(使用 NSTimer 不应在 SpriteKit 中使用)同时使用 currentTime 更新是完美的。请注意,因为您正在使用 currentTime 而不是系统时间,所以当游戏因任何原因暂停时它运行得很好。 - Skyler Lauren
我只看到一个空白屏幕..可能是我做了一些愚蠢的事情 :S - Reanimation
可能是你的特定项目设置的方式不同。最重要的部分是使用“update”来增加你的时间变量。尝试将这个部分添加到你的项目中。 - hamobi
你只需要添加一个布尔值来跳跃或弯腰,检查你当前是否正在跳跃或弯腰。 - hamobi
显示剩余2条评论

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