使用 UIPanGestureRecognizer 连续移动 SKSpriteNode

Posted

技术标签:

【中文标题】使用 UIPanGestureRecognizer 连续移动 SKSpriteNode【英文标题】:Move SKSpriteNode with UIPanGestureRecognizer continuously 【发布时间】:2018-05-30 17:07:24 【问题描述】:

我试图在一个简单的二维SKScene 中连续移动SKSpriteNode。这涉及到在UIGestureRecognizer 的平移的相反方向上移动包含所有背景节点的数组并同时为节点设置动画。

我已经通过实现节点和识别器并在平移时调用其方法来实现移动:

@objc func didPan(gesture: UIPanGestureRecognizer) 
    let pan = gesture.velocity(in: self.view)
    if gesture.state == .changed 
        for sprites in levelSprites 
            if pan.x > 0 && pan.x > pan.y 
                sprites.run(SKAction.moveTo(x: sprites.position.x - 40, duration: 0.5))
                self.player.run(SKAction(named: "WalkRight")!)
             else if pan.x < 0 && pan.x < pan.y 
                sprites.run(SKAction.moveTo(x: sprites.position.x + 40, duration: 0.5))
                self.player.run(SKAction(named: "WalkLeft")!)
             else if pan.y < 0 && pan.x > pan.y 
                sprites.run(SKAction.moveTo(y: sprites.position.y - 30, duration: 0.5))
                self.player.run(SKAction(named: "WalkUp")!)
             else if pan.y > 0 && pan.x < pan.y 
                sprites.run(SKAction.moveTo(y: sprites.position.y + 30, duration: 0.5))
                self.player.run(SKAction(named: "WalkDown")!)
            
        
    

我面临的问题是,当我实际上想要在持续时间内移动它们时,此方法仅针对我设置的值以及我必须保持的有限时间来移动精灵并为其设置动画以准确匹配动画长度的锅。

我特意为平移而不是滑动实现了一个识别器,因为场景还将包含一个用于稍后控制移动的操纵杆。

对于不使用UIGestureRecognizer 来完成的工作,是否有更简单的解决方案?

【问题讨论】:

【参考方案1】:

didPan() 在用户单次平移期间被多次调用,状态为.changed,但不是定期调用。

相反,您应该声明两个全局状态变量来保存当前的水平和垂直方向(例如,第一个方向是从左到右或从右到左)并在每次调用 didPan() 时更新这些变量。

您应该仅在方向发生变化时运行 SKAction 实例,并且这些 SKAction 实例应该无限循环,而不是仅具有 0.5 秒的一次性特定持续时间(请参阅 SceneKit API 手册中的 SKAction.repeatForever())。

另外,您需要确保在运行下一个移动动作之前删除已经运行的移动动作。

最后,当didPan().ended 状态调用时,调用removeAllActions()

enum VerticalState
   case .none
   case .topToBottom
   case .bottomToTop

enum HorizontalState
   case .none
   case .leftToRight
   case .rightToLeft


var verticalState = VerticalState.none
var horizontalState = HorizontalState.none


@objc func didPan(gesture: UIPanGestureRecognizer) 
    let pan = gesture.velocity(in: self.view)
    if gesture.state == .changed 
        for sprites in levelSprites 
            if pan.x > 0 && pan.x > pan.y  && horizontalState <> .leftToRight
                horizontalState = .none
                verticalState = .none
             else if pan.x < 0 && pan.x < pan.y && horizontalState <> .rightToLeft                
                horizontalState = .rightToLeft
                verticalState = .none
             else if pan.y < 0 && pan.x > pan.y && verticalState <> .bottomToTop                
                horizontalState = .none
                verticalState = .bottomToTop
             else if pan.y > 0 && pan.x < pan.y && verticalState <> topToBottom
                horizontalState = .none
                verticalState = .topToBottom
            


            switch horizontalState
            
               case .leftToRight:      
                   sprites.removeActionForKey("movement")    
                   self.player.removeActionForKey("movement")
                   sprites.run(SKAction.repeatForever(SKAction.moveBy(x: -40, y:0, duration: 0.5), forKey:"movement"))
                   self.player.run(SKAction(named: "WalkRight")!, forKey:"movement")
               case .rightToLeft:
                   sprites.removeActionForKey("movement")
                   self.player.removeActionForKey("movement")         
                                      sprites.run(SKAction.repeatForever(SKAction.moveBy(x: 40, y:0,, duration: 0.5), forKey:"movement"))
                   self.player.run(SKAction(named: "WalkLeft")!, forKey:"movement")
               case .none: 
                   break
            
            switch verticalState
            
               case .topToBottom:                
                   sprites.removeActionForKey("movement")
                   self.player.removeActionForKey("movement")         
                   sprites.run(SKAction.repeatForever(SKAction.moveBy(x: 0, y:-30, duration: 0.5), forKey:"movement"))
                self.player.run(SKAction(named: "WalkUp")!, forKey:"movement")
               case .bottomToTop:    
                   sprites.removeActionForKey("movement")      
                   self.player.removeActionForKey("movement")         
                   sprites.run(SKAction.repeatForever(SKAction.moveBy(x: 0, y:30,, duration: 0.5), forKey:"movement"))
                   self.player.run(SKAction(named: "WalkDown")!, forKey:"movement")
               case .none:
                   break
            


        
    else if gesture.state == .ended 
         sprites.removeActionForKey("movement")      
         self.player.removeActionForKey("movement")  
    

【讨论】:

注意,您还需要使用moveBy 而不是moveTo moveTo 的连续循环将带您到达第一个moveTo 位置 我尽我所能根据您的回答提供了编辑和示例。请随时更新示例以说明您的具体意思。

以上是关于使用 UIPanGestureRecognizer 连续移动 SKSpriteNode的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 UIPanGestureRecognizer 移动对象时会有延迟?

UIPanGestureRecognizer ***视图未获取事件,子视图使用它们

在 UITableView 上滑动删除以使用 UIPanGestureRecognizer

在 UIPanGestureRecognizer 中使用velocityInView

使用 UIPanGestureRecognizer 拖动 + 旋转触摸下车

使用 UIPanGestureRecognizer 移动和缩放 UIView