我创造了一个玩家可以移动的SceneKit 3D迷宫世界。像跳跃这样的一些动作涉及在几秒钟的时间段内改变观察方向时上下移动相机。在此期间,我想忽略用户的点击和滑动,这通常会导致其他类型的动作,如转弯和前进。
我可以创建一个与跳转持续时间匹配的计时器并设置Bool,但我希望能够以更简单的方式检查摄像机的SCNNode。
是否有一种简单的方法可以查看相机的SCNNode是否不再运行SCNAction进行跳转,以便我可以在其他点按和滑动操作前添加此逻辑?
或许有一个SCNAction可以设置我可以在跳转序列的开始和结束时放置的Bool?
这是我的跳转代码:
let jumpUp: SCNAction = SCNAction.move(to: SCNVector3Make(Float(Int(-yPos)), Float(Int(xPos)), jumpHeight), duration: jumpTime)
let jumpAppex: SCNAction = SCNAction.wait(duration: jumpWaitTime)
let fallDown: SCNAction = SCNAction.move(to: SCNVector3Make(Float(Int(-yPos)), Float(Int(xPos)), cameraHeight), duration: jumpTime)
var lookDown: SCNAction = SCNAction.rotateTo(x: 0, y: 0, z: CGFloat(π), duration: jumpTurnTime)
let noLook: SCNAction = SCNAction.wait(duration: jumpTime*2.0)
var lookBack: SCNAction = SCNAction.rotateTo(x: 0, y: 0, z: 0, duration: jumpTurnTime)
switch playerDirection.direction
{
case .south:
lookDown = SCNAction.rotateTo(x: 0, y: 0, z: CGFloat(southZ), duration: jumpTurnTime)
lookBack = SCNAction.rotateTo(x: CGFloat(π/2), y: 0, z: CGFloat(southZ), duration: jumpTurnTime)
case .north:
lookDown = SCNAction.rotateTo(x: 0, y: 0, z: CGFloat(northZ), duration: jumpTurnTime)
lookBack = SCNAction.rotateTo(x: CGFloat(π/2), y: 0, z: CGFloat(northZ), duration: jumpTurnTime)
case .east:
lookDown = SCNAction.rotateTo(x: 0, y: 0, z: CGFloat(eastZ), duration: jumpTurnTime)
lookBack = SCNAction.rotateTo(x: CGFloat(π/2), y: 0, z: CGFloat(eastZ), duration: jumpTurnTime)
case .west:
lookDown = SCNAction.rotateTo(x: 0, y: 0, z: CGFloat(westZ), duration: jumpTurnTime)
lookBack = SCNAction.rotateTo(x: CGFloat(π/2), y: 0, z: CGFloat(westZ), duration: jumpTurnTime)
}
let sequenceJump = SCNAction.sequence([jumpUp, jumpAppex, fallDown])
let sequenceLook = SCNAction.sequence([lookDown, noLook, lookBack])
mazeScene.mazeCamera.runAction(sequenceJump)
mazeScene.mazeCamera.runAction(sequenceLook)
由于
格雷格
答案 0 :(得分:4)
我认为行动是非常奇怪的事情。
这个想法受到了很大的启发(基本上是1:1的映射和'盗窃')来自cocos2D,在那里它们被用来避免直接与游戏循环,状态和时间(某种程度)进行交互。动作提供了一种模块化和抽象,用于处理时间,创建和调用活动,以及对结论的非常原始的处理。
在这里阅读时,您可以看到SpriteKit和SceneKit的动作与原创想法有多相似:http://python.cocos2d.org/doc/programming_guide/actions.html
由于他们创造的不同和不断演变的性质,有人忘记让他们意识到自己的状态,尽管他们影响了节点状态的某些方面。如果Action对象具有“运行”状态,那将近乎完美。但就我所知,他们没有。
相反,您可以检查Node是否具有给定的Action,如果有,则推断出Action正在运行。
尽管大多数行动都是在一段时间内做某事,但你也无法询问他们在整个持续时间内完成了多少事情,也没有询问他们刚刚解雇或接下来要发送的值。但是有一些行为可以做到这一点,特别是为了应对所有动作都缺失的事实意味着如果你想要这个,你需要使用一个提供此功能的特殊动作。
对于应该是天生的设施而言,这种递归和回归自我是动作中最令人讨厌的部分,而不是从熟悉时间轴和关键帧的人的角度进行思考。
整体而言,总结:您无法查询某个操作的状态,也无法查询其进度或它刚刚执行或将要使用的值。这绝对是荒谬的。
我不知道在各种行动演变中如何忽视这一点......他们是时间管理和模块化活动的创造者。将状态和进度报告纳入其中似乎是合乎逻辑的......好吧,我不知道......只是奇怪的是他们没有。
所以,回答你的问题:
您可以使用完成处理程序,即在Action完成时调用某些代码,设置值或调用其他函数或清理内容,或者您想要的任何内容。
SCNAction.sequence中的序列操作...这是一种顺序运行Action的方法,并且在内部使用一些在需要时运行代码块的Actions,调用设置所需的内容,当你需要时,按照动作的顺序。如果行动目前对时间和财产的价值具有透明度,那么所有这些都可以避免......但是......
您还可以使用少数特殊操作,这些操作通过对其进行的更改来识别他们正在编辑的值。我只熟悉SKEaseKit中的浮点设置能力,但你可能会在SceneKit和SpriteKit中做到这一点(如果你是一个比我好得多的编码器)。 SKEaseKit隐藏了一些非常有用的值更改操作。
我使用它,例如,像这样,其中这个混乱在一个持续时间(时间)内改变0到1的值,在这种情况下是线性的,并且每个帧(希望)更新节点的.xScale。这个growAction运行:
let growAction = SKEase.createFloatTween(
start: 0,
ender: 1,
timer: time,
easer: SKEase.getEaseFunction(.curveTypeLinear,
easeType: .easeTypeOut),
setterBlock: {(node, i) in
node.xScale = i}
)
答案 1 :(得分:3)
我最终使用了.customAction:
我添加了一个类变量isJumping
然后在我添加的功能代码前面:
isJumping = true
并添加了SCNAction:
let jumpDone: SCNAction = SCNAction.customAction(duration: 0, action: {_,_ in self.isJumping = false})
然后将序列更改为:
let sequenceLook = SCNAction.sequence([lookDown, noLook, lookBack, jumpDone])
然后我只是在isJumping上做一个if,看看跳跃运动是否已经完成。
答案 2 :(得分:1)
有一个runAction(_:completionHandler:)
,您可以在其中处理完成情况。请参阅documentation。
因此,您可以在此处传递完成块并检查必要条件:
mazeScene.mazeCamera.runAction(sequenceJump) {
print("Sequence jump is completed")
}