CCNode get position an action is moving it to - cocos2d-iphone

Just wondering how I can get the position of a node that will be the current position if it is not moving, but if there is a CCMoveTo action being performed I need the position it is moving to.
Is this already built in or will I have to implement it myself, if so any suggestions on how I would go about it?
Thanks for the help,
Ben

Usually, if there are already built-in methods that "single-lined-ly" give you the information that you want, for example using [[CCDirector sharedDirector] running Scene] to get the current scene, it is a bad idea to create an ivar to keep track of the information for later querying.
But if using the built-in methods are hackish or long-winded, with no guarantee for accuracy, then it is better to create an ivar.
So, just create an ivar on that node that you update whenever the position is changed or a CCMoveTo action is assigned.
Note: I'm calling the -(NSArray *)getActionsByTarget:(id)target method as hackish because it is not guaranteed that you can find the CCMoveTo action in it. The action might be embedded deep in [CCSequence actions:[CCFadeIn ...], [CCSpawn actions:[CCCallBlock actionWithBlock: ^{ [self runAction:[CCSequence actions:[CCDelay ...], [CCMoveTo ...], nil]]; }], [CCMoveTo ...], nil], [CCMoveTo ...], nil]. Now how in the world you would implement the logic that can correctly find all the three CCMoveTo and deduce which of it will give the final position?

You need to have any CCMoveTo action object, and then search it by CCNode object that you want to get the position of. If you found the CCMoveTo object that is targeted for the CCNode object, then you can get the position from the CCMoveTo object. For example, add -(NSArray *)getActionsByTarget:(id)target method or something like that to CCActionManager class.
Or, create derived class from CCNode to have the position of the CCMoveTo action.

Related

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.

How to vibrate CCSprite in cocos2d

Can anyone tell me how to vibrate/shake CCSprite in cocos2d???plz give me some example.
If the object is static you could create a CCSequence of MoveTo actions - pixel or two to the one side and pixel to the other of the real position and repeat it with CCRepeat how many times you need or use CCRepeatForever and remove action by tag with CCActionManager method removeActionByTag:(int)tag target:(id)target when some event occur.