3
\$\begingroup\$

I’m working in Objective-C, SKSpriteKit and am trying to create a class which will work in both OS X and iOS for flexibility.

Recently, I asked the question which asked if there was a way to create a method in a new class which automatically ran itself every frame. I was answered that it would be a good idea to use CADisplayLink. However, I soon realised I could not, as it is exclusive to iOS — and I had forgotten to specify that I wanted this class to be usable in both platforms. This was quite a surprise, and I had to dig deep into the system’s .h files to be sure this was true.

I also forgot to mention that I was using SKSpriteKit and that my class was a subclass of SKSpriteNode in the question. So I then tried to add the method which needed to be ran every frame using SKActions and performSelector:. However, the end result was undesirable, as I observed 2 identical nodes of this class (called PhysicsObject) move across the screen at different speeds. Using SKActions and runBlock: was even worse, as the runBlock: action can’t be copied when the node is copied.

As a result, I managed to invent my own solution which was to created a new category: SKNode (updateExtension). The SKNode(updateExtension).h file (which is quite short) is pasted below.

The extension works by adding 2 new methods to all SKNodes, and hence all subclasses of SKNode:

- (void)update:(NSTimeInterval)currentTime;
- (void)updater:(NSTimeInterval)currentTime;

The update: method works by calling the node’s own updater: method, and then calling all of its children’s update: methods. The updater: method of any particular node is then filled with custom code of the user’s choice (if left blank, it will do nothing). The update: method then cannot be touched.

The idea behind this was that the topmost node of the node tree would then be able to call all of its descendantsupdater: methods which would only take one line of code. Or even better, no lines of code, since the SKScene, which is the topmost node of the tree, already has its update: method called every frame — which was why I had given my method the same name.

Or so I had hoped. Omitting the update: method from where I would usually put it in the @implementation GameScene doesn’t seem to have worked, as my version of update: isn’t called at all. I am forced to copy and paste my own version of the update: method from the category into the @implementation GameScene for this all to work…

  1. Why does this happen? Surely the SKScene should call its update: method every frame even if I had omitted it from the @implementation since it already has been implemented by my category?

  2. In addition to this issue, I was wondering about the efficiency of my method. Given a reasonably large tree of nodes (around 1000 nodes?), even if the majority of the nodes’ updater: methods were empty, would the program be slowed to less than 60 frames per second as it would have to dig to the bottom of the node tree each frame? I have only been working with OS X so far — would the efficiency be much worse with iOS?

  3. I now want to add 2 boolean properties to the SKNode category: selfPaused and childrenPaused. I would add 2 if statements inside the update: method for this (selfPaused around the call to the node’s own updater: method and childrenPaused around the children enumeration block). However, it turned out that I could not add properties to class categories, and to do so required access to the @implementation file using class extensions. Is there any way around this problem that anyone could think of?

The advantage of this would be that a node could pause all of its descendants’ update: methods with one line of code saving time for the user.

The SKNode(updateExtension).h file:

#import <SpriteKit/SKNode.h>
#import <SpriteKit/SpriteKitBase.h>

@interface SKNode (updateExtension)

- (void)update:(NSTimeInterval)currentTime;
- (void)updater:(NSTimeInterval)currentTime;

@end
@implementation SKNode (updateExtension)

- (void)update:(NSTimeInterval)currentTime {

    [self updater:currentTime];

    [self.children enumerateObjectsUsingBlock:^(SKNode *child, NSUInteger idx, BOOL * _Nonnull stop) {
        [child update:currentTime];
    }
     ];
}
- (void)updater:(NSTimeInterval)currentTime {
}
@end // SKNode (updateExtension)
\$\endgroup\$
2
\$\begingroup\$

In this case, I would recommend using the built in SpriteKit methods for the best readability and the best control of the state while the game is running.

Why do you need the nodes to automatically update themselves? It seems like this could lead to a situation where you do not know what exactly is causing things to happen to the nodes in the scene. Furthermore, it seems like you are putting all of the logic for the game into the scene, breaking encapsulation between the game model and the way it is rendered.

SKScenes have a built in method that is called every frame: - (void)update:(NSTimeInterval)currentTime.

Carefully reading over your question, it appears that you already know about this method. I recommend that you use that method and make the updates to the appropriate nodes inside that method (calling their update methods from there). If you confine all changes of game state to this method, then it will be easier to keep track of what happens each frame. The individual nodes could have a state that would determine whether anything happens during their update.

\$\endgroup\$
  • \$\begingroup\$ The aim of this is to create a class called physicsObjects which when given their displacement, velocity, acceleration, .... and as many derivatives as the user deems to be necessary, then the object will update its displacement (position) on the screen every frame. Therefore, the code for that needs to be in the class itself... And I don't want users of this class having to use an enumeration block to update all physicsObjects on the screen. \$\endgroup\$ – Shuri2060 Oct 31 '15 at 16:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.