1

I'm writing an application that displays chemical reactions and molecules in 3D. I read in all the values and positions of each atom from a text file and I am creating each atom shape with SCNSpheres. I have all the other values I need read in properly, but I can't figure out how to add keyframe animations to each node object in my scene.

I set up the molecules like this in ViewController.swift

func makeAtom(atomName: String, coords: [Double], scene: SCNScene) {
        guard let radius = atomRadii[atomName]?.atomicRadius else { return }
        
        atoms.append(Atom(name: atomName, x: coords[0], y: coords[1], z: coords[2], radius: radius, positions: []))
        
        let atomGeometry = SCNSphere(radius: CGFloat(radius))
        let atomNode = SCNNode(geometry: atomGeometry)
        atomNode.position = SCNVector3(coords[0], coords[1], coords[2])
        scene.rootNode.addChildNode(atomNode)
        
        atomNodes.append(atomNode)
    }

I know that the CAKeyframeAnimations are supposed to be set up like this

let animation = CAKeyframeAnimation()
animation.keyPath = "position.y"
animation.values = [0, 300, 0]
animation.keyTimes = [0, 0.5, 1]
animation.duration = 2
animation.isAdditive = true

vw.layer.add(animation, forKey: "move")

I just don't know where I should be declaring these animations and how the layers factor into all this. What layer should I be adding the animations to? And how can I trigger them to play? I've been searching all over the internet for help with this but I can't find anything that just shows a simple implementation.

I can provide more code if need be, I'm pretty new to StackOverflow and want to make sure I'm doing this right.

Gabe R
  • 82
  • 8

2 Answers2

1

You can do it different ways, but I like this method: 58001288 (my answer here) as you can pre-build some animations using scenekit and then run them as a sequence.

James P
  • 4,786
  • 2
  • 35
  • 52
Voltan
  • 1,170
  • 1
  • 7
  • 16
  • This works great, my only question now is I'm wondering if there's a way to access the current action being run? For instance if I were to push a button onscreen, at that instant could I access the current action being performed? – Gabe R Sep 02 '20 at 00:27
0

Per the comment.. needed more room.

A sequence is a fixed thing. You can start, repeat, and stop it. However, it's hard to interact with it during phases.

If you really need to do that, then one way is to break up your sequence into its parts and call the next one yourself after a completion handler of the current one. I keep an array and a counter so that I know where I am. So basically it's just a queue of actions that I manage - if I'm on a certain step and the button is pressed, then I can cancel all current actions, set the desired effect, and restart it.

Edit:

The completion handler calls itself at the end of the function and advances its own array count so that the next one in the list can be called. This is obviously a bit dangerous, so I would use sparingly, but that's how I did it. I started mine on a timer, then don't forget to clean it up. I had a global GAME_ACTIVE switch and within the code I checked for it before calling myself again.

Edit2: This is actually a moveTo, but it's still just a custom set of SCNActions that calls itself when complete based on duration so that it immediately goes to the next one without a delay.

func moveTo()
    {
        let vPanelName = moves[moveCount]
        let vLaneNode = grid.gridPanels[vPanelName]!.laneNodes[lane]
        let vAction = SCNAction.move(to: vLaneNode.presentation.position, duration: TimeInterval(data.getAttackSpeed(vGameType: gameType)))
        
        node.runAction(vAction, completionHandler:
            {
                self.moveCount += 1
                if(self.moveCount >= self.moves.count - 1)
                {
                    self.killMe(vRealKill: false)
                    return
                }
                else
                {
                    self.moveTo()
                }
        })
    }
Voltan
  • 1,170
  • 1
  • 7
  • 16
  • Would you mind sharing how you did that? I have the array of SCNActions I want to perform (not as a sequence) and I get completion handlers, but I'm not sure how to code it so that it keeps calling the next one. I'm guessing it has to be in a loop but I'm just not sure what it would look like. – Gabe R Sep 03 '20 at 22:00
  • Sorry to keep coming back to this, but the code for the animation in the completion handler is being reached but it won't actually execute the animation. Am I missing something obvious? – Gabe R Sep 15 '20 at 19:50
  • Can you share the code you used? This is not trivial - IMO, it's rather tricky but that's the only way I know to get to a level where you can actually interact with the actions in the way that you described. Really, what you doing there is calling each action yourself one at a time and regulating the changes via a timer. – Voltan Sep 16 '20 at 16:28
  • https://stackoverflow.com/questions/63910293/calling-consecutive-animations-in-swift-with-completion-handler – Gabe R Sep 21 '20 at 14:45
  • I think I'm doing it correctly, because it will work for a little bit, but the problem is that the atom animations have hundreds to thousands of steps, and I think the app runs out of memory after a while, and the whole thing freezes up. Does that seem right or am I potentially doing something else wrong? – Gabe R Sep 24 '20 at 20:41
  • Hundreds of thousands... Yeah - that is a possibility. Pretty ez to check - just click the debug window and see what the memory looks like while the app is running. – Voltan Sep 25 '20 at 14:21