Tag Archives: animation

Extending classes with categories in Objective-C

For the last couple of months I’ve been playing around with cocos2d to build a simple catch ’em all game for kids to learn the alphabet, numbers etc. It is progressing great so I am planning to release the first preview soon. For the time being, my son is the only tester.

Cocos2d is a great open-source 2d (and 3d) game engine with support for many advanced features that every game is comprised of. However, in this post I will only concentrate on actions. During one of the refactoring, I realised that I frequently need to execute a specific action after all other actions have be successfully executed. Cocos2d supports this via special actions like CCCallFunc and CCCallBlock. For this to work, you need to encapsulate your action in a CCSequence and add a CCCallBlock at the end. For example, if you need to perform an action after the FadeTo action is completed, you need something like this (alpha is an instance of CCSprite which in turn is a subclass of CCNode):

[alpha runAction:[CCSequence actions:

[CCFadeTo actionWithDuration:2.0f opacity:50],

[CCCallBlock actionWithBlock:animCompletionBlock],

nil]];

As there is much of a boilerplate code in the above example, I decided to extend the CCNode to behave much like UIView animations do, i.e., by using a special block invoked after actions are finished. However, instead of extending the CCNode class in the source code, I extended it using Objective-C feature called categories. Categories allow developers to extend a class (not subclass!) in a way that does not affect the original implementation. Here is an example of an interface with a special category Helper (as in helper methods):

@interface CCNode (Helper)

– (CCAction*) runAction:(CCFiniteTimeAction *)action completion:(void(^)())block;

@end

The implementation is trivial and resembles the example above:

@implementation CCNode (Helper)

– (CCAction*) runAction:(CCFiniteTimeAction *)action completion:(void(^)())block

{

CCSequence *sequence = [CCSequence actions:action,

[CCCallBlock actionWithBlock:block],

nil];

return [self runAction:sequence];

}

@end

Now the above example can be rewritten as a single line of code:

[alpha runAction:[CCFadeIn actionWithDuration:2.0f] completion:animCompletionBlock];

Much cleaner and easier to invoke a block after the action is finished. Surely, the same extension mechanism could be used for other block methods that also pass the node and user data object to the block.

Advertisements