【问题标题】:SKAction runAction does not execute completion blockSKAction runAction 不执行完成块
【发布时间】:2015-06-02 14:34:01
【问题描述】:

SKSpriteNodeSKNode 的子代,并且出于存储目的放置在 SKSpriteNode 的数组中。

这个SKSpriteNode 使用动画删除。在这个动画的最后会执行一个完成块来执行一些语句......

删除必须同时发生在 SKSpriteNode 父级和数组中。根据这 2 次删除的顺序,结果是否正确:

  • 如果 SKSpriteNode 从数组 1/ 中删除,然后从 SKNode 父数组中删除 2/,则执行完成块。
  • 如果逆序,1/ SKNode parent 的 then 2/ 数组,完成块不被执行。

为什么会有这种行为?

for position in listOfPositions {

   theSprite:SKSpriteNode = theGrid[position]

   /// the SKSpriteNode referenced by theSprite :
   /// - belongs to an array of SKSpriteNode: theGrid
   /// - belongs to a SKNode: theGameLayer
   ///
   /// In other words this SKSpriteNode is referenced twice
   ///

   let theActions = SKAction.sequence([
      /// Some actions here
      /// ...

      /// Remove theSprite from the Grid
      /// - position is an instance of a structure of my own
      /// - theGrid is accessed via a subscript
      ///
      SKAction.runBlock({self.theGrid[position] = nil}),

      /// remove theSprite from it's parent
      SKAction.removeFromParent(),
   ])

   theSprite.runAction(theActions,completion:{NSLog("Deleted")})
}

显示完成消息。

现在如果将removeFromParent 放在remove from theGrid 操作之前,如下所示,完成不会执行:

let theActions = SKAction.sequence([
   /// Some actions here
   /// ...

   /// remove theSprite from it's parent
   SKAction.removeFromParent(),

   /// remove theSprite from the Grid
   SKAction.runBlock({self.theGrid[position] = nil}),
])

【问题讨论】:

  • 试试这个:SKAction.runBlock({self.theGrid[self.position] = nil}),
  • self 添加到position 与这种情况无关:它会导致编译错误。
  • 我建议写 self 因为我认为这就像一个范围问题。
  • 您的建议是可以的,但不适用于这种情况,因为position 的范围是正确的。它似乎与removeFromParent 如何管理完成块的执行更相关。
  • 我已通过添加解释来更新问题以更好地理解上下文。

标签: swift sprite-kit


【解决方案1】:

来自SKAction Reference

SKAction 对象是由节点执行的动作 场景(SKScene)...当场景处理其节点时,与这些节点相关的动作 节点被评估。

换句话说,当且仅当该节点在场景中时,该节点的动作才会运行。通过调用removeFromParent,您将节点从场景中移除,runBlock 动作永远不会被调用(因为节点不再在场景中),因此序列永远不会完成。由于序列未完成,因此不会调用完成块。

为了安全起见,我建议将 removeFromParent 调用移至完成块。这样的事情感觉更安全:

for position in listOfPositions {

   let theSprite: SKSpriteNode = theGrid[position]

   /// the SKSpriteNode referenced by theSprite :
   /// - belongs to an array of SKSpriteNode: theGrid
   /// - belongs to a SKNode: theGameLayer
   ///
   /// In other words this SKSpriteNode is referenced twice
   ///

   let theActions = SKAction.sequence([
      /// Some actions here
      /// ...

      /// Remove theSprite from the Grid
      /// - position is an instance of a structure of my own
      /// - theGrid is accessed via a subscript
      ///
      SKAction.runBlock({self.theGrid[position] = nil})
   ])

   theSprite.runAction(theActions) {
      /// remove theSprite from it's parent
      /// Might need to weakly reference self here
      theSprite.removeFromParent(),
      NSLog("Deleted")
   }
}

TL;DR
序列未完成,因此不会调用序列的完成块。

【讨论】:

  • 虽然这段代码对我来说是一个很好的改进,但我对解释很不满意。因为 1/ 您引用的引用没有明确包含对完成使用的任何警告,2/ 因为完成是一个块,即使它所附加的 SKNode 已从场景中移除,它也可以执行,并且 3/ 这不能解释行为在这个问题中描述。
  • 1.没错,Apple 可以在描述此行为的文档中添加注释。即使它非常直观,它也会很有帮助。 2. 如果说SKAction 从未成功完成,你为什么期望SKActioncompletion 块被调用。 3. 它准确解释了为什么不调用完成块,这是您在问题中描述的行为。我很想听听你为什么认为这个答案不能解释这种行为。
  • 我的问题可能写得不好:抱歉。感谢您花时间回答我。在问题中,完成块不是在一种情况下调用,第二种情况是,当removeFromParent 操作放置在从theGrid 操作中删除之前。在另一种情况下,它被称为遵循您的解释,它根本不应该被执行。正如文档中所写,完成块被称为“当操作完成时”
  • "动作完成时调用完成块。"它没有在第二个中完成,您将 removeFromParent 放在从 theGrid 操作中删除之前。我不知道我能清楚多少:(
  • 从父级移除导致序列中的其余动作不被调用,因为theSprite 不再在场景中。该序列未完成,因此不应调用完成块。这是一个完成块,而不是一个您会获得成功/失败状态的结果块。完成块在逻辑上仅在成功完成时才被调用。
猜你喜欢
  • 2015-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多