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 SKAction
s and performSelector:
. However, the end result was undesirable, as I observed 2 identical node
s 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 descendants
’ updater:
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…
Why does this happen? Surely the
SKScene
should call itsupdate:
method every frame even if I had omitted it from the@implementation
since it already has been implemented by my category?In addition to this issue, I was wondering about the efficiency of my method. Given a reasonably large
tree
ofnode
s (around 1000node
s?), even if the majority of thenode
s’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 thenode tree
each frame? I have only been working with OS X so far — would the efficiency be much worse with iOS?I now want to add 2 boolean properties to the
SKNode
category:selfPaused
andchildrenPaused
. I would add 2if
statements inside theupdate:
method for this (selfPaused
around the call to thenode
’s ownupdater:
method andchildrenPaused
around thechildren
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 descendant
s’ 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)