【问题标题】:Delay next action in SKAction.sequence after runBlock occurs (Swift)?在 runBlock 发生后延迟 SKAction.sequence 中的下一个动作(Swift)?
【发布时间】:2015-03-10 06:24:28
【问题描述】:

moveToduration 属性在 runBlock 内部时不被遵循,从而允许序列中的后续操作在应该只在 duration 秒后执行时立即执行。

代码A(序列正确执行):

let realDest = CGPointMake(itemA.position.x, itemA.position.y)
let moveAction = SKAction.moveTo(realDest, duration: 2.0)

itemB.runAction(SKAction.sequence([SKAction.waitForDuration(0.5), moveAction, SKAction.runBlock {
    itemB.removeFromParent()
}]))

代码 B(序列未正确执行):

let badMoveAction = SKAction.runBlock {
    let realDest = CGPointMake(itemA.position.x, itemA.position.y)
    let moveAction = SKAction.moveTo(realDest, duration: 2.0)
    itemB.runAction(moveAction)
}

itemB.runAction(SKAction.sequence([SKAction.waitForDuration(0.5), badMoveAction, SKAction.runBlock {
    itemB.removeFromParent()
}]))

Code A 中,itemBmoveAction 完成(大约 2 秒)后被删除。这是正确的顺序。

Code B 中,itemBbadMoveAction 完成之前被删除,这意味着itemB 永远不会从其原始位置移动。就好像Code B 中不支持持续时间属性一样。

我们如何才能像在Code B 中一样移动itemB,但要确保序列中的下一个操作在badMoveAction 完成之前不会开始?

【问题讨论】:

    标签: ios swift sprite-kit


    【解决方案1】:

    这应该做你想做的事。我只是稍微重新安排了代码。

    itemB.runAction(SKAction.sequence([
    
        // wait for half a second
        SKAction.waitForDuration(0.5),
        SKAction.runBlock({
    
            // after waiting half a second, get itemA's position
            let realDest = CGPointMake(itemA.position.x, itemA.position.y)
            let moveAction = SKAction.moveTo(realDest, duration: 2.0)
    
            // move to that position, after we get there, remove itemB from scene
            itemB.runAction(moveAction, completion: {
                itemB.removeFromParent()
            })
    
        })
    ]))
    

    【讨论】:

      【解决方案2】:

      解释: 当你执行一段代码时,它是异步执行的。这意味着代码将在单独的队列中执行,而其余代码将继续执行。

      代码 A 的情况下,这不会导致问题,因为 moveTo 操作在当前队列上运行,完成,然后触发 runBlock。

      代码 B 的情况下,这会产生一个问题,因为 badMoveAction 块被触发,它开始在单独的队列上执行,并且代码继续执行下一个部分,恰好是删除操作删除 itemB,而 badMoveAction 在后台执行。如果您在该 runBlock 中执行了其他操作,您会看到它们同时运行,但是因为您删除了它,所以所有内容都被删除了。

      解决方案 如果您说您想将 badMoveAction 添加到节点并在每次您可以执行以下操作时进行计算:

      let waitAction = SKAction.waitForDuration(0.5)
      let removeAction = SKAction.removeFromParent()
      
      let sequence = SKAction.sequence([waitAction, moveAction(), removeAction])
      
      func moveAction() -> SKAction {
          let realDest = CGPointMake(itemA.position.x, itemA.position.y)
          let moveAction = SKAction.moveTo(realDest, duration:2.0)
          return moveAction()
      }
      

      *代码只是举例说明您可以采取哪些措施来解决此问题。

      【讨论】:

      • 不幸的是,在“runBlock”之后添加括号不起作用。它对你有用吗?
      • 嗨对不起,我更新了我的答案,这是一个错误。不幸的是,我不在电脑前测试任何代码,所以我也从我的答案中删除了代码。我认为问题在于 runBlocks 立即执行一次并且只执行一次。您需要为所需的每个操作创建一个 SKAction 并将它们按顺序排列。你有什么理由要使用 runBlock 吗?
      • 因为我们需要多个活动作为一个逻辑块执行(例如,计算新位置并移动项目 A,删除项目 A 并更新标签)。还有其他方法可以实现吗?
      • 你能不能调用一个函数来计算新的位置,生成一个 SKAction 来移动 Item A 并带有一个完成块,该完成块将删除 itemA 并更新标签,然后让该函数返回它。那么你可以让你的节点执行返回的操作吗?
      • 我们可以试试,但你是说 Swift 中没有办法以线性方式将函数链接在一起?
      【解决方案3】:

      您可以尝试其他解决方案:

      itemB.runAction(SKAction.waitForDuration(0.5)) {
           let realDest = CGPointMake(itemA.position.x, itemA.position.y)
           let moveAction = SKAction.moveTo(realDest, duration: 2.0)
           itemB.runAction(moveAction) {
               itemB.removeFromParent()
           }
      }
      

      runAction 函数中的尾随闭包是一个完成块。

      【讨论】:

        【解决方案4】:

        您需要更改runAction 的来电者。使用self 调用它。因为您使用的是runBlock,并且您说parasite 在其中运行动作,所以无需调用parasite 上的函数。所以就这样称呼吧:

        self.runAction(SKAction.sequence([SKAction.waitForDuration(0.5), moveParasite]))
        

        【讨论】:

        • 不幸的是,使用“self”代替“parasite”并没有改变任何东西。其他建议?
        【解决方案5】:

        根据文档,runBlock 会立即执行,并且不遵守moveTo 的持续时间。 代码 A代码 B 的顺序都是正确的,但在后一种情况下,它似乎是乱序的,因为 moveTo() 持续时间没有得到遵守。

        作为解决运行代码块导致一个或多个操作的问题的解决方案,同时尊重持续时间,请尝试以下代码:

        func notSoBadMoveAction() -> SKAction {
            let realDest = CGPointMake(itemA.position.x, itemA.position.y)
            let moveAction = SKAction.moveTo(realDest, duration: 2.0)
            return moveAction
        }
        
        itemB.runAction(SKAction.sequence([   
           SKAction.waitForDuration(0.5), 
           notSoBadMoveAction(), 
           SKAction.runBlock {
               itemB.removeFromParent()
           }]))
        

        此代码确实使用了移动的全部持续时间,它可以替代 runBlock 的一些(但可能不是所有其他)其他用途。如果您愿意,该函数也可以接受参数,因此可以成为生成动作的更一般情况。

        添加:这是该函数的替代版本,显示了添加操作的可能性,并在函数内计算内容:

        func myMoveAction(pos: CGPoint, duration : NSTimeInterval) -> SKAction {
            let realDest = CGPointMake(pos.x, pos.y)
            let moveAction = SKAction.moveTo(realDest, duration: duration/4)
            let moveAction2 = SKAction.moveTo(CGPointMake(realDest.x/2, realDest.y/2), duration: duration * 2/4)
            let moveAction3 = SKAction.moveTo(realDest, duration: duration/4)
            return SKAction.sequence([moveAction, moveAction2, moveAction3])
        }
        

        【讨论】:

        • 当你说runBlock立即执行时,你是什么意思?在代码块 A 中,最终的 runBlock 在 moveAction 完成后不会执行。感谢您的帮助,只是想了解 Swift!
        • 似乎 runBlock 只是忽略了所有持续时间的东西,并在瞬间完成所有事情......我只是参考文档,以及我在操场上看到的情况。 ;-)
        • 要清楚,前面的评论应该说最终的 runBlock 在 moveAction 完成后不会执行 直到。因此,混乱。 :)
        • 我的理解是 runBlock 的 immediate 部分与给定的 runBlock 相关,并且当前块立即执行。顺序保持不变,因此在您的原始代码中,最终的 runBlock 确实在 badMoveAction 之后执行。但是 badMoveAction 在没有持续时间的情况下执行。使用我的回答中的函数可以避免这个“立即”问题
        【解决方案6】:

        SKAction.runBlock 的持续时间为 0.0。幸运的是,duration 属性是可变的。

        badMoveAction.duration = 2.0 应该延迟块足够长的时间,以便在块内的操作完成后运行。

        【讨论】:

        • 不幸的是,设置持续时间属性并没有改变任何东西。这对你有用吗?
        • 您是否还检查了 speed 属性设置为高于 0 的值?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-11
        • 2011-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多