Run-Time Dynamic Length Actions list for Sequence
-
06-07-2019 - |
Question
Cocos2D defines the static method 'actions' for the Sequence class as such:
+(id) actions: (FiniteTimeAction *) action1, ... { /* omitted */ }
How could I build a list of actions to perform at run-time, perhaps read from a disk file or such?
I read that the variable length argument list can be translated into a (char *) and passed in that way ...
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject: [DelayTime actionWithDuration:1]];
[actions addObject: [ScaleBy actionWithDuration:2 scale:4];
char *argList = (char *)malloc(sizeof(FiniteTimeAction *) * [actions count]);
[actions getObjects:(id *)argList];
[self runActions: actions];
Is this the 'best way' or the 'correct' way to do this? Are their better alternatives, faster alternatives?
Solution
The vaargs is just a helper to build nested Sequence
objects. It returns a FiniteTimeAction*
which is built by successive calls to [Sequence actionOne:one_ two:two_]
. You could do this yourself in your code by looping through your set or array. It should go something like this:
FiniteTimeAction *seq = nil;
for (FiniteTimeAction *action in actions) {
if (!seq) {
seq = action;
} else {
seq = [Sequence actionOne:seq two:action];
}
}
[self runActions:seq];
OTHER TIPS
I would look at using NSInvocation - you can basically build one using the method signature you are targeting, and then use the setters for each object like so:
NSInvocation *invoker = setup invoker here...
for ( int i = 0; i < actions.count; i++ )
{
NSObject *arg = [actions objectatIndex:i];
[invoker setArgument:&arg atIndex:i+2];
}
[invoker setArgument:nil atIndex:i+2];
The i+2 bit is because the first two arguments are really self and _cmd, so you set everything from index 2 and on... read the docs on setArgument:atIndex: in NSInvocation for more detail.
Once you have that you can invoke the operation with a target. I've never used this with variable argument methods so I'm unsure how well it works there, but it's the only means I know of to dynamically construct a call at runtime.
Given the options provided, it appears that the only real way to accomplish what I was after is to use the approach I mentioned in the question, which is was:
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject: [DelayTime actionWithDuration:1]];
[actions addObject: [ScaleBy actionWithDuration:2 scale:4];
char *argList = (char *)malloc(sizeof(FiniteTimeAction *) * [actions count]);
[actions getObjects:(id *)argList];
[self runActions: actions];
Perhaps a set of pre-built sequences?
id move = [MoveBy actionWithDuration:3 position:ccp(350,0)];
id move_back = [move reverse];
id move_ease_in = [EaseIn actionWithAction:[[move copy] autorelease] rate:3.0f];
id move_ease_in_back = [move_ease_in reverse];
id move_ease_out = [EaseOut actionWithAction:[[move copy] autorelease] rate:3.0f];
id move_ease_out_back = [move_ease_out reverse];
id seq1 = [Sequence actions: move, move_back, nil];
id seq2 = [Sequence actions: move_ease_in, move_ease_in_back, nil];
id seq3 = [Sequence actions: move_ease_out, move_ease_out_back, nil];
[grossini runAction: [RepeatForever actionWithAction:seq1]];
[tamara runAction: [RepeatForever actionWithAction:seq2]];
[kathia runAction: [RepeatForever actionWithAction:seq3]];