cocos2d dynamically changing animation speed with CCSpeed - iphone

In a simple game, a character has a walking animation of around 15 frames. Based on the accelerometer is speed in either direction changes. I wanted the animation to speed up or slow down accordingly. I realize that once the animation is initialized, you can't change the delay speed, and instead you need to create a new animation. But since the speed changes almost constantly, if I kept reseting the animation, it would always be stuck on the same frame.
Is there a way to find which frame an animation is currently in, stop it, then create a new animation with the same frame but starts at which the last animation ended?
Otherwise does anybody have an idea of how you could go about implementing a dynamically changing animation delay time?
EDIT:
I have tried using CCSpeed to do this from an example in Learn Cocos2d 2. Here is the code:
CCRepeatForever *walkLeftRepeat = [CCRepeatForever actionWithAction:[CCAnimation animationWithSpriteFrames:walkLeftFrames]];
self.walkLeft = [CCSpeed actionWithAction:walkLeftRepeat speed:animationWalkingSpeed];
I get the following warning: Incompatible pointer types sending 'CCRepeatForever *' to parameter of type 'CCActionInterval *'
Here is the example I am following from:
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:rotateBy];
CCSpeed* speedAction = [CCSpeed actionWithAction:repeat speed:0.5f];
Second EDIT:
Then I tried this:
CCAnimation *walkLeftRepeat = [CCAnimation animationWithSpriteFrames:walkLeftFrames];
CCAnimate *temp = [CCAnimate actionWithAnimation:walkLeftRepeat];
CCAnimate*temp2 = [CCRepeatForever actionWithAction:temp];
self.walkLeft = [CCSpeed actionWithAction:temp2 speed:animationWalkingSpeed];
[_guy runAction:_walkLeft];
Final edit. This problem of it not showing up was fixed by adding a delay to ccanimation.
This doesn't give any error's but nothing happens.
Thanks!

No need to recreate the animation. Use a CCSpeed action with the CCAnimate action as its inner action. Then you can modify the CCSpeed action's speed property to speed up or slow down the animation.
The alternative is to advance frames yourself. It's easy to do, you only need a scheduled update and a timer that tells you when to change to the next frame. You speed up or slow down the animation by simply changing how much you add to the timer every frame. If the timer is > than a specific value you change frames and reset the timer to 0.

You can do it in Cocos2d Game:
-(void)preLoadAnim
{
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:#"ANIMATION_HERO"];
if(!animation)
{
CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache];
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=4;i++)
{
CCSpriteFrame *frame = [cache spriteFrameByName:[NSString stringWithFormat:#"HeroRun_%d.png",i]];
[animFrames addObject:frame];
}
animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.5f;
animation.restoreOriginalFrame = NO;
[[CCAnimationCache sharedAnimationCache] addAnimation:animation name:animName];
}
}
-(void)runHeroSlowMode
{
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:#"ANIMATION_HERO"];
animation.delayPerUnit = 1.0f;
CCAnimate *animAction = [CCAnimate actionWithAnimation:animation];
CCRepeatForever *runningAnim = [CCRepeatForever actionWithAction:animAction];
runningAnim.tag = kTagHeroRunAction;
[self stopActionByTag: kTagHeroRunAction];
[heroSprite runAction:runningAnim];
}
-(void)runHeroFastMode
{
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:#"ANIMATION_HERO"];
animation.delayPerUnit = 0.1f;
CCAnimate *animAction = [CCAnimate actionWithAnimation:animation];
CCRepeatForever *runningAnim = [CCRepeatForever actionWithAction:animAction];
runningAnim.tag = kTagHeroRunAction;
[self stopActionByTag: kTagHeroRunAction];
[heroSprite runAction:runningAnim];
}

I was also facing the same problem. In my game whenever I collect something my speed increases. at that time I paused my animation at the current frame changed its delay time and then resumed it.... Now its working

Related

Sprite jumping around when i use resumeSchedulerAndActions

Ok I am attempting some AI stuff here and I have been following some Ray Wenderlich tutorials. I have some strange behavior going on. Maybe I am just doing this all wrong... but here you go. When a sprite is within 75 pixels of the target it switches to the Defending AIState and i call pauseSchedulerAndActions and set it to a predetermined safe spot via getDefensePosition method. What I am trying to do is after 2 seconds resume the actions so the sprite will move around again. so I call resumeSchedulerAndActions. Now this just goes through the getDefenseMethod and it moves te sprite between these three places but this is the strange behavior i have two slog calls one before getDefenseMethod and one after the sprite is jumping around from the center of the screen then back to the new spawnPoint:
2013-03-04 20:08:14.897 10-8[2629:c07] before: {217.533, 177.32}
2013-03-04 20:08:14.898 10-8[2629:c07] spawnPoint 1
2013-03-04 20:08:14.899 10-8[2629:c07] after: {100, 100}
dont understand why it is doing that. Why does it not just start from the position it was in?
- (void)execute:(GangMembers *)player {
// Check if should change state
NSArray * enemies = [player.layer enemiesOutsideRange:75 ofPlayer:player];
if (enemies.count > 0) {
NSLog(#"outside range 75");
[player changeState:[[Attacking alloc] init]];
return;
}
[player.layer setPlayer:player attacking:NO];
// Make build decision
[player.layer unschedule:#selector(shoot:)];
[player pauseSchedulerAndActions];
NSLog(#"before: %#", NSStringFromCGPoint(player.position));
[self getDefensePosition];
player.position = spawnPoint;
NSLog(#"after: %#", NSStringFromCGPoint(player.position));
[player performSelector:#selector(resumeSchedulerAndActions) withObject:player afterDelay:2];
}
- (void)getDefensePosition {
// CGSize winSize = [CCDirector sharedDirector].winSize;
int spawnChoice = arc4random() % 3;
spawnPoint = ccp(100, 100);
if(spawnChoice == 0){
spawnPoint = ccp(100, 100);
NSLog(#"spawnPoint 1");
}
else if(spawnChoice == 1){
spawnPoint = ccp(100, 200);
NSLog(#"spawnPoint 2");
}
else {
spawnPoint = ccp(100, 300);
NSLog(#"spawnPoint 3");
}
}
FWIW, I suspect your player object has some CCMove type of actions (which you are pausing). Even though you change the position while paused, when the action resumes, the action sets the position to its current state (startPosition, endPosition, duration, time elapsed since start), which may be quite different from the position you set during the pause.
not certain of your object model/class structure, but something like this:
[player stopAllActions];
player.position = spawnPoint;
[player runAction: [CCSequence actions:
[CCDelayTime actionWithDuration:2.0],
[CCMoveTo actionWithDuration:arc4random()%5+1 position: randomPoint],
[CCCallBlock actionWithBlock:^{ [self performSelector:#selector(moveRandom:) withObject:s afterDelay:0.5]; }],
nil]
];
this way, you recreate a moveto action that will be executed from spawnPoint, and your player.position is not in contention with a running action. Written from memory, you mileage may vary :)

Slow preformance during replaceScene

I'm facing a performance issue whenever the code reach "replaceScene". It happens only in the Play Scene. So after the game is over, I display a score, and then, it's time for CCDirector to do replaceScene in order to go back to the main menu.
After waiting for around 20 seconds then it finally display the main menu. But somehow this is not right, player will feel that the game suddenly hang. Then I tried to put some animation like preloader, it happens the same, the preloader picture did animate a while then suddenly stop, and I think due to the same issue triggered by replaceScene, although still it'll display the main menu scene. Care to give some tips how to speed up the releasing of all the objects which no longer needed.
Hoping to get a solution from experts here. Thanks.
Here is my code :
............
//button at the score pop up sprite
CCMenuItem *btContinue = [CCMenuItemImage itemFromNormalImage:BTCONTINUE
selectedImage:BTCONTINUE_ON
target:self
selector:#selector(goLoader)];
btContinue.anchorPoint = ccp(0,0);
btContinue.position = ccp(340, 40);
CCMenu *menu = [CCMenu menuWithItems:btContinue, nil];
menu.position = CGPointZero;
[self addChild:menu z:ZPOPUP_CONTENT];
//prepare the loader, but set visible to NO first
CCSprite *loaderBg = [CCSprite spriteWithFile:LOADER_FINISH];
loaderBg.anchorPoint = ccp(0,0);
loaderBg.position = ccp(0,0);
loaderBg.visible = NO;
[self addChild:loaderBg z:ZLOADER_BG tag:TAG_LOADER_BG];
NSLog(#"prepare loader finish");
//animate loader
CCSprite *loaderPic = [[CCSprite alloc] initWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:LOADER]];
loaderPic.anchorPoint = ccp(0.5,0.5);
loaderPic.position = ccp(200,35);
loaderPic.visible = NO;
[self addChild:loaderPic z:ZLOADER_PIC tag:TAG_LOADER_PIC];
[loaderPic runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:0.05f angle:10.0f]]];
}
-(void)goLoader {
NSLog(#"goMainMenuScene");
CCSprite *tmpBg = (CCSprite *) [self getChildByTag:TAG_LOADER_BG];
if (tmpBg != nil)
tmpBg.visible = YES;
CCSprite *tmpPic = (CCSprite *) [self getChildByTag:TAG_LOADER_PIC];
if (tmpPic != nil)
tmpPic.visible = YES;
double time = 2.0;
id delay = [CCDelayTime actionWithDuration: time];
id proceed = [CCCallFunc actionWithTarget:self selector:#selector(goMainMenuScene)];
id seq = [CCSequence actions: delay, proceed, nil];
[self runAction:seq];
}
-(void)goMainMenuScene {
[[GameManager sharedGameManager] runSceneWithID:SCENE_MAIN_MENU];
}
Your problem is most likely the new scene, and whatever happens in the new scene's and its child node's init methods. Loading resource files can take quite a while. If you defer doing this into onEnter you might see better results. But 20 seconds, that's a lot. Check what you're doing that takes THIS long. I bet it's loading a gross amount of resources, or loading them in an extremely inefficient way. JPG files are known to load very slowly, if you use JPG convert them to PNG.

Removing animation sprite frames from layer?

Let's say I have a character in a game and its class is like this.
#interface Player
{
CCSprite* stand;
CCAnimation* run;
}
-(void) playRunAction
{
// Create CCAnimate* object from CCAnimation object (run)
[self runAction:runAniate];
}
-(void) playStandAction
{
stand.visible = YES;
[self stopAllActions];
}
The player has ability to stand or run.
But one problem is, after playStandAction is called, stand animation is visible and running animation stopped, but one frame of running animation still there!
( Now you see 'stand sprite' AND 'one of running animation frame' together. )
How can I make running animation not visible?
P.s Can anyone throw me a better way of managing animation in one character? This is totally disaster as animations added.
-(void) playStandAction
{
//Make the animation object.visible = NO; here
stand.visible = YES;
[self stopAllActions];
}
and in
-(void) playRunAction
{
// Create CCAnimate* object from CCAnimation object (run)
//Make the animation object.visible = YES; here
stand.visible = NO;
[self runAction:runAniate];
}
Use method with parameter restoreOriginalFrame and pass it yes
I don't know which method you are calling for creating CCAnimate object...
Like this:
[CCAnimate actionWithAnimation:animation restoreOriginalFrame:YES]];
And don't call runAction on layer. I would prefer you to runAction on sprite itself...
You don't need to hide and show 2 different objects...
Hope this helps. :)

iphone cocos2d CCSequence of Action and CCParticleSystem animation

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];
...
}

Animations in cocos2d

I am wondering if someone can explain me how to re-use animations in cocos2d? I was used to so-called usual way: I create a 'game object', load animations and after that simply call my animations like gameObj->idle(); or gameObj->walkToPoint();...
I understand that these helper methods should be defined by myself, and I also studied a lot of guides about CC2d and using animations in different ways, but these guides are too simple and don't shot real cases.
So at the moment I wrote method which loads animation from plist and png texture, adds this texture to parent layer and assigns first sprite to the 'Game Object' sprite. But I have some questions - I still can't find the way to play animations.
Situation is more difficult for me (but I know that this is usual case :) - animations should be assigned to 'actions' and there should be different types of actions. For example - idle action plays forever, death plays once and stays at the last frame and shoot action plays once and restores previous frame - idle's one.
To make things simple I'll show some basic code w\o complex checks and for..in loops (I wondering who decided store all frames in cc2d animation format as Array and load them with frame_%d, not as Dictionary with structure Animations->Idle->Frames Animations->Shoot->Frames, but this not main point :))
//These things are global
CCAnimation *idleAnim;
CCAction * idleAction; // idle animation should be played forever
CCAnimation *deathAnim;
CCAction * deathAction; // death animation should be played once and stop at last frame
CCAnimation *shootAnim;
CCAction * shootAction; // shoot animation should be played once and idle frame restored
// loading animations with simple for loops
- (id)init {
self = [super initWithColor:ccc4(255,255,255,255)];
if (self) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"player.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode
batchNodeWithFile:#"player.png"];
[self addChild:spriteSheet];
NSMutableArray *idleAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 20; ++i) {
[idleAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"hero_idle01_%04i_Layer-%i.png", i-1, i]]];
}
idleAnim = [CCAnimation animationWithFrames:idleAnimFrames delay:0.1f];
idleAction = [CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:idleAnim restoreOriginalFrame:NO]];
NSMutableArray *deathAnimFrames = [NSMutableArray array];
for(int i = 0; i <= 30; ++i) {
[deathAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"hero_death01_%04i_Layer-%i.png", i, i+1]]];
}
deathAnim = [CCAnimation animationWithFrames:deathAnimFrames delay:0.1f];
deathAction = [CCAnimate actionWithAnimation:deathAnim restoreOriginalFrame:NO];
NSMutableArray *shootAnimFrames = [NSMutableArray array];
for(int i = 0; i <= 19; ++i) {
[shootAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"hero_idleshoot02_%04i_Layer-%i.png", i, i+1]]];
}
shootAnim = [CCAnimation animationWithFrames:shootAnimFrames delay:0.1f];
shootAction = [CCAnimate actionWithAnimation:shootAnim restoreOriginalFrame:YES];
self.player = [CCSprite spriteWithSpriteFrameName:#"hero_idle01_0000_Layer-1.png"];
self.player.position = ccp(20, self.size.height/2);
[self.player runAction:shootAction];
[spriteSheet addChild:self.player];
[self.player runAction:[CCRotateTo actionWithDuration:0.0f angle:45]];
[self schedule:#selector(gameLogic:) interval:1.0];
}
return self;
}
This code runs correctly (loads idle animation and plays it forever) but I can't find the way to switch between actions and animatinos nicely. I've seen approaches when for example when someone wan't shoot animation, he deletes original sprite takes first sprite from shoot, plays shoot, deleted shoot sprite, restores original sprite. But what if sprite was rotated? One should pass all the params from original sprite such as flip, rotation, scale, etc. back and forward between sprites? Usually Game Objects are inherited from CCSprite, so this method generally means that one should delete and restore main game object all the time? This is not clear for me.
I tried next things, but they didn't work for me, program just halts and nothing is written to debug (I guest it is because error happens in other thread, the one which is responsible for touch handling).
// somewhere after touch happened in [shootToPoint:(CGPoing)point] method
[idleAction stop];
[shootAction startWithTarget:self.player];
[self.player runAction:[CCSequence actions:
shootAction,
[CCCallFuncN actionWithTarget:self selector:#selector(shooted)],
nil]];
And in shooted method I call again something like this:
[shootActino stop]; // or [self.player stopAllActions] - doesn't matter I guess
[idleAction startWithTarget:self.player];
CCSprite *sprite = (Bullet *)sender; // remove bullet sprite from memory
[self removeChild:sprite cleanup:YES]; // remove bullet sprite from memory
// other routines...
So can someone explain me how to create game objects with helper methods to switch animations? Please don't send me to cc2d docs because for it is unclear how to solve this simple problem from cc2d docs or other tutorial on the web.
Since you are creating your instances with helper methods instead of alloc/initWith... they are getting autoreleased, that's why you get a crash.
For example your idle animation should be created like this:
NSMutableArray *idleAnimFrames = [[NSMutableArray alloc] init];
for(int i = 1; i <= 20; ++i) {
[idleAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"hero_idle01_%04i_Layer-%i.png", i-1, i]]];
}
CCAnimation *idleAnimation = [[CCAnimation alloc] initWithFrames:idleAnimFrames delay:0.1f];
[idleAnimFrames release];
CCAnimate *idleAnimate = [[CCAnimate alloc] initWithAnimation:idleAnimation restoreOriginalFrame:NO];
[idleAnimation release];
idleAction = [[CCRepeatForever alloc] initWithAction:idleAnimate];
[idleAnimate release];
That way you can use this action multiple times until you release it (which you should do in your dealloc method, or somewhere else when you're done with it).