Shalin Shah
Shalin Shah

Reputation: 8183

How to spawn CCSprites in random heights like flappy bird

In the iOS game flappy bird, there are pipes that generate after a certain distance and they generate at random heights

This is a fake image, but the logic is the same

I am also trying to make flappy bird pipes (I called it a tree branch in my code instead of pipe). Except the pipes are moving vertically instead of horizontally because it is a vertical scrolling game (it scrolls like the game doodle jump)

This is a drawing of what I want it to be: https://docs.google.com/drawings/d/18bxsVsNOlScCvgi1mwuzD2At7R6xKM3QCh6BfAVMuMo/edit?usp=sharing (The horizontal lines are the branches)

So this is what I have tried to do so far to make the vertical branches (or pipes)...

in my .h

CCSprite *branch;
NSMutableArray *_branches;
CCSprite *obstacle;
CCNode *previousBranch;
CGFloat previousBranchYPosition;

in my .m

@implementation HelloWorldLayer 

static const CGFloat firstBranchPosition = 426.f;
static const CGFloat distanceBetweenBranches = 140.f;
#define ARC4RANDOM_MAX      0x100000000
static const CGFloat minimumXPositionRightBranch = 280.f;
static const CGFloat maximumXPositionLeftBranch = 50.f;
static const CGFloat pipeDistance = 100.f;
static const CGFloat maximumXPositionRightBranch = maximumXPositionLeftBranch - pipeDistance;

setBranchInitialPosition method

    /* This is where I am setting the initial position of the branches. 
So I am specifying the position of the first branch and the other branches after it so it gets placed every time a certain distance is passed. I have a left branch and a right branch*/
     -(void) setBranchInitialPosition {
        CGFloat random = ((double)arc4random() / ARC4RANDOM_MAX);
            CGFloat range = maximumXPositionRightBranch - minimumXPositionRightBranch;

            _rightBranch.position = ccp(minimumXPositionRightBranch + (random * range), _rightBranch.position.y);
            _leftBranch.position = ccp(_rightBranch.position.x + pipeDistance, _leftBranch.position.y);
        }

spawnNewBranches method

// This is how I want the branches to spawn and I want to add them to an array full of branches
   - (void)spawnNewBranches {
        previousBranch = [_branches lastObject];
        previousBranchYPosition = previousBranch.position.y;

        if (!previousBranch) {
            // this is the first obstacle
            previousBranchYPosition = firstBranchPosition;
        }

        _rightBranch = [CCSprite spriteWithFile:@"branch.png"];
        _leftBranch = [CCSprite spriteWithFile:@"branch.png"];
        [_leftBranch addChild:_rightBranch];
        [self setBranchInitialPosition];

        obstacle = [CCSprite node];
        [obstacle addChild:_leftBranch];


        obstacle.position = ccp(160, previousBranchYPosition + distanceBetweenBranches);
        [self addChild:obstacle];
        [_branches addObject:obstacle];
    }

scroll method

-(void) scroll:(ccTime)dt
{
    // moves the bg
    background.position = ccp(screenCenter.x, background.position.y + [[NSUserDefaults standardUserDefaults] integerForKey:@"scrollSpeed"]*dt);
    bg2.position = ccp(screenCenter.x, background.position.y-background.contentSize.height);

    // it adds the new bg's to the screen before the old bg's move off the screen
    if (background.position.y >= screenSize.height*1.5)
    {
        background.position = ccp(screenCenter.x, (screenCenter.y)-(background.size.height/2));
    } else if (bg2.position.y >= screenSize.height*1.5) {
        bg2.position = ccp(screenCenter.x, (screenCenter.y)-(bg2.size.height/2));
    }

        // This is where I want them to appear every certain distance and also move with the brackground
    obstacle.position = ccp(obstacle.position.x, obstacle.position.y*[[NSUserDefaults standardUserDefaults] integerForKey:@"scrollSpeed"]*dt);

    NSMutableArray *offScreenObstacles = nil;
    if (obstacle.position.y >= screenSize.height*1.5) {
        [offScreenObstacles addObject:obstacle];
    }

    for (CCNode *obstacleToRemove in offScreenObstacles) {
        [obstacleToRemove removeFromParent];
        [_branches removeObject:obstacleToRemove];
        // for each removed obstacle, add a new one
        [self spawnNewBranches];
    }

}

Right now, the branches are appearing, but they stay in the bottom left corner and they dont move or spawn at all. I want to make them move with the background and spawn after a certain distance while also being generated in random heights. I provided you with all my code, do you know how I can make this work? Thanks in advance!

Upvotes: 3

Views: 4649

Answers (2)

rcmstark
rcmstark

Reputation: 1041

I created a copy of Flappy Bird just for fun. I used this code to create the pipes:

-(void)createPipes{

    //Create Random
    int from = 65;
    int max = [[UIScreen mainScreen] bounds].size.height - 124;
    int delta = max - from - dy;
    int y = from + arc4random() % (delta - from);

    //Pipe Bottom
    UIImageView *pipeBottom = [[UIImageView alloc] init];
    [pipeBottom setContentMode:UIViewContentModeTop];
    [pipeBottom setImage:[UIImage imageNamed:@"pipeBottom"]];
    [pipeBottom setFrame:CGRectMake(320, y+dy, 60, max - y - dy)];
    [pipeBottom setClipsToBounds:YES];

    //Pipe Top
    UIImageView *pipeTop = [[UIImageView alloc] init];
    [pipeTop setFrame:CGRectMake(320, 0, 60, y)];
    [pipeTop setContentMode:UIViewContentModeBottom];
    [pipeTop setImage:[UIImage imageNamed:@"pipeTop"]];

    [self.view insertSubview:pipeTop atIndex:1];
    [self.view insertSubview:pipeBottom atIndex:1];

    if (!self.pipes)
        self.pipes = [[NSMutableArray alloc] init];

    [self.pipes addObject:pipeBottom];
    [self.pipes addObject:pipeTop];

}

and to move them:

-(void)moveArray:(NSMutableArray *)array{
    float ds = dv * dt;
    NSMutableArray *trash = [NSMutableArray array];
    for (UIImageView *obj in array) {
        CGRect frame = obj.frame;
        frame.origin.x -= ds;
        if (frame.origin.x < -frame.size.width) {
            [obj removeFromSuperview];
            [trash addObject:obj];
        }else{
            obj.frame = frame;
        }
    }
    [array removeObjectsInArray:trash];
}
-(void)movePipes{
    [self moveArray:self.pipes];
}

I call this function every 0.01 seconds, to run the game:

-(void)runGame{
    _time += dt;
    if (_time >= 180.0/dv) {
        _time = 0;
        [self createPipes];
    }
    [self movePipes];
    [self moveEnemies];
    [self moveYoshi];
    [self moveBar];
    [self verifyScore];
    [self verifyCollision];
    [self verifyState];
}

I defined dt = 0.01 and dv = 110.

You can see my parody in youtube: (http://www.youtube.com/watch?v=tTcYdpSIKJg)

I hope this help you.

Best, Rafael Castro.

Upvotes: 1

Rob Segal
Rob Segal

Reputation: 7625

You may want to try placement of the pipes based on a trigonometric curve like sine or cosine (https://en.wikipedia.org/wiki/Trigonometric_functions). It seems like you are placing the pipes within a fairly define random range though if you change this range to an offset from the plot of the trigonometric curve it would take into account the ability of the player to transition between the open gaps better. At least that's my feel. I think the code would be easier to follow as well as I'm a bit confused going through it. You can also easily vary the difficulty of the curve by changing the parameters such as increasing the amplitude or frequency.

Upvotes: 1

Related Questions