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
变量在同一节点上运行时被写入两次而中断(我知道即使在不同节点上运行时也存在这个问题。但是,我设法解决了这个问题使用键值对的问题。所以这不是问题)。在这种情况下,timePassed
将不准确。
有谁知道我如何实现这个自定义操作并让它工作,即使同一个实例在不同时间在同一个节点上运行?请记住,
elapsedTime
之间的间距并不相同,因为计时模式并不总是线性的。任何想法将不胜感激。
正如您所发现的,您的尝试不起作用的原因是因为只有一个
lastElapsedTime
。一旦第二个动作(0.25 秒后开始的那个)开始,单个 lastElapsedTime
变量就会被第二个动作的经过时间覆盖。
换句话说,你的闭包同时输入了第一个和第二个动作的经过时间,你无法区分它是哪个。
解决这个问题的一个简单方法是不要重复使用
move
动作两次。创建其中两个。
let move1 = SKAction.moveX(by: 5.0, duration: 1)
let move2 = SKAction.moveX(by: 5.0, duration: 1)
node.run(.group[move1, .sequence[.wait(forDuration: 0.25), move2]])
现在有两个
lastElapsedTime
s.
内置的
move
不是使用customAction
实现的。比较这些方法创建的类型:
type(of: SKAction.moveBy(x: 100, y: 100, duration: 1)) // SKMove
type(of: SKAction.customAction(withDuration: 1, actionBlock: { _, _ in })) // SKCustomAction
您可以在此处找到它们的标题:SKMove.h SKCustomAction.h。他们分别有
SKCMove
和SKCCustomAction
作为他们的成员。
似乎每个内置动作都有这样一个类。有可能
SKAction
中的工厂只是调用这些类中的工厂。
我还发现
SKCMove
里面有个方法叫cpp_updateWithTargetForTime(SKCNode*, double)
。这似乎也出现在所有以SKC
为前缀的动作类中。想必,动作如何执行的实际实现是写在这个方法中的。
该方法可能可以访问我们的代码无法访问的内容,例如自上一帧以来的时间,因此能够计算出节点应该移动多少。