6

我创建了一个玩家可以在其中移动的 SceneKit 3D 迷宫世界。像跳跃这样的一些动作涉及上下移动相机,同时在几秒钟的时间内改变观察方向。在此期间,我想忽略用户的点击和滑动,这通常会导致其他类型的运动,如转身和向前移动。

我可以创建一个与跳跃持续时间相匹配的计时器并设置一个布尔值,但我希望有一种更简单的方法来检查相机的 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)

谢谢

格雷格

4

3 回答 3

4

我认为,行动是非常奇怪的事情。

这个想法受到 cocos2D 的极大启发(基本上是 1:1 映射和“盗窃”),它们被用作避免直接与游戏循环、状态和时间(有点)交互的一种方式。动作为处理时间以及活动的创建和调用提供了一种模块化和抽象,以及对其结论的非常原始的处理。

在这里阅读时,您可以看到 SpriteKit 和 SceneKit 的 Actions 与原始想法有多么相似:http: //python.cocos2d.org/doc/programming_guide/actions.html

由于他们创造的不同和不断发展的性质,尽管事实上他们影响了节点状态的某些方面,但有些人忘记了让他们了解自己的状态。动作对象具有“运行”状态几乎是完美的。但据我所知,他们没有。

相反,您可以检查节点是否具有给定的动作,如果有,则推断该动作正在运行。

尽管大多数动作在一段时间内都在做某事,但您也无法查询它们在持续时间内完成了多远,也无法查询它们刚刚触发或接下来将发送的值。但是有一些动作可以做到这一点,特别是为了应对所有动作都缺少这个的事实,这意味着如果你想要这个,你需要使用一个提供这个的特殊动作。

对于本应与生俱来的设施,这种自上而下的递归是动作中最烦人的部分,没有从熟悉时间线和关键帧的人的角度考虑。

从整体上来说,总结一下:你不能查询一个动作的状态,也不能查询它的进度,或者它刚刚做的或接下来将要使用的值。这绝对是荒谬的。

我不知道这在 Actions 的各种演变中是如何被忽视的……他们是时间管理和模块化活动的创造者。在其中包含状态和进度报告似乎很合乎逻辑,以至于……嗯,我不知道……奇怪的是他们没有。

所以,回答你的问题:

  1. 您可以使用完成处理程序,即在操作完成时调用某些代码,设置值或调用其他函数或清理内容,或任何您想要的..

  2. 序列动作,在 SCNAction.sequence 中......这是一种让动作按顺序运行的方法,并在内部使用一些动作,当你需要时运行代码块,调用设置你需要的东西,当你需要时,在动作序列。如果操作当前具有的时间和属性值具有透明度,则所有这些都可以避免......但是......

  3. 您还可以使用少数特殊操作,这些操作通过对它们所做的更改来了解它们正在编辑的值。我只熟悉 SKEaseKit 中的浮点设置功能但您可能可以在 SceneKit 和 SpriteKit 中执行此操作(如果您是比我更好的编码器)。SKEaseKit 隐藏了几个非常有用的改变值的动作。

例如,我像这样使用它,其中这个混乱在持续时间(时间)内将值从 0 更改为 1,在这种情况下是线性的,并且每一帧(希望)更新这个 growAction 所在节点的 .xScale跑:

let growAction = SKEase.createFloatTween(
                start: 0,
                ender: 1,
                timer: time,
                easer: SKEase.getEaseFunction(.curveTypeLinear,
             easeType: .easeTypeOut),
          setterBlock: {(node, i) in
            node.xScale = i}
            )
于 2016-12-02T14:59:02.007 回答
4

我最终使用了 .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])

然后我只做一个 if on isJumping 来查看跳跃动作是否完成。

于 2016-12-08T03:09:56.140 回答
2

有一个runAction(_:completionHandler:),您可以在其中处理完成。请参阅文档

因此,您可以在此处传递完成块并检查必要条件:

mazeScene.mazeCamera.runAction(sequenceJump) { print("Sequence jump is completed") }

于 2017-08-01T09:47:48.443 回答