检查 SCNNode SCNAction 是否完成

Check if SCNNode SCNAction is finished

我创建了一个玩家可以在其中移动的 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)

谢谢

格雷格

我认为动作是很奇怪的东西。

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

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

由于他们的创造具有不同和不断发展的性质,有人忘记让他们了解自己的状态,尽管他们会影响节点状态的某些方面。如果 Action 对象具有 "running" 状态,这将近乎完美。但据我所知,他们没有。

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

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

而这种本应与生俱来的设施的递归和回归自我是动作中最令人讨厌的部分,没有从熟悉时间轴和关键帧的人的角度考虑。

整体而言,总而言之:您无法查询操作的状态,也无法查询其进度,也无法查询它刚刚执行或将要使用的值。这绝对是荒谬的。

我不知道这在 Actions 的各种演变中是如何被忽视的……他们是时间管理和模块化 activity 创作者。在其中包含状态和进度报告似乎是合乎逻辑的......好吧,我不知道......只是奇怪他们没有。

所以,回答你的问题:

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

  2. 顺序操作,在 SCNAction.sequence... 中,这是一种顺序执行操作 运行 的方法,并且在内部使用一些 运行 阻止的操作当你想要的时候,代码会调用设置你需要的东西,当你需要的时候,在动作的序列中。如果 Actions 当前具有的时间和属性的值是透明的,那么所有这些都可以避免……但是……

  3. 您还可以使用一些特殊操作,这些操作由于对它们所做的更改而对它们正在编辑的值有一定的了解。我只熟悉 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}
            )

我最终使用了 .customAction:

我添加了一个 class 变量 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 来查看跳跃运动是否完成。

有一个 runAction(_:completionHandler:),您可以在其中处理完成。见 documentation.

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

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