## How To Create A Simple 2D iPhone Game with OpenGL ES 2.0 and GLKit – Part 2

Ray Wenderlich

This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.

Create a simple game for iOS - the hardcore way!

In this tutorial series, we are creating a very simple game for the iPhone using the lowest level APIs on iOS – OpenGL ES 2.0 and GLKit.

In the first part of the series, we created a basic OpenGL 2.0 and GLKit project, created a class for Sprites, rendered them to the scene, and made them move.

In this second and final part of the series, we’ll let the ninja shoot stars, kill monsters, and win the game!

Keep reading to become a code ninja and finish this game!

## Shooting Projectiles

We’re going to implement shooting by registering a tap gesture recognizer on the view. When we receive a tap, we’ll perform the following steps:

1. Convert the touch to OpenGL coordinates.
2. Figure out the direction (vector) of the touch, with respect to the ninja.
3. Normalize the vector so that it is a length of 1. This makes it easy to make a vector pointing in the same direction as the offset vector, but with a particular length, which we’ll need in the next step.
4. Multiply the normalized offset by the velocity we want the ninja star to move. We now have a vector pointing in the same direction as the diference between the touch and the ninja, but a length based on where we want the ninja star to be after 1 second.
5. Create a ninja star sprite and set its velocity based on the above.

Here’s a diagram of the above:

Now let’s see this in code. Make the following changes to SGGViewController.m:

 ```// Add to bottom of viewDidLoad UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapFrom:)]; [self.view addGestureRecognizer:tapRecognizer];   // Add new method to file (anywhere) - (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {   // 1 CGPoint touchLocation = [recognizer locationInView:recognizer.view]; touchLocation = CGPointMake(touchLocation.x, 320 - touchLocation.y);   // 2 GLKVector2 target = GLKVector2Make(touchLocation.x, touchLocation.y); GLKVector2 offset = GLKVector2Subtract(target, self.player.position);   // 3 GLKVector2 normalizedOffset = GLKVector2Normalize(offset);   // 4 static float POINTS_PER_SECOND = 480; GLKVector2 moveVelocity = GLKVector2MultiplyScalar(normalizedOffset, POINTS_PER_SECOND);   // 5 SGGSprite * sprite = [[SGGSprite alloc] initWithFile:@"Projectile.png" effect:self.effect]; sprite.position = self.player.position; sprite.moveVelocity = moveVelocity; [self.children addObject:sprite];   }```

The numbered steps here correspond to the same steps described above – flip back if you’re confused.

Compile and run, and now you should be able to tap to shoot ninja stars!

## Collision Detection

So now we have shurikens flying everywhere – but what our ninja really wants to do is to lay some smack down. So let’s add in some code to detect when our projectiles intersect our targets.

First, we need to add a new method to SGGSprite in order to return the bounding box for the sprite on the screen. We need this so we can check if two sprites collide.

 `- (CGRect)boundingBox;`

And then add the implementation to SGGSprite.m:

 ```- (CGRect)boundingBox { CGRect rect = CGRectMake(self.position.x, self.position.y, self.contentSize.width, self.contentSize.height); return rect; }```

The next thing we have to do is keep better track of the targets and projectiles currently in the scene. Make the following changes to SGGViewController.m:

 ```// Add to private @interface @property (strong) NSMutableArray *projectiles; @property (strong) NSMutableArray *targets; @property (assign) int targetsDestroyed;   // Add to synthesize section @synthesize projectiles = _projectiles; @synthesize targets = _targets; @synthesize targetsDestroyed = _targetsDestroyed;   // Add to bottom of viewDidLoad self.projectiles = [NSMutableArray array]; self.targets = [NSMutableArray array];   // Add to bottom of handleTapFrom [self.projectiles addObject:sprite];   // Add to bottom of addTarget [self.targets addObject:target];```

Now we have an array of the targets and projectiles currently in the scene. We also added a variable to keep track of the number of targets destroyed (we’ll need this later).

Finally, add the meat of the logic to the beginning of the update method:

 ```NSMutableArray * projectilesToDelete = [NSMutableArray array]; for (SGGSprite * projectile in self.projectiles) {   NSMutableArray * targetsToDelete = [NSMutableArray array]; for (SGGSprite * target in self.targets) { if (CGRectIntersectsRect(projectile.boundingBox, target.boundingBox)) { [targetsToDelete addObject:target]; } }   for (SGGSprite * target in targetsToDelete) { [self.targets removeObject:target]; [self.children removeObject:target]; _targetsDestroyed++; }   if (targetsToDelete.count > 0) { [projectilesToDelete addObject:projectile]; } }   for (SGGSprite * projectile in projectilesToDelete) { [self.projectiles removeObject:projectile]; [self.children removeObject:projectile]; }```

The above should be pretty clear. We just iterate through our projectiles and targets, creating rectangles corresponding to their bounding boxes, and use CGRectIntersectsRect to check for intersections. If any are found, we remove them from the scene and from the arrays. Note that we have to add the objects to a “toDelete” array because you can’t remove an object from an array while you are iterating through it. Again, there are more optimal ways to implement this kind of thing, but I am going for the simple approach.

Compile and run, and now when your projectiles intersect targets they should disappear!

## Gratuitous Music and Sound Effects

If you’ve been following the tutorials on this site, you’ll know that we can never leave you hanging without some awesome music and sound effects for our game tutorials :]

You should already have some cool background music I made (background-music-aac.caf) and my awesome pew-pew sound effect (pew-pew.wav) in your project, from the resources for this tutorial that you added to the project in part 1.

We’re going to cheat a little bit in this part of the tutorial and use a premade sound engine, CocosDenshion. CocosDenshion is actually the sound engine that comes with Cocos2D, but you can use it in any project just by including the files.

We could play the music and effects ourselves by using the easy-to-use AVAudioPlayer, but you’ll find that performance is not up-to-snuff for playing rapid sound effects like we want for this game. Alternatively you could use OpenAL yourself, but the focus of this tutorial is not on OpenAL so we are going to go the easy route.

If you try to compile your project you’ll get a ton of errors, because CocosDenshion is not ARC compatible. Luckily fixing this is easy. Select your project in the Project Navigator, select the SimpleGLKitGame target, and go to the Build Phases tab.

Double click CDAudioManager.m, and a popup will appear. Enter -fno-objc-arc into the box – this makes them compile without ARC support.

Sadly you can only do one file at a time, so repeat this process for all 4 of the CocosDenshion files:

• CDAudioManager.m
• CDOpenALSupport.m
• CocosDenshion.m
• SimpleAudioEngine.m

CocosDension also requires some libraries. Sitll in the Build Phases tab, expand the Link Binary With Libraries section and add the following libraries:

• AudioToolbox.framework
• OpenAL.framework
• AVFoundation.framework

Your project should build OK now. Phew – hard part is done – now let’s play some music! Make the following changes to SGGViewController.m:

 ```// Add to top of file #import "SimpleAudioEngine.h"   // Add to end of viewDidLoad [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"]; [[SimpleAudioEngine sharedEngine] preloadEffect:@"pew-pew.wav"];   // Add to end of handleTapFrom [[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew.wav"];```

Compile and run, and enjoy the groovy tunes!

## The Great Refactor

The only step remaining in our game is to handle the win/lose condition. When we did this with Cocos2D, we would simply switch to a different game scene that would display some text showing if the user won or lost, and then restart the game.

That was trivial with Cocos2D, but would be a bit of a pain now. Right now we’ve hard-coded all of our game logic into the SGGViewController.m, which kind of acts like the “one true scene.”

We could cheat and add logic into the SGGViewController.m to clear out all the nodes, display a win/lose, and restart the game, but it would be better to refactor our code a bit into using a node hierarchy.

Right now the SGGViewController.m contains a list of sprites to display. But why not make it so that it just has a root “node” to display, and that “node” can have children, and so on? This way we can have a node that is a “scene” that contains the action scene, and another for the “game over” scene.

This is also handy for situations where you want to have other parent/child relationships, so that moving the parent node moves the child node, and so on.

There’s a good bit of refactoring to do, so if you want to call it a day at this point, here’s the finished project without refactoring.

## Sprites to Nodes

The first thing we’re going to do is create a class for a node, and move some of the code that’s currently in the SGGViewController and the SGGSprite to this new class.

Create a new file with the iOS\Cocoa Touch\Objective-C class template. Enter SGGNode for the Class, NSObject for the Subclass, click Next, and click Create.

Open SGGNode.h and replace it with the following:

 ```#import #import   @interface SGGNode : NSObject   @property (assign) GLKVector2 position; @property (assign) CGSize contentSize; @property (assign) GLKVector2 moveVelocity; @property (retain) NSMutableArray * children;   - (void)renderWithModelViewMatrix:(GLKMatrix4)modelViewMatrix; - (void)update:(float)dt; - (GLKMatrix4) modelMatrix:(BOOL)renderingSelf; - (CGRect)boundingBox; - (void)addChild:(SGGNode *)child;   @end```

Here we bring in several properties from SGGSprite (position, contentSize, and moveVelocity), as well as the children property from SGGViewController.

We also bring in some methods from SGGSprite (render, update, modelMatrix, boundingBox). Note that render has been modified to take a model view matrix as parameter, and modelMatrix has a flag – more on this later.

Next open SGGNode.m and replace it with the following:

 ```#import "SGGNode.h"   @implementation SGGNode @synthesize position = _position; @synthesize contentSize = _contentSize; @synthesize moveVelocity = _moveVelocity; @synthesize children = _children;   - (id)init { if ((self = [super init])) { self.children = [NSMutableArray array]; } return self; }   - (void)renderWithModelViewMatrix:(GLKMatrix4)modelViewMatrix { GLKMatrix4 childModelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, [self modelMatrix:NO]); for (SGGNode * node in self.children) { [node renderWithModelViewMatrix:childModelViewMatrix]; } }   - (void)update:(float)dt {   for (SGGNode * node in self.children) { [node update:dt]; }   GLKVector2 curMove = GLKVector2MultiplyScalar(self.moveVelocity, dt); self.position = GLKVector2Add(self.position, curMove);   }   - (GLKMatrix4) modelMatrix:(BOOL)renderingSelf {   GLKMatrix4 modelMatrix = GLKMatrix4Identity; modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0); if (renderingSelf) { modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.width/2, -self.contentSize.height/2, 0); } return modelMatrix;   }   - (CGRect)boundingBox { CGRect rect = CGRectMake(self.position.x, self.position.y, self.contentSize.width, self.contentSize.height); return rect; }   - (void)addChild:(SGGNode *)child { [self.children addObject:child]; }   @end```

This code is mostly moving methods from SGGSprite.h to SGGSprite.m. However, there are a few changes to point out:

• Render method. The render method takes a paremter of the current transform so far. For example, if its parent has moved 100 points to the right, this will be a matrix representing a translation of 100 points to the right. It then updates the matrix with its own transform, and calls render on all its children.
• Update method. The main difference here is that it calls update on all of its children.
• modelMatrix method. This takes a parameter to see if it’s rendering itself. If it is, we use the “anchor point” to shift the sprite to the lower left to render the center of the texture at the point. Otherwise, we don’t want it to affect the matrix, so we don’t do anything with the anchor point. Note that Cocos2D does not seem to do things this way (I don’t understand why not, can someone explain)?

Now with our new SGGNode class, we can greatly simplify SGGSprite. Replace SGGSprite.h with the following:

 ```#import #import #import "SGGNode.h"   @interface SGGSprite : SGGNode   - (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect;   @end```

And replace SGGSprite.m with the following:

 ```#import "SGGSprite.h"   typedef struct { CGPoint geometryVertex; CGPoint textureVertex; } TexturedVertex;   typedef struct { TexturedVertex bl; TexturedVertex br; TexturedVertex tl; TexturedVertex tr; } TexturedQuad;   @interface SGGSprite()   @property (strong) GLKBaseEffect * effect; @property (assign) TexturedQuad quad; @property (strong) GLKTextureInfo * textureInfo;   @end   @implementation SGGSprite @synthesize effect = _effect; @synthesize quad = _quad; @synthesize textureInfo = _textureInfo;   - (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect { if ((self = [super init])) { self.effect = effect;   NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil];   NSError * error; NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil]; self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error]; if (self.textureInfo == nil) { NSLog(@"Error loading file: %@", [error localizedDescription]); return nil; }   self.contentSize = CGSizeMake(self.textureInfo.width, self.textureInfo.height);   TexturedQuad newQuad; newQuad.bl.geometryVertex = CGPointMake(0, 0); newQuad.br.geometryVertex = CGPointMake(self.textureInfo.width, 0); newQuad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height); newQuad.tr.geometryVertex = CGPointMake(self.textureInfo.width, self.textureInfo.height);   newQuad.bl.textureVertex = CGPointMake(0, 0); newQuad.br.textureVertex = CGPointMake(1, 0); newQuad.tl.textureVertex = CGPointMake(0, 1); newQuad.tr.textureVertex = CGPointMake(1, 1); self.quad = newQuad;   } return self; }   - (void)renderWithModelViewMatrix:(GLKMatrix4)modelViewMatrix {   [super renderWithModelViewMatrix:modelViewMatrix];   self.effect.texture2d0.name = self.textureInfo.name; self.effect.texture2d0.enabled = YES; self.effect.transform.modelviewMatrix = GLKMatrix4Multiply(modelViewMatrix, [self modelMatrix:YES]);   [self.effect prepareToDraw];   long offset = (long)&_quad;   glEnableVertexAttribArray(GLKVertexAttribPosition); glEnableVertexAttribArray(GLKVertexAttribTexCoord0);   glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex))); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex)));   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);   }   @end```

Not much has changed here except we removed a lot of code.

OK, let’s try this out! Go to SGGViewController.m and replace the line that calls render in glkView:drawInRect with the following:

 `[sprite renderWithModelViewMatrix:GLKMatrix4Identity];`

Compile and run, and your project should work as normal.

“But wait a minute,” you might ask, “why did we bother doing all that refactoring, when nothing changed!”

Well, check out something we can do now that we couldn’t have done before. Let’s say we want to give this ninja a “posse” to follow him around the map. Previously we would have had to add 3 sprites, and manually set them to move all at the same rate. Now, we can add the posse as children to the main ninja, and when we move the main ninja his posse will follow!

Try this out by adding the following code to the bottom of viewDidLoad:

 ```self.player.moveVelocity = GLKVector2Make(25, 0); SGGSprite * posse1 = [[SGGSprite alloc] initWithFile:@"Player.png" effect:self.effect]; posse1.position = GLKVector2Make(-25, 50); [self.player addChild:posse1]; SGGSprite * posse2 = [[SGGSprite alloc] initWithFile:@"Player.png" effect:self.effect]; posse2.position = GLKVector2Make(-25, -50); [self.player addChild:posse2];```

Compile and run, and now we have a ninja posse!

Right now you can set the position of a node, but there are two other attributes on a node you commonly want to set: rotation and scale.

We won’t actually be using either of these in our game, but I wanted to show you guys how to do this because most likely you will need this in your games.

Make the following changes to SGGNode.h:

 ```@property (assign) float rotation; @property (assign) float scale; @property (assign) float rotationVelocity; @property (assign) float scaleVelocity;```

Here we add a property for the rotation (in degrees) and the scale (1.0 = normal size). We also add a rotationVelocity and scaleVelocity for a easy way to get these values to change over time.

Next switch to SGGNode.m and make the following changes:

 ```// Add in @synthesize section @synthesize rotation = _rotation; @synthesize scale = _scale; @synthesize rotationVelocity = _rotationVelocity; @synthesize scaleVelocity = _scaleVelocity;   // Add in init self.scale = 1;   // Add at bottom of update float curRotate = self.rotationVelocity * dt; self.rotation = self.rotation + curRotate;   float curScale = self.scaleVelocity * dt; self.scale = self.scale + curScale;   // Add at bottom of modelMatrix: method float radians = GLKMathDegreesToRadians(self.rotation); modelMatrix = GLKMatrix4Rotate(modelMatrix, radians, 0, 0, 1);   modelMatrix = GLKMatrix4Scale(modelMatrix, self.scale, self.scale, 0);```

The key part here is in the update method – we use the GLKMath methods to update our matrix based on the rotation and scale values.

Let’s try this out. Switch to SGGVIiewController.m and add these lines to the bottom of viewDidLoad:

 ```self.player.rotationVelocity = 45; self.player.scaleVelocity = 0.1;```

Now you have a rotating and scaling posse!

However we don’t want a posse at all, so go ahead and comment out the lines that create the posse :]

Also, there’s a problem we have to fix. The bounding box method currently doesn’t take into effect the scale or the rotation of an item. This will mess up our collision detection if we were to scale a monster to be 3x the size, for example.

Try it out for yourself. Add the following to the end of addTarget:

 `target.scale = 3.0;`

Compile and run, and shoot at a monster but aim for the very bottom or very top. You’ll notice that it sometimes doesn’t register the collision, because it’s using the monster’s original size for the bounding box.

To fix this, update the boundingBox method in SGGNode.m to the following:

 ```- (CGRect)boundingBox { CGRect rect = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height); GLKMatrix4 modelMatrix = [self modelMatrix:YES]; CGAffineTransform transform = CGAffineTransformMake(modelMatrix.m00, modelMatrix.m01, modelMatrix.m10, modelMatrix.m11, modelMatrix.m30, modelMatrix.m31); return CGRectApplyAffineTransform(rect, transform); }```

Here we start with the non-translated rectangle for the node, and then get the model view matrix. We then need to convert our 4×4 matrix into a CGAffineTransform. Don’t worry if you don’t understand what line 3 does, just know that it converts a 4×4 matrix to a CGAffineTransform.

We then use a handy built-in method to apply a CGAffineTransform to a rectangle, and give us the closest matching bounding rectangle.

Compile and run, and now you should be able to shoot the giant monsters just fine!

When you’re done, comment out the line that sets them to the larger size as we won’t be needing that.

## View Controller to Scene

Now that we have our node class in place, we can start moving the game logic that is in SGGViewController.m into a scene class.

First things first. We’re going to add a stub method onto SGGNode to handle receiving taps (we’ll need this later on), so add the following method declaration to SGGNode.h:

 `- (void)handleTap:(CGPoint)touchLocation;`

And the following stub declaration to SGGNode.m:

 ```- (void)handleTap:(CGPoint)touchLocation { }```

You could modify this to forward touches to children based on their bounding boxes, etc. but again to keep thing simple we’re just going to implement what we need here – which is the scene getting info about when there’s a touch.

Next create a new file with the iOS\Cocoa Touch\Objective-C class template. Enter SGGActionScene for the Class, SGGNode for the Subclass, click Next, and click Create.

Open SGGActionScene.h and replace it with the following:

 ```#import "SGGNode.h"   @interface SGGActionScene : SGGNode   - (id)initWithEffect:(GLKBaseEffect *)effect;   @end```

Then open SGGActionScene.m and replace it with the following:

 ```#import "SGGActionScene.h" #import "SGGSprite.h" #import "SimpleAudioEngine.h"   @interface SGGActionScene () @property (strong) GLKBaseEffect * effect; @property (strong) SGGSprite * player; @property (assign) float timeSinceLastSpawn; @property (strong) NSMutableArray *projectiles; @property (strong) NSMutableArray *targets; @property (assign) int targetsDestroyed; @end   @implementation SGGActionScene @synthesize effect = _effect; @synthesize player = _player; @synthesize timeSinceLastSpawn = _timeSinceLastSpawn; @synthesize projectiles = _projectiles; @synthesize targets = _targets; @synthesize targetsDestroyed = _targetsDestroyed;   - (id)initWithEffect:(GLKBaseEffect *)effect { if ((self = [super init])) { self.effect = effect;   self.player = [[SGGSprite alloc] initWithFile:@"Player.png" effect:self.effect]; self.player.position = GLKVector2Make(self.player.contentSize.width/2, 160);   [self.children addObject:self.player];   self.projectiles = [NSMutableArray array]; self.targets = [NSMutableArray array];   [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"]; [[SimpleAudioEngine sharedEngine] preloadEffect:@"pew-pew.wav"];   } return self; }   - (void)handleTap:(CGPoint)touchLocation {   // 2 GLKVector2 target = GLKVector2Make(touchLocation.x, touchLocation.y); GLKVector2 offset = GLKVector2Subtract(target, self.player.position);   // 3 GLKVector2 normalizedOffset = GLKVector2Normalize(offset);   // 4 static float POINTS_PER_SECOND = 480; GLKVector2 moveVelocity = GLKVector2MultiplyScalar(normalizedOffset, POINTS_PER_SECOND);   // 5 SGGSprite * sprite = [[SGGSprite alloc] initWithFile:@"Projectile.png" effect:self.effect]; sprite.position = self.player.position; sprite.moveVelocity = moveVelocity; [self.children addObject:sprite];   [self.projectiles addObject:sprite];   [[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew.wav"]; }   - (void)addTarget { SGGSprite * target = [[SGGSprite alloc] initWithFile:@"Target.png" effect:self.effect]; [self.children addObject:target];   int minY = target.contentSize.height/2; int maxY = 320 - target.contentSize.height/2; int rangeY = maxY - minY; int actualY = (arc4random() % rangeY) + minY;   target.position = GLKVector2Make(480 + (target.contentSize.width/2), actualY);   int minVelocity = 480.0/4.0; int maxVelocity = 480.0/2.0; int rangeVelocity = maxVelocity - minVelocity; int actualVelocity = (arc4random() % rangeVelocity) + minVelocity;   target.moveVelocity = GLKVector2Make(-actualVelocity, 0);   [self.targets addObject:target];   }   - (void)update:(float)dt {   [super update:dt];   NSMutableArray * projectilesToDelete = [NSMutableArray array]; for (SGGSprite * projectile in self.projectiles) {   NSMutableArray * targetsToDelete = [NSMutableArray array]; for (SGGSprite * target in self.targets) { if (CGRectIntersectsRect(projectile.boundingBox, target.boundingBox)) { [targetsToDelete addObject:target]; } }   for (SGGSprite * target in targetsToDelete) { [self.targets removeObject:target]; [self.children removeObject:target]; _targetsDestroyed++; }   if (targetsToDelete.count > 0) { [projectilesToDelete addObject:projectile]; } }   for (SGGSprite * projectile in projectilesToDelete) { [self.projectiles removeObject:projectile]; [self.children removeObject:projectile]; }   self.timeSinceLastSpawn += dt; if (self.timeSinceLastSpawn > 1.0) { self.timeSinceLastSpawn = 0; [self addTarget]; } }   @end```

Wow – a lotta code here, but it is literally ripped out from SGGViewController.m, so it’s all stuff we covered before. You can just copy/paste it in there.

Now we can greatly simplify SGGViewController.m. Open it up and replace it with the following:

 ```#import "SGGViewController.h" #import "SGGActionScene.h"   @interface SGGViewController () @property (strong, nonatomic) EAGLContext *context; @property (strong) GLKBaseEffect * effect; @property (strong) SGGNode * scene; @end   @implementation SGGViewController @synthesize effect = _effect; @synthesize context = _context; @synthesize scene = _scene;   - (void)viewDidLoad { [super viewDidLoad];   self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];   if (!self.context) { NSLog(@"Failed to create ES context"); }   GLKView *view = (GLKView *)self.view; view.context = self.context; [EAGLContext setCurrentContext:self.context];   self.effect = [[GLKBaseEffect alloc] init];   GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, 480, 0, 320, -1024, 1024); self.effect.transform.projectionMatrix = projectionMatrix;   UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapFrom:)]; [self.view addGestureRecognizer:tapRecognizer];   self.scene = [[SGGActionScene alloc] initWithEffect:self.effect];   }   - (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {   // 1 CGPoint touchLocation = [recognizer locationInView:recognizer.view]; touchLocation = CGPointMake(touchLocation.x, 320 - touchLocation.y);   [self.scene handleTap:touchLocation];   }   - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return UIInterfaceOrientationIsLandscape(interfaceOrientation); }   #pragma mark - GLKViewDelegate   - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND);   [self.scene renderWithModelViewMatrix:GLKMatrix4Identity]; }   - (void)update {   [self.scene update:self.timeSinceLastUpdate]; }   @end```

Ah, much cleaner, eh? Compile and run, and the game should work just as before – but nicely abstracted into a scene class.

Congratulations, the refactoring is complete! Now we can finally move on to creating a win/lose scene :]

## Creating a Win/Lose Scene

To allow replacing the scene, we’re going to make the scene property on SGGViewController public. Then other classes can get a reference to the SGGViewController and replace it directly. There are better ways to do this, but again – keeping it simple ;]

So go to SGGViewController.h and add the following property:

 `@property (strong) SGGNode * scene;`

And switch to SGGViewController.m and comment it out in the private interface:

 `//@property (strong) SGGNode * scene;`

OK cool – now let’s create our Game Overs scene. Create a new file with the iOS\Cocoa Touch\Objective-C class template. Enter SGGGameOverScene for the Class, SGGNode for the Subclass, click Next, and click Create.

Open SGGGameOverScene.h and replace it with the following:

 ```#import "SGGNode.h"   @interface SGGGameOverScene : SGGNode   - (id)initWithEffect:(GLKBaseEffect *)effect win:(BOOL)win;   @end```

Then open SGGGameOverScene.m and replace it with the following:

 ```#import "SGGGameOverScene.h" #import "SGGSprite.h" #import "SGGViewController.h" #import "SGGActionScene.h" #import "SGGAppDelegate.h"   @interface SGGGameOverScene () @property (assign) float timeSinceInit; @property (strong) GLKBaseEffect * effect; @end   @implementation SGGGameOverScene @synthesize timeSinceInit = _timeSinceInit; @synthesize effect = _effect;   - (id)initWithEffect:(GLKBaseEffect *)effect win:(BOOL)win { if ((self = [super init])) {   self.effect = effect; if (win) { SGGSprite * winSprite = [[SGGSprite alloc] initWithFile:@"YouWin.png" effect:effect]; winSprite.position = GLKVector2Make(240, 160); [self addChild:winSprite]; } else { SGGSprite * loseSprite = [[SGGSprite alloc] initWithFile:@"YouLose.png" effect:effect]; loseSprite.position = GLKVector2Make(240, 160); [self addChild:loseSprite]; }   } return self; }   - (void)update:(float)dt {   self.timeSinceInit += dt; if (self.timeSinceInit > 3.0) { SGGActionScene * scene = [[SGGActionScene alloc] initWithEffect:self.effect]; SGGAppDelegate * delegate = [[UIApplication sharedApplication] delegate]; UIWindow * mainWindow = [delegate window]; SGGViewController * viewController = (SGGViewController *) mainWindow.rootViewController; viewController.scene = scene; }   }   @end```

This simply places a premade image of “You Win” or “You Lose” in the middle of the screen on startup. After a certain amount of time elapses, it creates a new ActionScene and sets the currently running scene to this new scene.

Only thing left to do is run the GameOverScene on win or lose! Open up SGGActionScene.m and make the following changes:

 ```// Add to top of file #import "SGGGameOverScene.h" #import "SGGAppDelegate.h" #import "SGGViewController.h"   // Add new method, before update method - (void)gameOver:(BOOL)win { SGGGameOverScene * gameOver = [[SGGGameOverScene alloc] initWithEffect:self.effect win:win];   SGGAppDelegate * delegate = [[UIApplication sharedApplication] delegate]; UIWindow * mainWindow = [delegate window]; SGGViewController * viewController = (SGGViewController *) mainWindow.rootViewController; viewController.scene = gameOver; }   // Add at bottom of update method if (_targetsDestroyed > 5) { [self gameOver:YES]; return; }   BOOL lose = NO; for (int i = self.children.count - 1; i >= 0; i--) {   SGGSprite * sprite = [self.children objectAtIndex:i];   if (sprite.position.x <= -sprite.contentSize.width/2 || sprite.position.x > 480 + sprite.contentSize.width/2 || sprite.position.y <= -sprite.contentSize.height/2 || sprite.position.y >= 320 + sprite.contentSize.height/2) {   if ([self.targets containsObject:sprite]) { [self.targets removeObject:sprite]; [self.children removeObjectAtIndex:i]; lose = YES; } else if ([self.projectiles containsObject:sprite]) { [self.projectiles removeObject:sprite]; [self.children removeObjectAtIndex:i]; } } } if (lose) { [self gameOver:NO]; }```

Here we add the game logic. If the player destroys more than 5 targets, they win, so create and display a game over scene.

We also add some cleanup code to remove sprites that are outside the bounds of the scene to avoid endless memory allocations. If a monster escapes the boundary of the screen, the game is over.

Guess what – you’re done! Compile and run, and see if you can beat the game!

## Where To Go From Here?

Here is the finished and refactored project from this tutorial.

Congratulations, you have made a complete game with OpenGL ES 2.0 and GLKit! You now have experience with creating a very simple game engine, and if you like you could use this as the basis for your own game engine.

This tutorial should also have given you a better appreciation for Cocos2D and all that it does for you :] Since this engine is organized somewhat similarly to Cocos2D, you should also better understand how it works.

If you have any questions or comments on OpenGL ES 2.0, GLKit, or this tutorial, please join the forum discussion below!

This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.

Ray Wenderlich

Ray is part of a great team - the raywenderlich.com team, a group of over 100 developers and editors from across the world. He and the rest of the team are passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

[ 1 , 2 ]
• why when Sprite added to scene , all sprite is bad quality ? how to fix this problem ? thx a lot!
vlve
• Should the projectiles and targets be deleted once they're off the screen? I'm thinking they linger around in memory flying around past the bounds of the screen the whole time.
dejan9393
• Hi,

I think I've found a small error.
On refactored version of SGGViewController, the scene property is declared with the wrong class : SGGNode. The correct class is SGGActionScene.

Thank you for this great tutorial.

Regards

Dominique
Dominique
• I'm just starting to teach myself some game development for iOS and I appreciate that you put up the downloadable, runnable right away finished package. I really wanted to see first that it runs on my phone before I start modifying it to learn the details.
It works fine which for me is a great starting point.

Thanks!
• Great tutorials.
This is definitly the best information i have found on OpenGL Es with glkit.

It was easy to follow, and i feal im starting to get the hang of it.
For my project i need to implement Box2D, that was actually easier than i thought.
But i have problem with implenting a debugdrawer, have anyone here built something like this?
And maybe can point me in the right direction of creating one?

SteinOveHelset
• Great tutorial thank you! This has helped me get up and running with OpenGL for IOS and I'm happy coding away now.

One quick note! There are a few memory leaks that surface due to the fact that we never destroy enemies that pass the player or projectiles that don't actually collide with enemies. (I think there is another leak during the sprite creation process during addTarget but I haven't tracked it down).

I have some code to fix the former issues though. Feel free to include or rework as you see fit!

The issue is within the - (void)update method in SGGViewController. The entire fixed method is below:

Code: Select all
``` - (void)update {    NSMutableArray *projectilesToDelete = [NSMutableArray array];     NSMutableArray *targetsToDelete = [NSMutableArray array];         // Get targets that were hit     for(SGGSprite *projectile in self.projectiles) {         for (SGGSprite *target in self.targets) {            if (CGRectIntersectsRect(projectile.boundingBox, target.boundingBox))                [targetsToDelete addObject:target];         }                 if (targetsToDelete.count > 0)            [projectilesToDelete addObject:projectile];                 // Add off screen projectiles to destroy        CGFloat rightPosition = 480 + projectile.contentSize.width / 2;         CGFloat upPosition = 320 + projectile.contentSize.height / 2;         CGFloat downPosition = 0 - projectile.contentSize.height / 2;         CGFloat leftPosition = 0 - projectile.contentSize.width / 2;                bool isOffScreen = projectile.position.x > rightPosition ||            projectile.position.y > upPosition ||            projectile.position.y < downPosition ||             projectile.position.x < leftPosition;            if (isOffScreen)            [projectilesToDelete addObject:projectile];     }         // Get off screen targets     for (SGGSprite *offScreenTarget in self.targets) {         // Add targets that are off screen         if (offScreenTarget.position.x < 0 - offScreenTarget.contentSize.width / 2)             [targetsToDelete addObject:offScreenTarget];     }         // Destroy targets     for (SGGSprite *target in targetsToDelete) {         [self.targets removeObject:target];         [self.children removeObject:target];     }         // Destroy projectiles     for (SGGSprite *projectile in projectilesToDelete) {        [self.projectiles removeObject:projectile];         [self.children removeObject:projectile];    }    self.timeSinceLastSpawn += self.timeSinceLastUpdate;     if (self.timeSinceLastSpawn > 1.0) {        self.timeSinceLastSpawn = 0;         [self addTarget];     }     for (SGGSprite * sprite in self.children) {         [sprite update:self.timeSinceLastUpdate];     } } ```
DrivenByHim
• Hey I'm having a hell of a time trying to refactor the player sprite out to its own class is there anyone that can give me some advice on this i would like for it to have its own update method as well.
MxWeeks
• Hi folks,

I am using the code from this example as an engine for a game I am writing which will draw around ~80 sprites to the screen every update. I am getting a pretty slow frame rate on my iPhone during testing. Are there any ways I can speed it up? The sprites I am drawing are just 40x40 pixel .png files. I've read on line that I shouldn't be calling prepareToDraw for every sprite rendering, but I don't know how else to do it.

Thanks!
--B
Bianchi
• You have helped me with man other things, but this game did not work for me, and I am not sure why. Here is the error :

2014-11-03 13:15:06.427 SimpleGLKitGame[12042:907] *** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeInt32ForKey:]: value for key (GLKViewControllerPauseOnWillResignActiveCoderKey) is not an integer number'
*** First throw call stack:
(0x1bb3012 0x1936e7e 0x1bb2deb 0x13696cd 0x138d530 0x13694ac 0x83135d 0xa97a36 0x13676a9 0x13665c7 0xa975c8 0x13676a9 0x1368c37 0x13403b5 0x13676a9 0x13665c7 0xa969f0 0xcc254a 0xcc26c3 0x87a71e 0x87a9a2 0x879876 0x88acb5 0x88bbeb 0x87d698 0x2848df9 0x2848ad0 0x1b28bf5 0x1b28962 0x1b59bb6 0x1b58f44 0x1b58e1b 0x87917a 0x87affc 0x234d 0x22b5)
libc++abi.dylib: terminate called throwing an exception
(lldb)
stephenmwyatt
• I had this error and have no idea on what is wrong:

2014-11-03 13:15:06.427 SimpleGLKitGame[12042:907] *** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeInt32ForKey:]: value for key (GLKViewControllerPauseOnWillResignActiveCoderKey) is not an integer number'
*** First throw call stack:
(0x1bb3012 0x1936e7e 0x1bb2deb 0x13696cd 0x138d530 0x13694ac 0x83135d 0xa97a36 0x13676a9 0x13665c7 0xa975c8 0x13676a9 0x1368c37 0x13403b5 0x13676a9 0x13665c7 0xa969f0 0xcc254a 0xcc26c3 0x87a71e 0x87a9a2 0x879876 0x88acb5 0x88bbeb 0x87d698 0x2848df9 0x2848ad0 0x1b28bf5 0x1b28962 0x1b59bb6 0x1b58f44 0x1b58e1b 0x87917a 0x87affc 0x234d 0x22b5)
libc++abi.dylib: terminate called throwing an exception
(lldb)
stephenmwyatt
• The game doesn't render on my iPad air 2, but works fine on any iPhones I have. Even if I set the project's device family to be iPad instead of iPhone. The game runs, responds to touch events and thinks it is rendering, but nothing gets drawn. Anyone else run into this?

Thanks!
--Brian
Bianchi
• Ray,

Can you add a tutorial on moving this into a small sprite sheet? Having a hard time with this.
nick5454
• how do you get rid of the You Win screen. I want it to keep going until I die.
hiyperion
[ 1 , 2 ]

# Unity Starter Kit!

We are considering writing a new starter kit on making a game in C# with Unity.

Would this be something you'd be interested in, and if so which option would you prefer?

... 12 total!

... 10 total!

... 52 total!

... 9 total!

... 11 total!

... 10 total!

... 9 total!

... 10 total!