如何在 SpriteKit 中实现移动动作

问题描述 投票:0回答:1

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
之间的间距并不相同,因为计时模式并不总是线性的。任何想法将不胜感激。

swift animation sprite-kit implementation skaction
1个回答
0
投票

正如您所发现的,您的尝试不起作用的原因是因为只有一个

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
为前缀的动作类中。想必,动作如何执行的实际实现是写在这个方法中的。

该方法可能可以访问我们的代码无法访问的内容,例如自上一帧以来的时间,因此能够计算出节点应该移动多少。

© www.soinside.com 2019 - 2024. All rights reserved.