views:

238

answers:

4

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?

A: 

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]];
slf
That is still design-time ... and, my question was pretty clear about being dynamic and 'at run time' (ie; something that is determined at run-time ... and is unknown at design-time). My question also provides a possible solution and simply asks if there is a better way to do it ...
David Higgins
Why not just keep your info in a .plist and load the Sequence dynamically?
slf
A: 

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.

Kendall Helmstetter Gelner
A: 

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];
David Higgins
+2  A: 

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];
ongle
This seems like a perfect approach.
David Higgins