SpriteKit有一个名为.moveBy(x:y:duration:)
的动作,我想自己尝试并实现这个动作,发现它出奇的困难。
static func moveX(by amount: CGFloat, duration: TimeInterval) -> SKAction {
let speed = amount / duration
var lastElapsedTime = 0.0
return .customAction(withDuration: duration) { node, elapsedTime in
let timePassed = elapsedTime - lastElapsedTime
node.position.x += timePassed * speed
lastElapsedTime = elapsedTime
}
}
最初的实现看似简单,但是,当您考虑到如果这个SKAction
的同一个示例被同一个节点执行多次,但开始时间不同,会发生什么情况时,问题就开始出现了。
例如:
let move = SKAction.moveX(by: 5.0, duration: 1)
node.run(.group[move, .sequence[.wait(forDuration: 0.25), move]])
在这种情况下,lastElapsedTime
变量在同一个节点上运行时被写入两次时会中断 (我知道即使在不同的节点上运行也会存在这个问题。但是,我通过使用Key-Value对来解决这个问题。所以这不是一个问题)。在这种情况下,timePassed
将不准确。
有没有人知道如何实现这个自定义操作,即使同一个示例在不同的时间运行在同一个节点上也能正常工作?请记住,elapsedTime
之间的间隔并不相同,因为计时模式并不总是线性的。任何想法都将受到极大的赞赏。
1条答案
按热度按时间voase2hg1#
正如你所发现的,你的尝试不起作用的原因是因为只有一个
lastElapsedTime
。一旦第二个动作(0.25秒后开始的动作)开始,单个lastElapsedTime
变量就会被第二个动作的运行时间覆盖。换句话说,闭包同时接收第一个和第二个操作的运行时间,您无法区分它是哪一个。
解决这个问题的一个简单方法是不要重复使用
move
操作两次。现在有两个
lastElapsedTime
。内置的
move
不是使用customAction
实现的。比较这些方法创建的类型:你可以在这里找到这些标题:SKMove.hSKCustomAction.h。它们分别有
SKCMove
和SKCCustomAction
作为其成员。似乎每个内置动作都有这样一个类,
SKAction
中的工厂可能只是调用了这些类中的工厂。我还发现在
SKCMove
中有一个名为cpp_updateWithTargetForTime(SKCNode*, double)
的方法。这似乎也存在于所有以SKC
为前缀的action类中。据推测,如何执行action的实际实现都写在这个方法中。该方法可能可以访问我们的代码无法访问的内容,包括但不限于自上一帧以来的时间,因此能够计算节点应该移动多少。
从技术上讲,您也可以获得自场景中最后一帧以来的时间,如下所示:
然后,您可以编写一个
moveX
方法,其工作方式与内置方法非常相似,但仅限于MyScene
。