How to delay my AI move - cocos2d-iphone

I am doing a game like chess. As soon my player move is completed(if he starts move from one place to another), my AI move is started (before my player reaching his destination ). Sometimes i find difficult which AI coin is moved now. how to delay it.

If your player movement is bounded by Core Animation, you can setup the setAnimationDidStopSelector to a custom STOP function and starts your AI there.
If you have a game loop with states, just add enough states (e.g. user_move_began, user_move_ended, ai_think_began, ai_think_ended, ai_move_began .. ) to sequence the flow.

I think I understand what you are saying now.
You need to do a CCSequence, with your AI function call in a CCCallFunc at the end:
CCSequence *playerMove = [CCSequence actions:
[CCMoveTo actionWithDuration: 0.4f position: CGPointMake(10,10)],
[CCCallFunc actionWithTarget: self selector: #selector(doAIstuff)],
nil];
[playerSprite runAction: playerMove];
You need to have your AI be a callable function (i.e. doAIstuff). It is confusing to that you say the code is in draw().

Related

How to access b2body of CCPhysicsSprite using its tag

i`m working on a platformer game , i have A character who jumps on different platforms , until he reaches the end of the level.
one type of platform is a tree log floating on water. the log slowly moves up and down along the tide of water .
each platform is actually a b2body. here is how i define a platform :
b2Body *platformbody;
b2BodyDef platforbodydef;
b2FixtureDef platformfixdef;
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(0.5f, 0.5f);
platforbodydef.type=b2_staticBody;
platforbodydef.position.Set(BlockPlatX[i][j], BlockPlatY[i][j]);
platformbody= world->CreateBody(&platforbodydef);
platformfixdef.shape=&dynamicBox;
platformsprite=[CCPhysicsSprite spriteWithFile:#"TreeLog.png"];
dynamicBox.SetAsBox(platformsprite.texture.contentSize.width/PTM_RATIO/2,platformsprite.texture.contentSize.height/PTM_RATIO/2);
platformfixdef.friction=1;
platformfixdef.density=1;
platformfixdef.restitution=0;
platformbody->CreateFixture(&platformfixdef);
if(platforbodydef.position.y < watersprite.contentSize.height/2)
{
platforbodydef.position.Set(prevPlatX + 300, watersprite.contentSize.height/2 + 10);
CGPoint point=CGPointMake(platforbodydef.position.x,watersprite.contentSize.height/2);
CCMoveTo *waterMove=[CCMoveTo actionWithDuration:3 position:point];
point=CGPointMake(platforbodydef.position.x,watersprite.contentSize.height/2+ 10);
CCMoveTo *waterMoveBack=[CCMoveTo actionWithDuration:3 position:point];
CCSequence* sequence = [CCSequence actions:waterMove,waterMoveBack, nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence];
[platformsprite runAction:repeat];
}
[platformsprite setPTMRatio:PTM_RATIO];
[platformsprite setB2Body:platformbody];
[platformsprite setPosition:CGPointMake(platforbodydef.position.x, platforbodydef.position.y)];
[self addChild:platformsprite z:4 tag: 10000 + i*100 + j];
i am using a loop ,so i create more than one of these platforms .
but the problem is that when the sprite runs the action sequence , the position of the b2body associated with it does`nt change and this obviously causes lots of problems.
is there anyway i can access the b2body of the sprite using its tag and change the position of the body instead?
First thing is that the sprite follows the body and not the other way round. When you are repositioning the b2Body via setTransform you have to be very careful as you can find some really odd behaviours. The collision is not being performed in the proper fashion, so if the moved body will intersect with something else some crazy stuff can happen.
Why are you using sequence to move the body on the water. You are much better off using a prismatic joint to push it along and at the end of the track place a sensor which will revert the motor of the joint to move it in the other direction. Let the physics engine deal with the movement. This way you allow the system to work in the way it was intended and the sprite will updates its position to the body automatically.
Edit: I would also advised using a visual box2d editor. You are using Cocos2d as an engine, so there is quite few to choose from. I would personally recommend "Really Useful Box2D Editor" or "R.U.B.E." in short.

Communitacting between CCLayers in one Scene

I have 2 CCLayers that needs to communicate with each other in separate .m files
Level1.m (CCScene with Level1 CCLayer) - Holds tiled map and player sprite
HUDLayer.m (links to top of Level1.m) - Holds all buttons
How can I get the following code in HUDLayer.m to talk to player sprite in Level1.m?
- (void)MoveUpSelected {
CCMoveTo* moveup = [CCMoveBy actionWithDuration:1 position:ccp(0,-100)];
CCSequence* sequence = [CCSequence actions: moveup, nil];
[Player runAction:sequence];
}
Please help I've been stuck on this for days. At least if someone can point me in the right direction. Thanks!
I would advice you to use you scene object to control communication between its layers.
You can create a HUD protocol and set the scene as its delegate. And for each HUD event the scene will react accordingly by accessing the proper layer (stored as its member).
This way you won't have to make this layer coupling.
To access another layer, you need a reference to it. There are many ways to do that. In your case just add one property for each layer to the CCScene class. The layers can then access the scene via their parent:
CCLayer* otherLayer = [(YourActualSceneClass*)self.parent otherLayer];
It is very important that you do not store a reference to the other layer in either layer, or if you do, make sure to make it a weak reference, or nil them in the cleanup method. Otherwise you created a retain cycle.
You'll find more info on accessing other nodes here.

Replacing one long CCMoveTo to multiple CCMoveTo with update Cocos2D

Currently, I have a game where an object determines a point to go to. It then calculates a path to that point and constructs one long CCMoveTo animation to get to that point. With this method, the animation seems very smooth and continuous.
I am now trying to break this one long CCMoveTo to multiple CCMoveTo leveraging the update method that gets called periodically. I want to do this because at each node of the path that the objects passes through, there might be a distraction and I want my object to be able to act on that. So this is what I am doing:
- (void) update: (ccTime) dt
{
if(![self isWalking]){
CGPoint nextNode = [_path objectAtIndex:(_currentPathIndex%[_path count])];
_currentPathIndex++;
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject:[CCCallBlockO actionWithBlock:^void(id obj) {
[(Monkey *) obj setIsWalking:NO];
} object:self]];
[self moveTo: nextNode withCallbacks: actions];
}
}
Note: that I set isWalking to NO as a callback when the object has completely reached the destination node. This will let it calculate the next node to go to and construct that animation. Without this, the object would try to runAction in the middle of an ongoing CCMoveTo action. The problem with this method is that the movement does not seem smooth and continuous anymore. There seems to be a lag at the end of each CCMoveTo animation
Anybody has any clue on how to fix this?
That's a side effect of cocos2d's CCAction system respectively the CCScheduler.
There will always be a 1-frame delay because when an action stops, it won't do any work in the current frame: it made the last position update in the previous frame, and in the current frame it does no longer exist as a running action.
If you now run another move action in a scheduled method, that action won't begin updating until the next frame because it will schedule itself to receive updates. And updates that are scheduled while cocos2d's CCScheduler performs updates will not be run until the next frame, due to the fact that you can't modify an array during enumeration.
My advise is always to avoid using CCMove* actions for gameplay purposes, and instead manually update position of game objects. It's easy enough to do, and if you need a code example, look inside the CCMoveTo class.
A workaround would be to extend the distance of the CCMoveTo and replace the action shortly before it completes. Though that'll be a hack and may actually be harder to implement correctly than manual position updates.
PS: That's an issue I have addressed in the action model of KoboldTouch. It implements its own action system, with more lightweight and reusable actions.

How To Retrieve Actions From Sprite In cocos2d

I have a CCSprite that I'm using in a scene and have created multiple CCAnimation actions to apply to it all using a single CCSpriteFrameCache sharedSpriteFrameCache. While everything is working and I'm able to switch between animations, I feel like I'm doing poorly and would like to simplify my code by retrieving the running action(s) on the CCSprite to stop them individually before running the next action on it.
To help create some context, lets assume the following situation:
We have a CCSprite called mySprite
We have 3 separate CCAnimation actions defined for walking to the right, walking to the left, and sitting looking forward called: actionAnimWalkRight, actionAnimWalkLeft, and actionAnimSitForward respectively.
We want to have the sprite walk to the right when someone touches the screen right of mySprite, walk left when someone touches the screen left of mySprite and sit when someone touches mySprite.
The approach I'm using to accomplish this is as follows:
Place CCSprite as a child in the scene.
Tell the sprite to run an action using: [self runAction:actionWalkRight];
When I want to change the action after someone touches, I have a method called stopAllAnimationActions which I call before I apply a new action that stops any animation action no matter what's running. Basically lists ALL the CCAnimation/CCActions I have defined and stops each one individually since I don't want to use stopAllActions. as follows: [self stopAction:actionWalkRight]; [self stopAction:actionWalkLeft]; [self stopAction:actionSitForward];
Then I apply the new animation after the above method fires using: [self runAction:actionWalkLeft];
While this works, it just seems like a poor design to stop items that I know aren't running just because I don't know exactly what is running. So just looking for advice and the best recommended practice to do something like this within very complex situations so tracking every possible situation is difficult. Any feedback would be appreciated.
When creating the actions set the tag of that action with a constant:
actionWalkRight.tag= kCurrentAction;
[self runAction:actionWalkRight];
Then, retrieve the running action by that tag and stop it.
[self stopActionByTag:kCurrentAction];
I recommend you simplify your process and take advantage of the native Cocos features, including stopAllActions. Don't re-use actions, always create them from scratch as it has been well discussed among Cocos developers that re-using actions can be buggy.
Cocos is well optimized and features like stopAllActions are not performance hogs. It would probably be faster than your approach, actually.

cocos2d remove sprite with action

I have this setup:
A layer has a sprite as a child. The sprite has this code in its init method:
id fadeOut = [CCFadeOut actionWithDuration:1.0f];
id death = [CCCallFunc actionWithTarget:self selector:#selector(die)];
self.deathAction = [CCSequence actions:fadeOut, death, nil];
[self runAction:deathAction_];
The death action calls the sprite's 'die' method in which its status is changed to 'dead'.
On its update method the layer checks all children and removes the ones wich are 'dead' with:
[self removeChild:child cleanup:YES];
The problem is that the child sprite still has retaincount of 2 after this line. As I understand it is kept by CCCallFunc. If I omit this deathAction and instead remove the sprites that have zero opacity (when they are faded), the code is working and the dealloc method gets called.
How should I remove the sprite by using an action properly?
You don't show all of your code. But it seems you are keeping a reference to the action and your likely forgetting to release that.
retainCount is notoriously unreliable : link so please don't use it and certainly don't count on it to be accurate.