Cocos2d for iPhone: Handling Accelerometer Input and Detecting Collisions

Exclusive offer: get 50% off this eBook here
Cocos2d for iPhone 0.99 Beginner's Guide

Cocos2d for iPhone 0.99 Beginner's Guide — Save 50%

Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!

$23.99    $12.00
by Pablo Ruiz | December 2010 | Open Source

Cocos2d for iPhone is a framework for building 2D games, applications, presentations, demos, and more. It was originally made for Python and then ported to IPhone by Ricardo Quesada as an open source project with the MIT license.

In the previous article by Pablo Ruiz, author of the book Cocos2d for iPhone 0.99 Beginner's Guide, we saw how to create new scenes and move through them, and build the base for an action game.

In this article, we will see how to handle accelerometer input and detect collisions.

 

Cocos2d for iPhone 0.99 Beginner's Guide

Cocos2d for iPhone 0.99 Beginner's Guide

Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!

  • A cool guide to learning cocos2d with iPhone to get you into the iPhone game industry quickly
  • Learn all the aspects of cocos2d while building three different games
  • Add a lot of trendy features such as particles and tilemaps to your games to captivate your players
  • Full of illustrations, diagrams, and tips for building iPhone games, with clear step-by-step instructions and practical examples
        Read more about this book      

(For more resources on Cocos2d, see here.)

Handling accelerometer input

Now that we have our three basic elements roughly defined, let's focus on the player's interaction with the hero. We will start by allowing the player to move the hero using the device's built-in accelerometer.

The accelerometer opens a new world of interaction between the user and the device, allowing for a lot of interesting uses.

There are already lots of applications and games that use this feature in innovative ways. For example, in the game Rolando, players have to roll the main character around using the accelerometer, and here we will be doing something similar to that.

The accelerometer gives you data of the current tilting of the device in the three axes, that is the x, y, and z axes. Depending on the game you are making, you will be needing all that data or just the values for one or two of the axis.

Fortunately, Apple has made it very easy for developers to access the data provided by the accelerometer hardware.

Time for action – moving your hero with the accelerometer

We have to add a few lines of code in order to have our hero move. The end result will be the hero moving horizontally when the user tilts the device to the left or to the right. We will also do some checkovers, in order to avoid the hero moving out of the screen.

  1. The first step involved in using the accelerometer is to enable it for the CCLayers that want to receive its input. Add the following line of code to the GameLayer's init method:

    self.isAccelerometerEnabled = YES;

    Then we have to set the update interval for the accelerometer. This will set the interval at which the hardware delivers the data to the GameLayer.

  2. Add the following line afterwards:

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:
    (1.0 / 60)];

    Now that our GameLayer is prepared to receive accelerometer input, we just have to implement the method that receives and handles it.

  3. The following is the method that receives the input and makes our hero move. Add it to the GameLayer class:

    - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
    {
    static float prevX=0, prevY=0;

    #define kFilterFactor 0.05f

    float accelX = (float) acceleration.x * kFilterFactor + (1-
    kFilterFactor)*prevX;

    prevX = accelX;

    //If the hero object exists, we use the calculated accelerometer values to move him

    if(hero)
    {
    //We calculate the speed it will have and constrain it so it doesn't move faster than he is allowed to

    float speed = -20 * -accelX;
    if(speed > hero.movementSpeed)
    speed = hero.movementSpeed;
    else if(speed < -hero.movementSpeed)
    speed = -hero.movementSpeed;

    //We also check that the hero won't go past the borders of the screen, if he would exit the screen we don't move it

    if((accelX >0 || hero.mySprite.position.x
    >hero.mySprite.textureRect.size.width / 2) && ( accelX <0
    ||hero.mySprite.position.x <320-
    hero.mySprite.textureRect.size.width / 2))
    [hero.mySprite setPosition:ccp(hero.mySprite.position.x
    +speed,hero.mySprite.position.y)];
    }
    }

  4. Run the game now. You should be able to move the hero by tilting you device, as shown in the following screenshot:

The iPhone simulator does not provide a way to simulate accelerometer input, so you won't be able to test it in there; it won't move at all.

What just happened?

Accelerometer input can be achieved quite easily and fast. In our example, we just used the x component of the accelerometer input to handle the hero's position.

The accelX variable holds the current value of the x component. This value ranges from -1 to 1, so we multiply it by the speed of the hero, then we just add that result to the current position of the hero, giving the sense of motion.

We are also checking to make sure that the hero is not going off the screen before applying that movement.

Cocos2d for iPhone 0.99 Beginner's Guide Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!
Published: December 2010
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on Cocos2d, see here.)

Handling touches on layers

Handling touches on layers with Cocos2d is pretty straightforward. As a matter of fact, it works just as it does in regular Cocoa applications. So if you have already worked on other applications, handling touches on iPhone won't be anything new.

There are two ways of handling touches. Either by giving your CCNodes the ability to capture touches on themselves or by having the whole layer process them.

Here we are going to explore the second method. What we are going to do is make the GameLayer touchable, so it can receive and process touches on it.

There are four possible methods to handle the touches over a CCLayer. They are as follows:

  • ccTouchesBegan: Called when the user puts a finger over the layer
  • ccTouchesMoved: Called when the user moves that finger over the layer without lifting it
  • ccTouchesEnded: Called when the user lifts that finger
  • ccTouchesCancelled: Called when a system event occurs (for example, a low memory warning) and the touch is cancelled

Those four methods alone are enough to create any kind of touching logic.

Cocos2d does not offer any kind of off-the-shelve gestures. So if you want to implement pinching or swiping, you will have to come up with a way to do it for yourself.

Time for action – firing the bullets

What we'll do now is create the methods for handling touches on the layer. In our example, we will make the hero fire bullets while the player holds a finger on the screen. When he releases it, we will stop firing bullets. In order to do that, we need to define the methods which get fired when the user puts a finger over the screen and when the user lifts that finger.

Back when we used the other touching method, we needed to do some previous work of registering the nodes to receive the events, set their boundaries, and so on. With this other method, we don't need to do all that.

So, let's add those methods to the GameLayer, as follows:

  1. Before you do anything, you have to tell the layer that you want to receive the touches to enable them. To do that, add the following line to the init method of the GameLayer class:

    self.isTouchEnabled = YES;

    From now on, the methods that handle touches on the layer will be called (if they are there, of course).

  2. In order to register when the user touches the layer, we need to create the ccTouchesBegan method. The following is the code involved:

    - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    for( UITouch *touch in touches )
    {
    CGPoint location = [touch locationInView: [touch view]];

    location = [[CCDirector sharedDirector] convertToGL: location];

    [self setPlayerFiring:YES];
    }
    }

    The only part important for the task at hand is the last line, where we set the playerFiring variable to YES. While that value is true, the hero will fire bullets as fast as he can.

  3. Now we need to tell the hero to stop firing. In order to do that we need to know when the player stopped touching the screen. That means, we need to use the ccTouchesEnded method:

    - (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    for( UITouch *touch in touches )
    {
    CGPoint location = [touch locationInView: [touch view]];

    location = [[CCDirector sharedDirector] convertToGL: location];

    [self setPlayerFiring:NO];
    }
    }

    Again, the only thing important for the game right now is setting the playerFiring variable to NO, to let the game know it has to stop firing bullets.
    Let's make the corresponding changes to the GameLayer in order to allow the hero to shoot bullets.

  4. Add the following lines to the step method of the GameLayer class:

    -(void)step:(ccTime *)dt
    {
    [hero update];

    for(Bullet * b in bullets)
    {
    if(b.fired)
    {
    [b update];
    }
    else
    {
    if(self.playerFiring && hero.lastTimeFired > hero.fireInterval)
    {
    [b fire:1 position:hero.mySprite.position
    fspeed:hero.firingSpeed];
    hero.lastTimeFired=0;
    }
    }
    }

    ...
    //enemy updating code
    }

  5. Finally, add the update method to the Hero class:

    -(void)update
    {
    self.lastTimeFired +=0.1;
    }

  6. Launch the game. Now, when you touch anywhere on the screen, you should see bullets going upwards from the hero's position, as shown in the following screenshot:

What just happened?

After all that work, we now have a moving, shooting hero. The last example showed how to handle the touching over layers. For doing that, we used the CCTouchesBegan and the CCtouchesEnded methods.

All four touches methods can handle multiple touches at once. That is why they receive an NSSet which you can travel to get all those touches. In the preceding examples we were doing that, although we weren't doing anything with them.

As for the firing logic, we added a little code to the step method. What it does is check whether this playerFiring ivar is true or false. If it is true, it makes a bullet get fired. Also we are checking when the hero last fired a bullet, so he does not fire a continuous stream of bullets. You can change the fireInterval of the hero, so he fires more (or less) bullets per second, turning him into a flying machinegun.

Detecting collisions

Great! Now we have a moving hero, enemies flying towards him, bullets flying everywhere, but nothing is happening! Enemies collide with the hero and they don't crash, bullets hit enemies and they don't get destroyed. "What is going on?", you may ask. Well, we need to do a little more work in order to get those done.

The first thing to do is to detect the collisions, and then act in response to them. We are going to have the following possible collisions for now:

  • Hero collides with enemy
  • Bullet fired by the hero collides with enemy
  • Bullet fired by the enemy collides with hero

Each of those collisions will have an outcome which we have to define. For example, when the bullet fired by the hero hits an enemy, both that bullet and said enemy must be removed from the screen, points must be summed up, and an explosion should be triggered. Except for the explosion, we will do all that now.

Time for action – shooting down your enemies

We need to know when a bullet is touching an enemy. In order to do that we need to check the intersection between the bullet's sprite and the enemy's sprite. If the rectangles defined by both of those sprites are intersecting, then we have a collision.

A simple way of achieving that is using the CGRectIntersectsRect method, which receives two CGRects and returns either YES or NO depending on whether those two CGRects are intersecting or not.

At first sight it seems easy to detect a collision between two rectangles, and it is. However, if you need more advanced collision detection, like when those rectangles are rotated or just detecting collisions between non-transparent pixels, then you will be in trouble. You can either use a Physics system to handle the collision detection or research the different algorithms for collision detection. Which method you use will depend on the game's particular needs.

  1. The first step is to create a method that will return the CGRect of each sprite. Add the following method to the GameLayer class:

    -(CGRect)myRect:(CCSprite *)sp

    {
    CGRect rect = CGRectMake(sp.position.x-sp.textureRect.size.
    width/2,
    sp.position.y-sp.textureRect.size.height/2,sp.textureRect.
    size.width,
    sp.textureRect.size.height);
    return rect;
    }

    This method will return the CGRect of any given CCSprite that requests it.
    Now that we can get the CGRects of each element, we must check if any of those collide.

  2. Add the following lines to the update method of the Bullet class:

    -(void)update
    {
    switch (self.whoFired)
    {
    case 1:


    for(Enemy * s in theGame.enemies)
    {
    if(ccpDistance(self.mySprite.position,s.mySprite.position) <30)
    {
    if([self checkCollisions:[theGame myRect:s.mySprite]])
    {
    [s damage];
    }
    }
    }

    break;

    ...
    }

    ...
    }

    This will make each bullet check whether they are colliding with any enemy. If they do we call that enemy's damage method.

  3. Add the checkCollisions method to the Bullet class:

    -(BOOL)checkCollisions:(CGRect)r
    {
    BOOL x = NO;

    if(CGRectIntersectsRect([theGame myRect:self.mySprite],r))
    {
    x=YES;
    [self reset];
    }

    return x;
    }

  4. The following is the damage method from the Enemy class that gets called if an enemy is hit:

    -(void)damage
    {
    self.hp--;
    [self.mySprite runAction:[CCSequence actions:
    [CCTintTo actionWithDuration:0.5 red:255 green:0 blue:0],
    [CCTintTo actionWithDuration:0.5 red:255 green:255 blue:255],nil ]];
    if(hp<=0)
    [self destroy];
    }

  5. Finally, add the destroy method to the Enemy class. It will be called when an enemies hp is down to 0:

    -(void)destroy
    {
    [self reset];
    [theGame setScore:theGame.score+100];
    NSLog(@"%d",theGame.score);
    }

  6. Run the game now. Try shooting at enemies. If everything went well, they should flash red when hit and disappear when their hp is 0, as shown in the following screenshot:>br>

What just happened?

After inserting this new code, your bullets and enemies were able to interact with each other. For doing that, we created a method that returns a CGRect that represents the boundaries of those objects at any time. Every time we call the update method of the Bullet objects, they check if they are close enough to any of the Enemy objects. If they are, then we call the checkCollision method that checks the intersection between those two seemingly colliding objects.

Once the checkCollision returns YES, we are sure that the objects are colliding and we can act accordingly. In this case when a bullet hits an enemy, we subtract a point from its hp ivar and play an action. When hit many times and the hp reaches 0, the enemy is destroyed (removed from screen and reset).

Time for action – losing your life

We have to create a couple of more collisions and their responses. Those would be when a bullet hits the hero and when an enemy touches the hero, but cases make the player lose a life, the hero disappear and then reappear. We are going to do the same thing as before, but this time the response to the collision will be different. Let's get this done now.

  1. Begin by making some changes to the Hero class. Add the following lines of code to it:

    -(void)update
    {
    self.lastTimeFired +=0.1;

    for(Enemy * s in theGame.enemies)
    {
    if(ccpDistance(self.mySprite.position,s.mySprite.position) <30)
    {
    //CGRect rect = CGRectMake(s.mySprite.position.x-
    s.mySprite.textureRect.size.width/2,s.mySprite.position.y-
    s.mySprite.textureRect.size.height/2,s.mySprite.textureRect.size.width,
    s.mySprite.textureRect.size.height);

    if([self checkCollisions:[theGame myRect:s.mySprite]])
    {
    [s reset];
    [self destroy];
    }
    }
    }
    }

    -(BOOL)checkCollisions:(CGRect)tam
    {
    BOOL x = NO;

    if(CGRectIntersectsRect([theGame myRect:self.mySprite],tam))
    {
    x=YES;

    }

    return x;
    }

    By adding that, the Hero is now aware of the surrounding enemies and it can check if it is colliding with any of them.

  2. Also add these two other methods to the Hero class:

    -(void)destroy
    {
    //EXPLODE
    if(!self.reviving)
    {
    self.reviving = YES;
    [self.mySprite setPosition:ccp(160,-200)];
    [self.mySprite runAction:[CCSequence actions:[CCDelayTime
    actionWithDuration:1],
    [CCEaseOut actionWithAction:[CCMoveTo actionWithDuration:1
    position:ccp(160,50)] rate:5],
    [CCCallFunc actionWithTarget:self selector:@selector(finishReviving)],
    nil]];

    [theGame loseLive];
    }


    }

    -(void)finishReviving
    {
    self.reviving = NO;
    }

    These two methods handle what happens when the collision between the hero and any enemy is detected.

  3. Move to the Bullet class and modify the update method to look like this:

    -(void)update
    {
    switch (self.whoFired)
    {
    case 1:
    [self.mySprite
    setPosition:ccp(self.mySprite.position.x,self.mySprite.position.y +
    self.firingSpeed)];

    for(Enemy * s in theGame.enemies)
    {
    if(ccpDistance(self.mySprite.position,s.mySprite.position) <30)
    {
    if([self checkCollisions:[theGame myRect:s.mySprite]])
    {
    [s damage];
    }
    }
    }

    break;

    case 2:
    [self.mySprite
    setPosition:ccp(self.mySprite.position.x,self.mySprite.position.y +
    self.firingSpeed)];

    if(ccpDistance(self.mySprite.position,
    theGame.hero.mySprite.position) < 30)
    {
    if([self checkCollisions:[theGame myRect:theGame.hero.mySprite]])
    {
    [theGame.hero destroy];
    }
    }


    break;
    }

    if(self.mySprite.position.y >500 || self.mySprite.position.y <-20)
    {
    [self reset];
    }
    }

    That will check for a collision between the hero and the bullets fired by enemies.

  4. When the hero is hit, we call the GameLayer's loseLife method, and add it:

    -(void)loseLife
    {
    self.lives--;

    if(self.lives ==0)
    {
    //LOSE THE GAME
    //GO TO GAME OVER LAYER
    }
    }

  5. Run the game now. Try getting hit by a bullet shot from an enemy and by enemies themselves. When one of those collisions is triggered you should see the hero disappear and reappear a moment later as shown in the following screenshot:

What just happened?

Just as we made the bullets collide with enemies, we made them collide with the hero. We check whether it is close to the surrounding objects, then, if it is, we check their boundaries and if they are intersecting, we act accordingly. In the case of the hero, we make it go away and then run an action to make it return to the screen gracefully.

When the hero is hit, we call the loseLife method, which takes a life from the player, but nothing else happens. We will fix this when we make a Game Over screen.

That's all we would need to do for now regarding collisions.

Summary

In this article we saw how to handle accelerometer input and detect collisions.

In the next article we will see how to add more layers to your scenes and make a simple pause screen.


Further resources on this subject:


Cocos2d for iPhone 0.99 Beginner's Guide Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!
Published: December 2010
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Pablo Ruiz

Pablo Ruiz started his carreer as a developer working as PHP developer for a local government office in 2007. Before that, he would work in QA for a big multinational game development company.

While working as a PHP developer he would build big administrative and accounting systems which were used across all the offices for their daily work.

In 2008 he began working on some small C# games using the XNA framework in his spare time. At the time, the iPhone was gaining ground so he began working on a personal project, a little game called Aqua Rush. After releasing it and starting doing consultancy work for several companies he left his day job and began working as a freelance iPhone developer.

Half a year later, he founded, with his partner, their own company; Infinix Mobile Software Development in Argentina, where he employs and trains students to learn and master the art of programming.

Books From Packt

Drupal 7 Module Development
Drupal 7 Module Development

Panda3D 1.6 Game Engine Beginner's Guide
Panda3D 1.6 Game Engine Beginner's Guide

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

Blender 2.5 Materials and Textures Cookbook: RAW
Blender 2.5 Materials and Textures Cookbook: RAW

Inkscape 0.48 Essentials for Web Designers
Inkscape 0.48 Essentials for Web Designers

3D Graphics with XNA Game Studio 4.0
3D Graphics with XNA Game Studio 4.0

Learning Ext JS 3.2
Learning Ext JS 3.2

PHP jQuery Cookbook
PHP jQuery Cookbook

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software