check if all actions are finished cocos2d - cocos2d-iphone

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

Related

Is it bad practice to have nested render loops?

I'm porting a game from Ruby to C++. There is a main render loop that updates and draw the content. Now let's say that during the game, you want to select an item another screen. The way it's done in the original code is to do Item item = getItemFromMenu(); getItemFromMenu is a function that will open the menu and do have its own update/render loop, which mean that during the whole time the player has this other screen open, you are in a nested render loop. I feel like this is a bad method but I'm not sure why. On the other hand it's very handy because I can open the menu with just 1 function call and so the code is localized.
Any idea if this is a bad design or not?
I hesitated to post it on gamedev, but since this is mostly a design issue I posted it here
edit : some pseudo-code to give you an idea:
The usual loop in the main part of the code:
while(open) {
UpdateGame();
DrawGame();
}
now inside UpdateGame() i would do something like:
if(keyPressed == "I") {
Item& item = getItemFromInventory();
}
And getItemFromInventory():
while(true) {
UpdateInventory();
if(item_selected) return item;
DrawInventory();
}
A good way to handle something like this would be to replace the DrawInventory() call with something like InvalidateInventory(), which will mark the current graphical state of the inventory as outdated and request it to be redrawn during the next frame rendering (which'll happen pretty soon after when the main loop gets to DrawGame()).
This way, you can keep running through the main loop, but the only parts of the screen that get looked at for redrawing are the ones that have been invalidated, and during normal gameplay you can invalidate your (2/3)D environment as a normal part of processing, but then inside the inventory you can always mark only inventory assets as needing to be redrawn, which minimises overhead.
The other part of your inner loop, UpdateInventory(), can be a part of UpdateGame() if you use a flag to indicate the current game state, something like:
UpdateGame()
{
switch(gameState)
{
case INVENTORY:
UpdateInventory();
break;
case MAIN:
default:
UpdateMain();
break;
}
}
If you really wanted, you could also apply this to drawing:
DrawGame()
{
switch(gameState)
{
case INVENTORY:
DrawInventory();
break;
case MAIN:
default:
DrawMain();
break;
}
}
But I think drawing should be encapsulated and you should tell it which part of the screen, rather than which separate area of the game, needs to be drawn.
What you've created with your nested render loop is functionally a state machine (as most game render loops tend to be). The problem with the nested loop is that many times you'll want to do the same sorts of things in your nested loop as your outer loop (process input, handle IO, update debug info etc).
I've found that it's better to have one render loop and use a finite state machine (FSM) to represent your actual states. Your states might look like:
Main menu state
Options menu state
Inventory state
World view state
You hook up transitions between states to move between them. The player clicking a button might trigger the transition which could play an animation or otherwise, then move to the new state. With a FSM your loop might look like:
while (!LeaveGame()) {
input = GetInput();
timeInfo = GetTimeInfo();
StateMachine.UpdateCurrentState(input, timeInfo);
StateMachine.Draw();
}
A full FSM can be a bit heavyweight for a small game so you can try a simplified state machine using a stack of game states. Every time the user does an action to transition to a new state you push the state on a stack. Likewise when they leave a state you pop it off. Only the top of the stack typically receives input and the other items on the stack may/may not draw (depending on your preference). This is a common approach and has some upsides and downsides depending on who you talk to.
The simplest option of all is to just throw a switch statement in to pick which render function to use (similar to darvids0n's answer). If you're writing an arcade clone or a small puzzle game that would do just fine.

stopAllActions works but stopActionByTag does nothing

I have a CCRepeatForever action with a tag of 20. I call this:
[player stopActionByTag:20];
Noting happens.
I call this:
[player stopAllActions];
It stops. Any idea? The action is created like this and runs fine:
CCRepeatForever *repeat=[CCRepeatForever actionWithAction:animate];
repeat.tag=20;
[player runAction:repeat];
update: i also tried setting animate.tag=21 and stopping that action by itself or in addition to the repeat action, but neither works.
This should work, I made a quick test with stopActionWithTag using CCRepeatForever and it stops it correctly.
I can only imagine two cases where it wouldn't work:
You have more than one action with the tag = 20 running on the same object.
The object you send runAction to is a different object than the one you send stopActionByTag to.
The former is easy to check. If this code fixes your problem, you're running multiple actions with the same tag on the player object:
while ([player getActionByTag:20]) {
[player stopActionByTag:20];
};
The latter is rather unlikely but possible. One way to find out is to set a breakpoint on the runAction line and note the address of the player variable. Then set another breakpoint at the stopActionByTag line and compare that player's address with the previous one. If they're not the same, then that would be the problem.

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!

Button held down by code

I am writing a game and am trying to make a person move with the arrow keys.
I have this code
if (Key_Down(DIK_DOWN))
{movedown(player)}
This works but as I want the player to take four steps every time the key is pressed I created and animation loop. so the player cannot input any more move commands until the animation is over and they have taken four steps I made the code this.
if(player.move == false)
{
if (Key_Down(DIK_DOWN))
{movedown(player)}
}
The problem is that now once a button is pressed the program acts like the button is held down and the player keeps moving until another direction is pressed.
Can anyone explain what the outer loop has done to the code and fix the problem ?
it is programmed in visual c++ 2005 and direct x 9.c
Edit:
If I remove the outer loop then the button press is only registered once so I don't think it is the movedown function.
Simply keep track of the keystate in a variable. So you can reset a move counter. Something like this, combining both:
int MovingDown = 0;
...
if (!Key_Down(DIK_DOWN)) MovingDown = 0;
else if (MovingDown < 4) {
MovingDown++;
movedown(player);
}
Hard to tell without seeing more of your codebase but I would guess that you're not re-setting player.move after you've moved the player. Have you tried sticking some break poins in there to see what is being called and why?
If you remove the if(player.move == false) line does it have the same issue? ..or have you changed something elsewhere? If you could post more of your codebase like the whole movedown function and anythnig else which interacts (e.g. the animation code) it would help us help you...
[Edit] Is movedown a macro? Maybe you've got some peculiar side effect eminating from that? If not, it ought to have a semi colon after the function call.
What does KEY_DOWN return? Is that an API call? Try testing against the exact value you expect it to return?

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