首页 文章

检查 SCNNode SCNAction 是否完成

提问于
浏览
6

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

谢谢

格雷格

3 回答

  • 4

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

    这个想法受到了很大的启发(基本上是 1:1 映射和'盗窃')来自 cocos2D,在那里它们被用来避免直接与 game-loop,状态和时间(某种程度)进行交互。动作提供了一种模块化和抽象,用于处理时间,创建和调用活动,以及对结论的非常原始的处理。

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

    由于他们创造的不同和不断演变的性质,有人忘记让他们意识到自己的状态,尽管他们影响节点状态的某些方面。如果 Action 对象具有“运行”状态,那将近乎完美。但就我所知,他们没有。

    相反,您可以检查 Node 是否具有给定的 Action,如果有,则推断 Action 正在运行。

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

    对于应该是天生的设施而言,这种递归和回归自我是动作中最烦人的部分,而不是从熟悉时间轴和关键帧的人的角度进行思考。

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

    我不知道在各种行动的演变中如何忽视这一点......他们是时间管理和模块化活动的创造者。将状态和进度报告纳入其中似乎是合乎逻辑的......好吧,我不知道......只是奇怪的是他们没有。

    那么,回答你的问题:

    • 您可以使用完成处理程序,它可以在 Action 完成时调用某些代码,设置值或调用其他函数或清理内容,或者您想要的任何内容。

    • 序列动作,在一个 SCNAction.sequence ...中,这是一种顺序运行 Action 的方法,并且在内部使用一些运行代码块的动作,在需要时调用你需要的东西,按顺序动作。如果行动目前对时间和财产的价值具有透明度,那么所有这些都可以避免......但......

    • 您还可以使用一些特殊操作,这些操作可以通过对它们所做的更改来了解它们正在编辑的值。我只熟悉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}
                )
    
  • 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,看看跳跃运动是否已经完成。

  • 1

    有一个runAction(_:completionHandler:),你可以在那里完成。见文件

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

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

相关问题