I am trying to run an animation on a sprite each time a button is pressed.
But for some reason, the action is only ran once and wont run again like i need it to.
Here is my action if it helps.My action is declared as an id instance variable.
moveUp = [CCMoveTo actionWithDuration:3 position:ccp(60,self.position.y+200)];
Are you trying to re-use the same action? Like so:
moveUp = [CCMoveTo actionWithDuration:3 position:ccp(60,self.position.y+200)];
[self runAction:moveUp];
// some time later …
[self runAction:moveUp];
Then this won't work. You have to create a new action every time, like so:
CCAction* moveUp = [CCMoveTo actionWithDuration:3 position:ccp(60,self.position.y+200)];
[self runAction:moveUp];
// some time later …
CCAction* moveUp = [CCMoveTo actionWithDuration:3 position:ccp(60,self.position.y+200)];
[self runAction:moveUp];
Related
I'm making a top down tile based game (think old Pokemon and Zelda games on GameBoy). I'm having problems with the character moving smoothly. I think the problem is the delay between finishing an action and starting a new one.
Here's what the code looks like:
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchCoor = [self coordinateForTouch:touch];
// If the character was touched, open their dialogue
if (CGPointEqualToPoint(touchCoor, ebplayer.coordinate)) {
[[CCDirector sharedDirector] replaceScene:
[CCTransitionMoveInR transitionWithDuration:1.0 scene:[HelloWorldLayer scene]]];
}
else // otherwise, move the character
{
activeTouch = [self directionForPoint:[touch locationInView:[touch view]]];
[self movePlayer:nil inDirection:activeTouch];
}
return YES;
}
// given in screen dimension points
// is the base movement function for all movements
- (void)movePlayer:(NSString*)pid toPosition:(CGPoint)position{
CGPoint playerCoordinate = [self coordinateForPositionPoint:position];
// if we're not already moving, and we can move, then move
if(!isAnimating && [self coordinateIsOnMap:playerCoordinate] && [self isPassable:playerCoordinate]){
id doneAction = [CCCallFuncN actionWithTarget:self selector:#selector(finishedAnimating)];
id moveAction = [CCMoveTo actionWithDuration:WALK_DURATION position:position];
id animAction = [CCAnimate actionWithAnimation: [ebplayer animateDirection:activeTouch withDuration:WALK_DURATION]];
id walkAndMove = [CCSpawn actionOne:moveAction two:animAction];
id action = [CCSequence actions: walkAndMove, doneAction, nil];
isAnimating = YES;
[player runAction:action];
ebplayer.coordinate = playerCoordinate;
[self setViewpointCenter:position Animated:YES];
}
// if it's not passable, just run the animation
if(!isAnimating){
id doneAction = [CCCallFuncN actionWithTarget:self selector:#selector(finishedAnimating)];
id animAction = [CCAnimate actionWithAnimation: [ebplayer animateDirection:activeTouch withDuration:WALK_DURATION]];
id action = [CCSequence actions: animAction, doneAction, nil];
isAnimating = YES;
[player runAction: action];
}
}
Then when that action is finished, try and start it up again:
(void)finishedAnimating{
isAnimating = NO;
[self movePlayer:nil inDirection:activeTouch];
}
You will always end up with a 1-frame delay when sequencing multiple CCMove* actions.
What happens is the following:
frame 0-100: move action runs, sprite is moving
frame 101: move action ended, CCCallFunc runs
frame 102: new move action begins
This one-frame delay is one of the main problems of sequencing move actions, and the reason why I wouldn't recommend using move actions for gameplay purposes.
The alternative is to move objects manually in a scheduled update method by modifying their position. You can use the CCMove* action code as basis.
I want to start InFinite CCAction at particular time. I tried using CCSequence but it supports only finite time animation.
Any idea?
Best Regards,
Paras
Put the action that you want to repeat inside of a method. Then put this in your init method
[[CCScheduler sharedScheduler] scheduleSelector:#selector(myMethod) forTarget:self interval:10 paused:NO];
This will call myMethod after 10 seconds, however once inside myMethod you'll want to unschedule it. So my method should look something like this.
- (void) myMethod
{
[[CCScheduler sharedScheduler] unscheduleSelector:#selector(myMethod) forTarget:self];
CCMoveBy *move = [CCMoveBy actionWithDuration:3 position:ccp(75,0)];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:move];
[mySprite runAction:repeat];
}
The last two lines are what you need.
CCMoveBy* move = [CCMoveBy actionWithDuration:3 position:ccp(75,0)];
CCCallFuncO* shot = [CCCallFuncO actionWithTarget:self selector:#selector(shoot:) object:enemy];
CCSequence* sequ = [CCSequence actions:move,shot,nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequ];
[sprite runAction:repeat]; //sprite here
I´m new to Cocos2d. I´m trying to run two animations one after another.
The first one is:
CCAction *walkAction;
CCAnimation *walkAnim = [CCAnimation
animationWithFrames:walkAnimFrames delay:0.15f];
bear = [CCSprite spriteWithSpriteFrameName:#"normal1.png"];
walkAction = [CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO];
[bear runAction:walkAction];
[spriteSheet addChild:bear];
The second which I want to fire right after the first one is:
CCParticleSystem *killPigAnim = [CCParticleSystemPoint particleWithFile:#"killPigAnim.plist"];
[self addChild:killPigAnim];
How can I achive that when the second one is not an action but the CCParticleSystem object.
You can use the action CCCallFunc to either call the start method on the particle system or call a method in your class which starts the particle system.
i.e.
-(void) startParticles
{
//Start your particles
}
-(void) myOtherMethod
{
...
walkAction = [CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO];
CCCallFunc *callAction = [CCCallFunc actionWithTarget:self selector:#selector(startParticles)];
[bear runAction:[CCSequence actionWithActions:walkAction, callAction, nil];
...
}
I have three actions that are triggered in the CCSequence. The way I want it to be fired is that first the sprite should move in the center of the screen and then the scale action is fired. But for some reason the sprite moves to the center of the screen correctly but when the scale is fired it uses the old sprite position.
id actionRotate = [CCRotateBy actionWithDuration:0.6 angle:360];
id disappear = [CCFadeTo actionWithDuration:.5 opacity:0];
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:#selector(removeAlphabetToFindFromView:)];
id actionScale = [CCScaleBy actionWithDuration:0.6 scaleX:10 scaleY:10];
id moveTo = [CCMoveTo actionWithDuration:0.6 position:ccp(windowSize.width/2, windowSize.height/2)];
//[self removeAlphabetToFindFromView2:alphabetToFind];
[alphabetToFind runAction:[CCSequence actions:moveTo,actionScale,disappear,actionMoveDone, nil]];
UPDATE 1:
Maybe the startAnimation method has something to do with this. I have public x and y variables which is used as x and y positions for 4 different sprites:
-(void) startAnimation:(CCSprite *) sprite
{
[self generateRandomCoordinates];
id actionMove = [CCMoveTo actionWithDuration:3.0 position:ccp(x,y)];
id actionRotate = [CCRotateBy actionWithDuration:0.0 angle:rotateBy];
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:#selector(finishedMoving:)];
[sprite runAction:[CCSequence actions:actionMove,actionRotate, actionMoveDone, nil]];
}
-(void) finishedMoving:(id) sender
{
if(counter == randomAlphabets.count)
{
counter = 0;
}
CCSprite *sprite = [randomAlphabets objectAtIndex:counter];
[self generateRandomCoordinates];
[self startAnimation:sprite];
counter +=1;
}
UPDATE 2:
As expected the x and y used in the startAnimation method (getRandomCoordinates) were causing the problem. So, I removed all the actions before firing the sequence and now it works fine.
Friends i am new to cocos2d programming and Mac in general!
I have noticed this EXC_BAD_ACCESS errors ruining most of my time... Take the following snippet of code from the Geek & Dad's tutorial...
-(void) AddEnemyAtX:(int)x Y:(int)y {
CCSprite *enemy1 = [CCSprite spriteWithFile:#"enemy1.png"];
enemy1.position = ccp(x,y);
[self addChild:enemy1];
[self animateEnemy:enemy1];
NSLog(#"%#", enemy1);
}
-(void) animateEnemy:(CCSprite *)enemy {
ccTime actualDuration = .5;
id actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,enemy.position)), 10)];
id actionFinished = [CCCallFunc actionWithTarget:self
selector:#selector(animateEnemyFinished:)];
[enemy runAction:[CCSequence actions:actionMove,actionFinished,nil]];
}
-(void) animateEnemyFinished:(id)sender {
CCSprite *enemy = (CCSprite *)sender;
[self animateEnemy:enemy];
}
here _player is a global variable and accessible everywhere, I call AddEnemyAtX: Y: and pass some coordinates. My problem is the first time the loop runs fine... But again when the control is passed from animateEnemyFinished to animateEnemy then the app crashes mentioning "EXC_BAD_ACCESS"!
From what i figured out, the Sprite reference is not passed correctly! Help!
CCSprite *enemy1 = [CCSprite spriteWithFile:#"enemy1.png"];
gives you an autoreleased object. This means you should not call
[enemy1 release]
and 2nd after you set
enemy1=nil
you can't do
[self animateEnemy:enemy1];
because you give nil to animateEnemy:
Removing
[enemy1 release];
and
enemy1 = nil;
from your code should fix your problem.
Wow!!
Atlast figured it out...
A small mistake... just replacing the line
id actionFinished = [CCCallFunc actionWithTarget:self
selector:#selector(animateEnemyFinished:)];
with
id actionFinished = [CCCallFuncN actionWithTarget:self
selector:#selector(animateEnemyFinished:)];
did the trick! What i understood from this was that #selector() passes the id of the object which called upon it but at the same time when we use it along with CCCallFuncN it passes the id of the Node just parent to the object which called upon it!