如何在Spritekit节点上检测滑动手势?

4
我目前正在使用Swift3和Spritekit编写2D无尽奔跑游戏。我最近添加了一些代码以便于检测swipes的操作(如下所示)。我的问题是:如何在Spritekit节点上检测滑动动作并检查其方向?我意识到这个问题之前已经被问过,但我找不到适用于Swift3的解决方案,因为我遇到的所有东西似乎都是Swift和Swift2的。
以下是我的滑动检测代码:
override func viewDidLoad() {
  super.viewDidLoad()
  let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
    swipeLeft.direction = UISwipeGestureRecognizerDirection.left
    self.view.addGestureRecognizer(swipeLeft)

    let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
    swipeRight.direction = UISwipeGestureRecognizerDirection.right
    self.view.addGestureRecognizer(swipeRight)

    let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
    swipeUp.direction = UISwipeGestureRecognizerDirection.up
    self.view.addGestureRecognizer(swipeUp)

    let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
    swipeDown.direction = UISwipeGestureRecognizerDirection.down
    self.view.addGestureRecognizer(swipeDown)
}

我已经使用一个函数进行了测试:

func respondToSwipeGesture(gesture: UIGestureRecognizer) {
        if let swipeGesture = gesture as? UISwipeGestureRecognizer {
            switch swipeGesture.direction {
            case UISwipeGestureRecognizerDirection.right:
                print("Swiped right")
            case UISwipeGestureRecognizerDirection.down:
                print("Swiped down")
            case UISwipeGestureRecognizerDirection.left:
                print("Swiped left")
            case UISwipeGestureRecognizerDirection.up:
                print("Swiped up")
            default:
                break
            }
        }
}

手势似乎正常工作,因为打印语句已经正确地显示了我的手势反应结果。

我还添加了一个代理以避免可能的SIGABRT错误:

class GameViewController: UIViewController, UIGestureRecognizerDelegate

如果您需要更多的信息,请告诉我!

编辑: 添加了初始化拼图块的代码

//The class
class ChoosePiecesClass: SKSpriteNode{
    func moveWithCamera() {
        self.position.x += 5;
}


//initialize a piece
private var RandomPiece1: ChoosePiecesClass?;


override func didMove(to view: SKView) {
    RandomPiece1 = childNode(withName: "RandomPiece1") as? ChoosePiecesClass;
    RandomPiece1?.position.x = 195;
    RandomPiece1?.position.y = -251;
}

override func update(_ currentTime: TimeInterval) {
    RandomPiece1?.moveWithCamera();
}

编辑以提供控制台中的错误语句,单击“开始游戏”并触发致命错误代码,导致游戏崩溃。

fatal error: init(coder:) has not been implemented: file /Users/ardemis/Documents/PuzzleRunner/PuzzleRunner 3 V3/PuzzleRunner/ChoosePieces.swift, line 33

它还显示以下内容。
EXEC_BAT_INSTRUCTION(code = EXEC_1386_INVOP, subcode = 0x0)

在与致命错误声明相同的行上。
1个回答

8
我如果想要追踪特定的SpriteNodes,就不会使用UIGestures。您可以在TouchesBegan中轻松跟踪节点,并找出滑动方向。这个例子在屏幕上有三个精灵,将打印/记录任何一个精灵被划过的方向,并忽略所有其他滑动。

编辑>我刚刚为我的Box对象创建了一个子类(抱歉我没有使用您的命名,但它具有相同的功能)。可能有很多方法可以做到这一点,我选择在子类上创建一个协议,并使场景符合该协议。我将所有触摸/滑动功能移动到子类中,当检测到滑动时,它只需调用委托并指出哪个对象已被滑过。

protocol BoxDelegate: NSObjectProtocol {
    func boxSwiped(box: Box)
}

class Box: SKSpriteNode {

    weak var boxDelegate: BoxDelegate!
    private var moveAmtX: CGFloat = 0
    private var moveAmtY: CGFloat = 0
    private let minimum_detect_distance: CGFloat = 100
    private var initialPosition: CGPoint = CGPoint.zero
    private var initialTouch: CGPoint = CGPoint.zero
    private var resettingSlider = false

    override init(texture: SKTexture?, color: UIColor, size: CGSize) {

        super.init(texture: texture, color: color, size: size)

        self.isUserInteractionEnabled = true
    }

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

    func moveWithCamera() {
        self.position.x += 5
    }

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

        if let touch = touches.first as UITouch! {

            initialTouch = touch.location(in: self.scene!.view)
            moveAmtY = 0
            moveAmtX = 0
            initialPosition = self.position
        }
    }

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

        if let touch = touches.first as UITouch! {

            let movingPoint: CGPoint = touch.location(in: self.scene!.view)

            moveAmtX = movingPoint.x - initialTouch.x
            moveAmtY = movingPoint.y - initialTouch.y
        }
    }

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

        var direction = ""
        if fabs(moveAmtX) > minimum_detect_distance {

            //must be moving side to side
            if moveAmtX < 0 {
                direction = "left"
            }
            else {
                direction = "right"
            }
        }
        else if fabs(moveAmtY) > minimum_detect_distance {

            //must be moving up and down
            if moveAmtY < 0 {
                direction = "up"
            }
            else {
                direction = "down"
            }
        }

        print("object \(self.name!) swiped " + direction)

        self.boxDelegate.boxSwiped(box: self)
    }
}

在 GameScene.sks 中

确保将协议添加到声明后,使 GameScene 符合 BoxDelegate。

class GameScene: SKScene, BoxDelegate {

    var box = Box()
    var box2 = Box()
    var box3 = Box()

    override func didMove(to view: SKView) {

        box = Box(color: .white, size: CGSize(width: 200, height: 200))
        box.zPosition = 1
        box.position = CGPoint(x: 0 - 900, y: 0)
        box.name = "white box"
        box.boxDelegate = self
        addChild(box)

        box2 = Box(color: .blue, size: CGSize(width: 200, height: 200))
        box2.zPosition = 1
        box2.name = "blue box"
        box2.position = CGPoint(x: 0 - 600, y: 0)
        box2.boxDelegate = self
        addChild(box2)

        box3 = Box(color: .red, size: CGSize(width: 200, height: 200))
        box3.zPosition = 1
        box3.name = "red box"
        box3.position = CGPoint(x: -300, y: 0)
        box3.boxDelegate = self
        addChild(box3)
    }

    func boxSwiped(box: Box) {
        currentObject = box
        print("currentObject \(currentObject)")
    }

    override func update(_ currentTime: TimeInterval) {
        box.moveWithCamera()
        box2.moveWithCamera()
        box3.moveWithCamera()
    }
}

嗨,这个真的很好用,非常感谢!代码也非常漂亮。只要我有15分,我就会点赞它。不过我有一个小问题:我正在处理程序生成的拼图块,并将它们填充到插槽中。我已经将它们声明为一个类,并使用func moveWithCamera()来不断增加它们的x值,以匹配相机的速度。我知道如何使用box.texture替换盒子所需的纹理,但我不知道如何让它们随着相机移动。我应该在我的原始帖子中放置这个问题,可惜了! - Ardemis
为了进一步澄清,因为我很糟糕地解释事情:这些插槽是持久的,它们随着相机移动,因此之前可选择(通过滑动)的拼图块也会随之移动。我计划用这些盒子替换那些拼图块,并将其重新纹理化作为拼图块。 - Ardemis
这些拼图块是 SKSpriteNode 的子类吗? - Ron Myschuk
1
如果你只是在屏幕上打印出你划过的内容,那么这只是可能的。如果你想让GameScene对你划过的部分进行特殊处理,你需要找出断开连接的地方。如果我要猜的话,我会检查你是否在GameScene之后添加了Delegate,以及在创建对象时是否将box、delegate = self赋值。 - Ron Myschuk
1
事情是这样的...TouchesEnded在对象的子类中,这很好,因为代码与该对象封装在一起。你想要运行/控制的代码应该从主游戏场景而不是对象中运行/控制。从技术上讲,一个部分不知道另一个部分,但GameScene知道它们所有的部分。这就是为什么我使用协议将信息发送回GameScene。所以根据我对你正在做的有限了解,我可能会为你的平台部件使用不同的类。 - Ron Myschuk
显示剩余12条评论

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