Search code examples
xcodecocos2d-iphoneccsprite

How can I animate grass growing in Cocos2d?


I'm trying to animate a field of grass growing by sprouting the same small sprite 500 times. The effect I'm trying to acheive is each individual clump of grass would grow (in this case scaleY) and the field would be full over the course of 3 seconds. I'm using a CCSpriteBatchNode, which I'm now reading is not an efficient way to go with just one sprite file. Here's my code which gives me the desired effect, but I can't get the speed that I want.

    //GRASS batchNode
    CCSpriteBatchNode *grassBatch1 = [CCSpriteBatchNode batchNodeWithFile:@"grass1.png" capacity:500];
    grassBatch1.position = CGPointMake(0, -10);
    [self addChild:grassBatch1 z:1 tag:201];
    for (int x=0; x<500; x++) 
    {
        CCSprite *s = [CCSprite spriteWithBatchNode:grassBatch1 rect:CGRectMake(0, 0, 32, 32)];
        [s setOpacity:255];
        [grassBatch1 addChild:s];
        [s setPosition:ccp(arc4random()%1024-0, arc4random()%75-0)];
        [s setScaleY:0];
        [s setAnchorPoint:CGPointMake(0, 0)];
        CCScaleTo *grassScale = [CCScaleTo actionWithDuration:0.4 scale:1];
        CCEaseElasticOut *grassBounce = [CCEaseElasticOut actionWithAction:grassScale period:0.2f];
        CCDelayTime *grassDelay = [CCDelayTime actionWithDuration:random()%500];
        [s runAction:[CCSequence actions:grassDelay,grassBounce, nil]];
    }

In CCDelayTime, the 500 number causes each clump to animate in individually, but it does 1 clump per second. If I make that number 10, then it combines the 500 clumps into groups of 10 and animates each group 1 per second. I want each clump (or group of clumps) to animate 1 after another evenly timed over the course of 3 seconds. A plus on top of this would be to animate in a direction, like a wave effect, so the grass starts growing from left to right or bottom to top.

Is there a better approach to producing 500 individually growing sprites that use the same .png file?

EDIT:

OK, so I guess I needed a better understanding of the use of random()% and the integer that follows. I get that 500 in the duration part would indeed push the animation to 500 seconds. I wanted to use less than 1, and values with decimals (0.6 for example) were being denied. Fractions apparently work. In order to get something close to what I'm working towards I made the following adjustments to the code above.

First I added a random to the scale action:

CCScaleTo *grassScale = [CCScaleTo actionWithDuration:random()%7 scale:1];

and then used a fraction on the delay:

CCDelayTime *grassDelay = [CCDelayTime actionWithDuration:random()%1/20];

I know that going this route to acheive the effect that I want is pretty taxing on the device to manage. On an iPad2, my frame rate drops from 60 to 48 then goes back to 60 fairly quickly, but the CPU usage increases from 20% or so to 85%. The main visual drawback is the delay in transitioning from the prior scene to this one. Lesser devices will undoubtedly experience more delay and drops in frame rate.

I know this probably isn't the right way to approach this animation. And, I'd still like to do a rolling, or wave type animation on this so it travels over a direction. Does anyone have any thoughts on this effect?

EDIT 2:

This is the code attempted from andrewx's suggestion:

    for (int i = 0; i < 500; i++) 
    {
        CCSprite *grassClump = [CCSprite spriteWithFile:@"grass1.png"];
        grassClump.anchorPoint = CGPointMake(0.5, 0);
        grassClump.position = ccp(arc4random()%1024-0, arc4random()%180-0);
        grassClump.scaleY = 0;
        CCScaleTo *grassScale = [CCScaleTo actionWithDuration:random()%7 scale:1];
        CCEaseElasticOut *easeGrassScale = [CCEaseElasticOut actionWithAction:grassScale period:0.3f];
        CCDelayTime *grassDelay = [CCDelayTime actionWithDuration:random()%5];
        [self addChild:grassClump z:5];
        [grassClump runAction:[CCSequence actions:grassDelay, easeGrassScale, nil]];
    }

It acts identically to my previous code, but avoids the sprite batch node, which is probably a good thing. I haven't tested performance to see if this is an improvement or not yet.


Solution

  • You could draw one or several individual grass leaves, randomly select them like this:

    CCSprite *leaf=[CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:@"grass%i.png",arc4random%5+1]];

    This example assumes you have 5 different leaves to randomly choose from, and you could iterate through this in a loop, adding leaf to your parent for as many times as you want leaves of grass.

    Then, either separately or in the same loop, add a CCMoveTo to push the leaves up from below, so they appear to be growing. You could simultaneously add a CCScaleTo also so they do both at the same time. Would be a nice effect.