Composite Actions in Scene Editor

Hi all 🙂!


I’m really excited about the opportunities of designing actions in the Scene/Actions Editor and having them as external resources, as displayed in the WWDC 2015 video What's New in SpriteKit. I’ve been playing around with the stuff that they’re showing, but I’ve come across a pretty glaring hurdle.


In the video they are animating a character that’s a single node, but how does it work when your character is composition of nodes (e.g. torso, legs, arms, etc.) instead of a single one?


Here’s an example:


I have a cartoony spider character that is composed of eight legs of the same leg node, a torso node and an eyes node. Overall I need the spider to have two animations: Idle and Tap. Idle will display the spider bobbing around with all eight legs wiggling at different times and it’s eyes occasionally blinking. The tap animation will include some more non-uniform complex movement.


So first of all, I’ve built the spider in a separate Character.sks file, where each component node that I intend to animate is a child of the spider parent node. That .sks file is referenced in a SKReferenceNode in GameScene, so it shows up in-game without issues. Since the Scene Editor timeline of Character.sks runs on init, I can animate the Idle animations of each element on that timeline, make them all loop indefinitely and that’ll be it. However, adding the Tap animation then becomes an issue. You can’t really animate it on the timeline of the .sks file, because it doesn’t allow states, like an action file would. And you can’t really outsource that action composition to an action file, since actions are node-specific and contain no hierarchy.


GameScene.swift

-> GameScene.sks

-> SKReferenceNode "Character"

-> Character.sks

-> SKNode "Spider"

-> SKSpriteNode "Torso"

-> ...


I feel like there must be a way to do this smarter. Being only able to animate single nodes on the timeline seems overly restrictive, though I understand why there’s no current solution, since code doesn’t allow action compositions either for multiple nodes. Currently as I see it, if the character in your crazy-cool RPG game is composed of animating elements, you in fact won’t be able to design jump, run, shoot, etc. animations as they show it in the WWDC video.


1) One solution would be to add animations (Idle, Tap) to each sub-node that is animating, and then on the Character.sks timeline you only attach the Idle ones. Then when you want to activate the Tap animations, GameScene.swift would tell the Character.sks’s children to play the Tap animation. Thing is, I don’t know how to reference the Character.sks child elements from already one SKReferenceNode.


2) Another thing I’m trying now is to create a class for Character. That way at least GameScene could tell Character to “initiate Tap animations” and then Character could access those elements to make them animate. The problem here is I don’t know how to connect the Character.sks file to the Character class. I could of course easily build the character and animate it in code, but that just completely makes the Scene Editor irrelevant.


Can anyone shine some light onto this dilemma? Thanks!

Regarding animations of sub nodes:

What you could do is create a scene for each animation(e.g. "Spider_Idle"). In a scene you can define animations for every node so you could do everything you want.

I would definitely cache these scenes if you notice any performance issues but I think it should work and offers a lot of cool features, for example you could easily add particle effects with this approach.


Regarding your last question : You can just enter the class name in the "Custom Class" field of the scene editor. With that approach you can easily use custom classes for your nodes.

Thanks for a quick response! Stuff doesn't seem to move very fast on these forums, so it's almost surprising to get a response 😁.


I already tried separating the two into different .sks files. The problems that arose were pretty bad. For one, you would need to cache all the readily-available animations as separate .sks files, meaning the node count on scene would grow immensely with each animation. If you didn't cache them, you would need to create them when the action is needed, which would be terrible for performance and just clearly against all best practices. Whether you cache or not, it would still mean you would need to build the character into each .sks file. Imagine a game that has 20+ animations for a character, and your boss says "move the head a bit to the left" 😀. You'd have to do that for each of the 20+ files. Not exactly the object-oriented approach Apple is trying to promote. In addition to all this, animations on .sks timelines run on initiation. To control when an animation is supposed to start, you would need to initiate that .sks at that specific time. Not gonna work unfortunately...


I think I'll try and connect the class to the .sks file. It seems like a much more reasonable solution. Thanks for that custom class tidbit. I'll try that. With a separate class with an adjoined .sks file at least I could build the Idle animation right on the timeline, and then have a function that tells all elements to switch to their respective Tap animations at the same time. This would be a fair option, but I still can't design any of the animations, other than Idle, on the timeline. I really hope Apple provides some functionality for this, and doesn't wait until WWDC 2016 to put it in 😀.


I'll report my findings!

Update: Connecting the class to the .sks didn't work at least yet. Even if I add the custom class to the Character.sks file's inspector area, it doesn't make a difference. I think this is because my class is an SKNode, while the .sks is a scene file for SKScene. I actually tried changing my class to be a sub-class of SKScene, but that didn't work, supposedly because you can't put a SKScene within another SKScene (GameScene -> Character). Even if you could somehow embed it like that, Xcode doesn't allow changing the position of an SKScene, so there wouldn't be much control over it.


So the question is now: Is it possible to use an .sks scene file as the interface for an SKNode class?


The way I'm going to continue from here is to just do it all in code. I could build the character in code and identically match the elements into .sks files for each animation (Character-Idle.sks and Character-Tap.sks). Then I would at least be able to design the animations in the .sks files and convert them manually to code. At the very least, singular node-specific animations can be outsourced to animation files, so then I'm not really making animations in code, as much as just referencing them in code. Still, very far from ideal.


One thing I'm stressing is that I'm supposed to start a paid project soon that's pretty animation-heavy, and I have an animator who needs an interface to make the animations. I'm planning to go with SpriteKit, but dang Unity is looking like a better option in this case.

1. You don't need to keep each action in a separate SKS file.

2. While improving your acitons you can test them on any node (check WWDC video) by attaching actions to nodes.

3. You can make reference node no build a custum class in a scene.


I am sure your task may be easily done with the current SpriteKit technology, but you obviously have to understand the tool better.

Thanks for your reply!


1. I don't intend to keep each action in a separate .sks file. The idea is to have action compositions in separate .sks files. In this case I mean "composition" as a collection of actions as targets of multiple nodes. The actions contained in an action file only target one node at a time, which is hugely limited.


2. As far as I can see, this statement is incorrect. You can't test an animation on any node, but rather on any .sks file in the project. In the video you can see that they have a PlayerPreview.sks file that is being used to view the animation. The player node is separate. This scene file only includes one node, which is the character shown in the video, so that's why it works. Try to run an action on a scene file with multiple nodes, you're gonna run into trouble. So the preview ability would only work for single nodes, and only if they're packaged into their own .sks files. This system is not gonna be good when you have a lot of embedded animating nodes. You'd need an .sks preview for each node (a spider leg, in my example), and even then you would only see the leg animating, not the entire spider composition of actions.


3. I already have a reference node in GameScene to connect Character.sks to it. The problem is when you have a written custom class, such as Character.swift, that is a sub-class of SKNode instead of SKScene, I can't find out if it's possible to connect the SKNode based class to its own .sks file.


Judging by the many UI limitations of Scene Editor, such as the inability to move around elements on the timeline or delete them, it's pretty clear that the tool is still in a pretty early stage. So I'm not that surprised they haven't thought about these more complex action comps that I'm trying to do. I've been at this for two weeks now and I haven't seen one tutorial or solution yet to tackle the issue. The stuff shown by Apple themselves in WWDC videos, for example, are very simplistic cases. So in that sense it doesn't seem to be my lack of "understanding", but rather everyone's lack.


Also, I can understand that lumping actions together smartly isn't so self-evident, because there's no tool to do that in code either. The code equivalent of a composition would be a custom method that runs the animations on different nodes at the same time. That's what I'm going for now, in fact.

1. "The actions contained in an action file only target one node at a time" -- no, they don't.

2. You can assign action to any node in preview scene

3. If you can't do it directly in UI you can make a scene file and then later in code


SKScene.unarchiveFromFile(fileName) as? MyClass


Or just create your node in SKS file and later copy the contents of particular node to your node.

You are right, with a complex character it doesn't make sense to use one scene per animation.


This is a summary of what I am currently doing:

The player is composed like that:

- It is in a special scene with some child nodes like the weapon, the player node uses the "Custom Class" feature(Yes, it works) to automatically use this class when loading the scene(I don't set it for the scene itself, I set it for the node. You just need to subclass this nodetype and it should work, you also need to make sure that the module name is correct)


- The animations of the character are in an action file(SKS), one per character. I play them via code using the name of the action


The weapons are also in a special scene, same approach like the character


- For level parts(For example a hill with some other elements) I use reference nodes and place these references inside my levels. So it is easy to adjust them afterwards


This is the code I use to load nodes from a file with a specific custom class(That class must be set in the scene editor too!):

extension SKNode
{
    /
    /
    public static func loadFromFile<NodeType : SKNode>(fileName : String, withNodeType nodeType : NodeType.Type) -> NodeType?
    {
        if fileName.isEmpty
        {
            return nil
        }
        guard let scene = SKScene(fileNamed: fileName) else
        {
            return nil
        }
        if scene.children.count == 0
        {
            if let sceneNode = scene as? NodeType
            {
                return sceneNode
            }
            return nil
        }
        guard let node = scene.children.first as? NodeType  else
        {
            return nil
        }
        let result = node.copy() as! NodeType
        return result
    }


You can use it like that:

let character = SKNode.loadFromFile("Character_01.sks", withNodeType: CharacterNode.self)


I hope this helps.


For your setup with composite animations I'm not sure how to solve this in an efficient way, but I think it is possible with SpriteKit.

Composite Actions in Scene Editor
 
 
Q