Cocos2d v3 group actions like SpriteKit - cocos2d-iphone

Desired grouping behaviour
How would I group CCAction in order to execute them all at the same time while knowing when all of the actions in the group are complete?
Exact SpriteKit equivalent
+ (SKAction *)group:(NSArray *)actions
Use case
I want to use this in a board game.
I am moving my game elements around my board with CCActions. At the end of all of these animation I would like to a CCActionCallFunc as a callback to update game logic and permit user interaction once more.
Ideally I would like to add the CCActionGroup and the CCActionCallFuncin a CCActionSequence and have them execute sequentially.

Maybe you could try:
CCActionMoveTo *moveTo; // action to group
CCActionScaleBy *scale; // action to group
CCNode *node;
// Init the above actions...
CCActionSpawn *groupAction = [CCActionSpawn actionWithArray:#[moveTo, scale]];
CCActionSequence *sequence = [CCActionSequence actionWithArray:#[groupAction, [CCActionCallFunc actionWithTarget:self selector:#selector(allDone)]]];
// allDone is your method to run...
[_node runAction:sequence];

Since cocos2d doesn't employ any kind of multithreading, you can just fire the actions sequentially:
[node runAction:action1];
[node runAction:action2];
[node runAction:action3];
[node runAction:action4];
Just add a CCCallBlock to the longest running sequence that runs a block. If all actions run the same length, you would add the call block to the action4 sequence because that'll finish last.
In Sprite Kit there wouldn't be any guarantee which of action1 through action4 finishes last, in cocos2d there is: the last one to be run will also be the last to finish, assuming they all run for the same duration.
This is when they other actions will also have ended. Alternatively you can create a sequence for each group at the end of each they all run a CCCallBlock that increases a counter, and the last block where the count is equal to the number of grouped actions performs the actual "end of group actions" code.

Related

Cocos2d-x. How to add action on the sprite after the previous one was finished

I add action on the sprite.
auto moveBy = MoveBy::create(2, Vec2(moveX, moveY));
_Spr1->runAction(moveBy);
I want to add another action on touch, but I want that the second one started after the first one is finished.
And if I tap two times before first action stops, I want to create sequence with one old action and two new ones.
How about queue your following actions, wait the current actions finish, and then add them?
Check if animations are still running
auto moveBy1 = MoveBy::create(2, Vec2(moveX, moveY));
auto moveBy2 = MoveBy::create(2, Vec2(moveX, moveY));
auto *seq = Sequence::create(moveby1,moveby2, NULL);
_Spr1->runAction(seq);

check if all actions are finished cocos2d

My problem is this: i do quite a bit of code in ccTouchEnded and i want for the CCTouchDispacher to stop all input until all actions on the screen are over. The thing is that when i tap the screen repeatedly ccTouchEnded gets called every timeand it ruins everything. I've tried to stop the input but i don't know which of the actions will execute..so i can't just run a CCSequence and when it's finished re-enable input.
My code looks something like this:
if (taped in a rect)..do action, change some things around,do another action, call method 1
if (condition)..do action,check something,(if (check) do action, else call method 2)
Its worth noting that the methods do different things in different layers which take different amounts of time.
So my question is this: Is there any way to check if all actions on the screen are completed?
Check that the number of running actions on the node/sprite/layer is zero:
http://www.cocos2d-swift.org/docs/api/Classes/CCNode.html#//api/name/numberOfRunningActions
Such as:
[mySprite numberOfRunningActions]==0
You have the isDone function that tells you if an CCAction is finished. From the docs:
(BOOL) - isDone return YES if the action has finished

CCMoveBy behaviour

I'm getting stuck to implement some Cocos2D animations for my Tetris clone(that works perfectly, no logic bugs, i just want to perform some smooth animation when deleting rows).
The current code(no animation) just drops the block position, like this:
block.position = ccp(block.position.x, block.position.y - kBlockSize);
This happens in a for loop for, classic tetris programming. But when i try to animate, like this:
id move = [CCMoveBy actionWithDuration:0.5f position:(0, -kBlockSize)];
[block runAction:move];
Some blocks just moves down once, even tough the action may be called multiple times for the same block(when breaking more than one row for example)...
Why that happens ? I know it's a little bit confusing, but the point is that i'm doing the same stuff and getting different results...i could post more code to help clarify!
Thanks!
I'm quite sure actions are parallel operations so you could be calling a CCMoveBy action before a previous one is completed. Some alternatives I have used are...
Monitor for when the action completes by using a CCSequence finishing with a CCCallFunc action that sets a flag. Something like...
id myAction = [[CCSequence runWithActions:[CCMoveBy actionWithDuration:0.5f position:(0, -kBlockSize)], [CCCallFunc actionWithTarget:self selector:#selector(myFunc)], nil]
Roll your own solution using a velocity variable in a tick or update function where you can get a hold of delta time/# of ticks since the last update
Hope some of that helps.
Thank you guys, those answers help me a lot!
I've tried CCSequences before posting here, but without success.
The problem was the following:
Inside the CCSequence that deletes a row, i have 2 actions: the first one fades out the entire row of blocks(duration of x seconds), and the second one drops all the blocks above the row(duration of y seconds).
This works fine if ONLY ONE row needs to be deleted, because if there is more than one row, the next CCSequence starts nearly the same time the previous, reading a incorrect position of the blocks above, leading to a incorrect cascade of blocks.
I solved that using a longer CCSequence, that takes a CCCallFuncND as the last argument:
id fadeOutSequence = [CCSequence actions:fadeout, destroyBlocks, notifyFadeFinish, nil];
//inside method specified for notifyFadeFinish:
id dropAbove = [CCSequence actions: dropBlocks, notifyDropFinish, nil];
//inside method specified for notifyDropFinish
//start a new delete sequence, if there is more rows to delete.
Now going to implement gravity mode, thanks again!

How to implement simple tick-based engine in c++?

I'm writing a text game and I need a simple combat system, like in MUDs, you issue commands, and once in a while "tick" happens, when all those commands execute, player and monsters deal damage, all kinds of different stuff happens. How do I implement that concept?
I thought about making a variable that holds last tick time, and a function that just puts events on stack and when that time is (time +x) executes them all simutaniously. Is there any easier or cleaner variant to do that?
What would be possible syntax for that?
double lastTickTime;
double currentTime;
void eventsPile(int event, int target)
{
// how do i implement stack of events? And send them to execute() when time is up?
}
void execute(int event, int target)
{
if ((currentTime - lastTickTime) == 2)
{
eventsHandler(event, target);
}
else
{ // How do I put events on stack?
}
}
The problem with simple action stack is that the order of actions will probably be time based - whoever types fastest will strike a first hit. You should probably introduce priorities in the stack, so that for instance all global events trigger first, then creatures' action events, but those action events are ordered by some attribute like agility, or level. If a creature has higher agility then that it gets the first hit.
From what I've seen, most such engines are event, rather than time, based. with a new tick being triggered some interval after the last tick ended. (thus mostly avoiding the issue of ticks taking longer than the interval)
This also simplifies implementation; you simply have a game loop that triggers a tick event, then sleeps/yields for the required interval. Which is trivial.
It can further be simplified by modeling the world as a tree, where each element manages propagating events (such as ticks) to their children. so long as you avoid / manage 'loops', this works well (I've done it).
This effectively reduces the tick system to something like this (psudocode):
while (isRunning) {
world->tick();
sleep(interval);
}
In most cases, theres little need to get much fancier than adjusting for the length of the previous duration.
Any individual entities actions would be part of their own action queue, and handled during their own "tick" events.
Usually user commands would be split into "ingame" and "meta" commands, anything ingame would merely amend their character's action queue, to be processed in their next tick, as per normal for any other entity.
Simple round-based combat follows naturally from this foundation. realtime can be modeled with a finer division of ticks, with optional 'time-pooling'.
Use a timer executing every x ms (whereas x is your ticktime), execute any actions put on the stack in that method.

How to check if CCAction is running - cocos2d

I'm using Cocos2d to write game for iPhone.
Here's the problem.
I have CCSprite and CCAction which is run on it.
CCSprite texture;
CCAction anim_action;
"Anim_action" is a CCRepeatForever action.
Now I want to check if this animation is running.
First I though I can use [isDone] function, but I think it doesn't work on CCRepatForever actions (I'm not sure - this opion is based on my tests)
So how to check if this animation is already running on my "texture"?
Maybe there is a way to get name of action which is running on texture at the moment?
It may be also useful!
There is a way to check if a specific action runs on your texture. Use:
CCAction *action = [texture getActionByTag:kAsignedActionTag];
where kAsignedActionTag is the tag assigned to the your animation.
anim_action.tag = kAsignedActionTag;
If your action is still running the getActionByTag method will not return nil.
I don't believe there's a way to directly tell if a CCRepeatForever action has completed since the isDone would make no sense, but there are some techniques you can use to essentially provide a callback to indicate if something is still running:
Override the step: method and call out to something that checks the interval - when it exceeds a threshold you can assume completion...kinda'
Wrap the inner action of the CCRepeatForever with a CCSequence. The first action of the sequence would be your repeated action and the second would be a CCCalFunc, again indicating that the action is still running
Subclass the CCRepeatForever and override the dealloc so you can fire a callback when the action is killed and released
You can easily use [isDone] while appling an effect
- (void)shakeThatThingOn: (BOOL)on { //you can return BOOL and get if the animation is working or not
if (on == YES){
id shaky2 = [CCShaky3D actionWithRange:3 shakeZ:NO grid:ccg(15,10) duration:5];
if (![shaky2 isDone])
[self runAction:[CCSequence actions:shaky2,[CCStopGrid action],nil]];
}
else {//this else is being called when you turn off animation (it's just 0.2s continuation after turning off - for better visual effect.
[self stopAllActions];
id shaky2 = [CCShaky3D actionWithRange:3 shakeZ:NO grid:ccg(15,10) duration:0.2];
[self runAction:[CCSequence actions:shaky2,[CCStopGrid action],nil]];
}}
and control it by simple BOOL if it's on or off.
I don't know if it's what you mean, but hope it'll help anyway.
If you know how many actions will be running on the sprite, or if the animation is the only action, then you can infer that the animation is running by checking the sprite's total number of running actions.
if ([texture numberOfRunningActions] > 0) //animation is running
or
if ([texture numberOfRunningActions] > someNumber) //if you had other actions running