0

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.

BobbyScon
  • 2,537
  • 2
  • 23
  • 32

1 Answers1

1

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.

johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • I can't figure out how to get these sprites to appear sequentially though. I tried this, and it still has the same effect as above. I haven't tested performance comparison yet. I posted the code based on your suggestion in my question. – BobbyScon Mar 01 '12 at 13:33
  • what do you mean by "sequentially" ? also, i recommend using arc4random for everything, you have a lot of random(). finally, my suggestion works just fine with batch node too. – johnbakers Mar 01 '12 at 14:58
  • Sorry, sequentially probably isn't the right word. If I set the delay to duration of 5, it divides my 500 sprites into 5 groups and animates one group after another, after the animation is complete. I want it to be more fluid for example after the 1st sprite gets 10% through its animation, the 2nd sprite begins and so forth. Staggered animation on each sprite might be better phrasing. As far as random() vs arc4random goes, I'm lost. I've been copying a dozen different forums and examples to get this to work, but I don't truly understand the differences and how the numbers after () really work. – BobbyScon Mar 01 '12 at 15:37
  • And perhaps this is a case of my imagination visualizing something that the code can't quite match because of the difference in brain animations vs programmatic animations. – BobbyScon Mar 01 '12 at 15:38
  • SO to the random() rescue. http://stackoverflow.com/questions/8962848/arc4random-and-operator and http://stackoverflow.com/questions/2794201/whats-the-difference-between-arc4random-and-random – BobbyScon Mar 01 '12 at 15:44
  • Your code can definitely do this; just stagger them in your update method, send each group over when the update method determines the prior group is 10% done, etc. – johnbakers Jan 03 '13 at 05:01