Check if SCNNode SCNAction has completed

I created the world of SceneKit 3D mazes in which the player can move. Some of the movements, such as jumping, include moving the camera up and down while changing the viewing direction for a few seconds. During this time, I would like to ignore the user's clicks and clicks, which usually lead to other types of movements, such as turning and moving forward.

I could create a timer that matches the transition duration and sets Bool, but I was hoping for an easier way to check the SCNNode for the camera.

Is there an easy way to see that the SCNNode for the camera no longer triggers SCNAction for the transition, so I can add this logic before other click and swipe actions?

Or maybe there is a SCNAction that could install a Bool, which I could put at the beginning and end of my transition sequence?

Here is my transition code:

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) 

thanks

Greg

+5
source share
3 answers

I ended up using .customAction:

I added a class variable isJumping

then before the functional code I added:

  isJumping = true 

and added SCNAction:

  let jumpDone: SCNAction = SCNAction.customAction(duration: 0, action: {_,_ in self.isJumping = false}) 

then changed the sequence to:

  let sequenceLook = SCNAction.sequence([lookDown, noLook, lookBack, jumpDone]) 

then I just do if if is isJumping to check if the transition motion has completed.

+3
source

I think these are rather strange things.

The idea is strongly inspired (mainly 1: 1 mapping and “theft”) from cocos2D, where they were used as a way to avoid direct interaction with the game loop, state and time (sort of). Actions provide a kind of modularity and abstraction for processing both time and the creation and use of activity, as well as a very primitive approach to their conclusions.

You can see how similar actions of SpriteKit and SceneKit relate to original ideas when reading here: http://python.cocos2d.org/doc/programming_guide/actions.html

As a result of the diverging and developing nature of their creation, someone forgot to give them awareness of their condition, despite the fact that they affect some aspects of the state of the nodes. It would be almost ideal to understand that an Action object has a “running” state. But as far as I know, they do not.

Instead, you can check if Node has this action, and if so, then it assumes that the action is being performed.

Despite the fact that most of the actions do something for a long time, you also cannot ask them about how far they went through their duration, nor what values ​​they just fired, or sent forward. But there are actions that do this, in particular, to cope with the fact that this is absent for all actions, means that if you want it, you need to use a special action that provides this.

And such a recursion on and back for oneself for objects that were supposed to be innate is the most annoying part of the actions that was not thought out from the point of view of someone familiar with the timeline and key frames.

In general, and in the end: you can not query the status of the action, its progress or the value that it just used or will use the following. This is absolutely ridiculous.

I do not know how this was missed in the various evolutions of Actions ... they are the developers of time and modular activity. It seems so logical to include a report on the status and progress, which ... well, I don’t know ... it’s just strange that they don’t.

So, to answer your question:

  • You can use completion handlers that call some code when the action completes, set values ​​or call other functions, or clear stuff or whatever.

  • The actions of a sequence are in SCNAction.sequence ..., which is a way to start an action sequentially, and internally use some actions that trigger blocks of code when you want to trigger customization of what you need, when you need, in the sequence of actions. All could have been avoided if there were transparency in the values ​​that currently have actions of both time and properties ... but ...

  • You can also use some special actions that have some idea of ​​the value they are editing due to the changes made to them. I am familiar with the possibilities of installing float in SKEaseKit , but you can probably do it (if you are much better than an encoder than me) in SceneKit and SpriteKit. SKEaseKit hides a couple of action-changing values ​​that are very useful.

I use it, for example, like this, where this mess changes the value from 0 to 1 for the duration (time), in this case linearly, and each frame (hopefully) updates the .xScale Node on which this moveAction is executed:

 let growAction = SKEase.createFloatTween( start: 0, ender: 1, timer: time, easer: SKEase.getEaseFunction(.curveTypeLinear, easeType: .easeTypeOut), setterBlock: {(node, i) in node.xScale = i} ) 
+3
source

There is runAction(_:completionHandler:) where you can handle completion. See the documentation .

So, you can go through the completion block and check the necessary conditions here:

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

+1
source

Source: https://habr.com/ru/post/1260696/


All Articles