How To Create A Simple 2D iPhone Game with OpenGL ES 2.0 and GLKit – Part 1
This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer. There are a lot of great tutorials out there on OpenGL ES 2.0, but they usually stop after drawing a rotating cube on the screen. How to take that rotating box and turn it into a full game is […] By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Create A Simple 2D iPhone Game with OpenGL ES 2.0 and GLKit – Part 1
35 mins
This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.
There are a lot of great tutorials out there on OpenGL ES 2.0, but they usually stop after drawing a rotating cube on the screen.
How to take that rotating box and turn it into a full game is usually left as an exercise to the poor reader. But how do you create classes for sprites, move them around, add your game logic, and handle scene management?
That, my friends, is where this tutorial series comes in! In this tutorial series we’re going to take the simple 2D “pew-pew ninja” game from our beginner Cocos2D tutorial and implement it completely in OpenGL ES 2.0, with GLKit!
I’ve tried to make this tutorial series as similar as possible to the above Cocos2D tutorial so you can compare the two to see the differences in implementation if you are curious.
The goal of this tutorial series is to keep things as simple as possible, and walk you through the process step by step. By the end, you’ll have a basic starting point you can use for your own 2D game engine!
Before reading this tutorial series, I recommend reading the Beginning OpenGL ES 2.0 with GLKit tutorial series. It’s also helpful (but not necessary) to read the Intermediate OpenGL ES 2.0 with GLKit chapter from iOS 5 by Tutorials.
Once you’ve read that, keep reading to make a simple 2D game for the iPhone – the hardcore way! :]
Why OpenGL ES 2.0 and GLKit?
For the skeptics out there, let’s discuss why you might want to make a game engine with OpenGL ES 2.0 and GLKit in the first place.
If you’re already convinced that this is what you wnat to do, feel free to skip this section and go straight to “Getting Started” :]
Why Use OpenGL ES?
Open GL ES is the lowest level graphics API on iOS. It interacts directly with the graphics card and is very powerful, so is often used to make games and highly visual apps.
The only problem with OpenGL is that it has a notoriously large learning curve and you have to write a lot of engine code to get a simple game working. Because of this, many programmers prefer to use a game engine instead, such as Cocos2D, Corona, or Unity.
Under the hood, all of these engines use OpenGL ES – they just hide the lower level details from you to make things simpler.
Although these game engines are powerful and can save you a lot of time, it’s still fun, and a good learning experience, to practice building a game engine yourself – using raw OpenGL ES.
You’ll learn a ton about game programming and OpenGL ES in the process, and better understand how to accomplish the effects you want in your games – whether you’re using your own engine, or one written by a third party!
Why Use OpenGL ES 2.0?
I’ve already discussed the difference between OpenGL ES 1.0 and OpenGL ES 2.0 in the Beginnng OpenGL ES 2.0 with GLKit tutorial series.
But as a quick reminder, OpenGL ES 2.0 is the way of the future, and you can make cooler effects with OpenGL ES 2.0 because it has shader support.
Since most modern devices supports OpenGL ES 2.0 now, for new apps I’d recommend going straight to OpenGL ES 2.0.
And for beginners, I’d also recommend you go straight to learning OpenGL ES 2.0 and not even bother with OpenGL ES 1.0 (or trying to learn them both at once). This tutorial (and the others on the site) don’t assume you have any prior OpenGL ES 1.0 experience.
Why Use GLKit?
GLKit is a set of APIs that makes working with OpenGL ES on iOS much easier than it used to be.
“But wait a minute!”, you might be thinking, “I thought we were trying to go as low level as possible!”
That’s true, but using GLKit doesn’t hurt you at all (using it doesn’t make you lose anything form the learning experience), but it does help you a ton.
GLKit saves you from writing some extremely boring (and easy to get wrong) code regarding setting up a basic display, creating basic shaders, performing vector/matrix math, and loading textures.
The number of boilerplate lines of code it takes to get started will be reduced significantly, and you can keep the focus on the game engine itself.
Bad-Ass Challenge: GLKit Setup

How bad ass do you feel today?
To get started, we’re going to create a simple project using OpenGL ES 2.0 and GLKit that just renders a green screen. Rather than using the “OpenGL Game” template, we’re going to do this “from scratch” so you understand how everything fits together better.
This will be review if you’ve gone through the Beginning OpenGL ES 2.0 with GLKit tutorial series.
So if you’ve read that tutorial series, feel comfortable with the material, and are feeling particularly bad-ass, see if you can complete this challenge without reading the step-by-step instructions below:
- Starting with the “Empty Application” template, create a simple OpenGL ES 2.0/GLKit project that renders the screen green. It should use ARC, a Storyboard, a GLKViewController and a GLKView.
If you haven’t read that tutorial or aren’t feeling particularly bad-ass today, keep reading for step-by-step instructions :]
Getting Started
Open Xcode, and create a new project with the iOS\Application\Empty Application template. Enter SimpleGLKitGame for the Product Name, SGG (for Simple GLKit Game) for the Class Prefix, and iPhone for the Device Family. Make sure Use Automatic Reference Counting is selected, and click Next.
First of all, we want this game to run in landscape only, so select your project in the Project Navigator and select your SimpleGLKitGame target. In the Summary tab’s Supported Device Orientations section, unselect the Portrait orientation as follows:
Second, we want to use OpenGL ES 2.0 and GLKit in this project, so we need to add some frameworks. Still with your project and target selected, select Build Phases, Expand the Link Binary With Libraries section, and click the Plus button. From the drop-down list, select the following frameworks and click Add:
- QuartzCore.framework
- OpenGLES.framework
- GLKit.framework
Next, let’s add a Storyboard to the project. Create a new file with the iOS\User Interface\Storyboard template, select iPhone for the Device Family, and name it MainStoryboard.storyboard.
Open MainStoryboard.storyboard, and drag a GLKit View Controller onto the storyboard. Since it’s your first view controller, Xcode will automatically set it up as the initial view controller. You know this because there’s an arrow to the left of the view controller, and “Is Initial View Controller” is checked.
We’re going to need to add some custom code to the view controller, so let’s create a subclass. Create a new file with the iOS\Cocoa Touch\UIViewController subclass template. Enter SGGViewController for the Class, GLKViewController for the Subclass, make sure both checkboxes are unchecked, click Next, and click Create.
To make the compiler happy, add the following to the top of SGGViewController.h:
#import <GLKit/GLKit.h>
Now open MainStoryboard.storyboard again. Select your view controller, and in the Identity Inspector set the Class to SGGViewController.
Now time for the code! Open up SGGViewController.m and replace the contents with the following:
#import "SGGViewController.h"
@interface SGGViewController ()
@property (strong, nonatomic) EAGLContext *context;
@end
@implementation SGGViewController
@synthesize context = _context;
- (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];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
#pragma mark - GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
- (void)update {
}
@end
This is a bare bones implementation of a GLKViewController subclass. When the view loads it creates an OpenGL ES 2.0 context that it will use for further drawing, and associated it with the view. It also implements glkView:drawInRect to clear the screen to a green color. For review on this, check the previous tutorial.
One final step – we need to set up our project to use the storyboard we created. select your project in the Project Navigator and select your SimpleGLKitGame target. In the Summary Tab, set the Main Storyboard to MainStoryboard.storyboard.
Finally, open SGGAppDelegate.m and replace application:didFinishLaunchingWithOptions with the following (which will allow the main window to be created from the Storyboard rather than programatically):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
Compile and run, and enjoy your beautiful green screen! :]
Adding a Sprite
On to the fun part – adding a sprite to the scene!
First things first – you’ll need some artwork and other resources for the game. Go ahead and download the resources for this tutorial, and add them to your project.
To make things as easy as possible to understand, we’re going to implement a sprite class in the simplest possible way to start. We’ll come back and iterate over this class several times in the tutorial for a better implementation.
Create a new file with the iOS\Cocoa Touch\Objective-C class template. Enter SGGSprite for the Class, NSObject for the Subclass, click Next, and click Create.
Open SGGSprite.h and replace it with the following:
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
@interface SGGSprite : NSObject
- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect;
- (void)render;
@end
This is the interface for our sprite class. As you can see, right now we’re going to start as simple as possible. We’ll allow the user to specify the image file for the sprite to display, and the GLKBaseEffect (shader) that will render it. We also define a routine that the GLKViewController will call to render the sprite.
Now switch to SGGSprite.m and delete everything in the file. We’ll add the new code next – but there’s a fair bit of it, so we’re going to do it one chunk at a time. Start by pasting this in:
#import "SGGSprite.h"
typedef struct {
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
First we import our header file. Next, we start creating some structures that will store the information we neeed to render the sprite. This is the most important part to understand, so I’m going to go over this in gory detail.
The TexturedVertex is a structure that we will use to keep track of the information we need at each corner of the sprite. We create a second structure, TexturedQuad, to contain 4 TexturedVertex structures – one for each corner. Bl stands for bottom left, tr stands for top right, and so on.
The TexturedVertex structure contains two pieces of information we need for each corner: the point for where to draw it on the screen (kinda, more on this later), and the area of the texture that should be mapped to that spot.
We’re going to fill in the TexturedQuad (and its four TexturedVertex) structure with values based on the size of the sprite we’re displaying. For example, here’s how we’re going to set it up for our ninja sprite:
The geometry vertices here are the point at which we want each corner of the ninja to appear on the screen. We put his lower left corner at the bottom left of the screen (0,0). Then we don’t want to deform, stretch, or scale the ninja at all, so we have the top right corner be based on the size of the sprite itself (27x40px).
The texture vertices allow you to map what spot on the texture should map to each vertex. When you’re working with sprites, most of the time you want the bottom left corner of the texture to map to the bottom left corner of the sprite – and that’s exactly what we’re doing here.
If you’re wondering why the texture vertex values only go between 0 and 1, that’s just the way texture vertices work – a value of (0.5, 0.5) would be in the middle of a texture, for example.
Even though we’re only giving OpenGL the values at each corner of the sprite, it still knows how to render all the areas in between as well. It does this just by interpolating the values automatically. For example, the point (0,20) is halfway between the bottom left and the top left. OpenGL knows that the bottom left’s texture coordinate is (0,0) and the top left’s texture coordinate is (0,1), so it can choose the value halfway inbetween (0, 0.5) for the texture coordinate.
This is just defining the data structure that will hold the geometry and texture vertex values – we haven’t actually set it up yet. It’s coming in a bit. For now, add this next:
@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;
Here we create a private category of SGGSprite. This is a fancy way of creating private variables in Objective-C. Since we define them here, inside the implementation file (.m), nobody outside our class knows about these. This makes for better encapsulation, and is a general good practice.
We define and synthesize three private properties here:
- GLKBaseEffect * effect: The effect (shader) that we’ll use to render the sprite. More on this later.
- TexturedQuad quad: The instance of our TexturedQuad structure. We’ll fill this in as described above soon.
- GLKTextureInfo * textureInfo: We’re going to use GLKit’s GLKTextureLoader class to easily load our texture. It will return some info about the texture, which we’ll store here.
Continue on by adding this next:
- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect {
if ((self = [super init])) {
// 1
self.effect = effect;
// 2
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft,
nil];
// 3
NSError * error;
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
// 4
self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
if (self.textureInfo == nil) {
NSLog(@"Error loading file: %@", [error localizedDescription]);
return nil;
}
// TODO: Set up Textured Quad
}
return self;
}
Here we create the initializer for our class. Let’s go over this line by line:
- Stores the GLKBaseEffect that will be used to render the sprite.
- Sets up the options so that when we load the texture, the origin of the texture will be considered the bottom left. If you don’t do this, the origin will be the top left (which we don’t want, because it won’t match OpenGL’s coordinate system).
- Gets the path to the file we’re going to load. The filename is passed in. Note that if you pass nil as the type, it will allow you to enter the full filename in the first parameter. Believe it or not, I just learned this after 2 years of iOS dev :P
- Finally loads the texture with the handy GLKTextureLoader class. You should appreciate this – it used to take tons of code to accomplish this :]
Next add this after the TODO:
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;
This fills in our quad as described in the diagram above. I’ll post the diagram again here for handy reference.
Note that the texture vertex values will be the same no matter what sprite you use, since we always want to map the texture the same way with respect to the various corners. The geometry vertex values will change though, based on the size of the texture.
OK, one final bit to add for our sprite class:
- (void)render {
// 1
self.effect.texture2d0.name = self.textureInfo.name;
self.effect.texture2d0.enabled = YES;
// 2
[self.effect prepareToDraw];
// 3
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
// 4
long offset = (long)&_quad;
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)));
// 5
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
@end
Let’s go over this line by line as well. Note that to fully understand this (especially the pointer math), you need to have some basic familiarity with C. If some of this confuses you, ask in the forums and somebody can help you out.
- When you use a GLKBaseEffect to render geometry, you can specify a texture to use for the drawing. You do this by setting the texture2d0.name to the textureInfo.name, and setting texture2d0.enabled to YES. There’s also a second texture unit for more advanced effects – more details are in iOS 5 by Tutorials.
- Before you draw anything, you have to call prepareToDraw on the GLKBaseEffect. Note that you should only call this after you have finished configuring the parameters on the GLKBaseEffect the way you want.
- There are two pieces of information we want to pass to the effect/shader: the position and texture coordinate of each vertex. We use this function to enable us to pass the values through.
- Next we need to actually send each piece of data. We do that via the glVertexAttribPointer method. For example, the first function says “I want to send some position values. I’m going to send 2 floats over. After you read the first two floats, advance the size of the TexturedVertex structure to find the next two. And here’s a pointer to where you can find the first vertex.”
- Finally, we draw the geometry, specifying 4 vertices of data drawn as a triangle strip. This warrants some special discussion, continued below.
We’ve arranged the vertices in our array in a special manner so they work nicely with the triangle strip drawing method. This prevents us from having to define a second array to specify indices.
Here’s a diagram of how the triangle strip method draws our quad:
The way a triangle strip works is the following:
- The first three points define the first triangle.
- The next point defines the next triangle, along with the previous two points.
- Usually this algorithm continues on and on, but since we only have 4 vertices it actually stops here for us.
Note: Here we are passing data directly to OpenGL without using vertex buffers or vertex array objects. A while ago I thought using vertex buffers and vertex array objects would increase performance, but after some testing it seems they don’t really. After some investigation I found this great blog post by Daniel Pasco that explains the subject in more detail. Long story short: this simple way works fine.
OK! Time to try this code out. Open SGGViewController.m and make the following changes:
// Add to top of file
#import "SGGSprite.h"
// Add inside private interface
@property (strong) GLKBaseEffect * effect;
@property (strong) SGGSprite * player;
// Add in synthesize section
@synthesize player = _player;
// Add at bottom of viewDidLoad
self.effect = [[GLKBaseEffect alloc] init];
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, 480, 0, 320, -1024, 1024);
self.effect.transform.projectionMatrix = projectionMatrix;
self.player = [[SGGSprite alloc] initWithFile:@"Player.png" effect:self.effect];
// Replace glkView:drawInRect with the following
- (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.player render];
}
First we include the header file and create and synthesize a property for our player sprite.
Next we set the projection matrix on the transform, which controls how things are rendered to the screen.
There are two main types of projection matrices: perspective projections (makes things appear smaller the farther away they are) and orthographic matrices (makes things the same size regardless of how far away they are). Usually for 2D games you want an ortohographic matrix.
The good news is with GLKit you don’t really need to understand how the math works to create one. You just specify the left side of the screen, right side of the screen, bottom, and top as parameters to GLKMatrix4MakeOrtho. The last two parameters are the minimum and maximum z values. Our z values are going to default to 0 so it doesn’t really matter, but I chose a range here.
Finally we create a player sprite, passing in the effect, and call render in glkView:drawInRect. We also include two lines to set up blending properly, so that transparency in the texture shows up properly.
Note: If you want more info about how the blending modes work, check out this tutorial.
One last step. We don’t want the status bar to appear in our game, so open Supporting Files\SimpleGLKitGame-Info.plist and create a new entry for “Status bar is initially hidden”, and set it to YES.
That’s it – compile and run, and you’ll see the ninja appear on the screen!
Positioning Sprites
Great progress so far, except we don’t want our ninja to be in the bottom left of the screen – we want to put him in the center left of the screen.
One way to do this would be to update each geometryVerex in our TexturedQuad to specify different coordinates.
This would work, but there is a better way of doing things. We can keep the TexturedQuad just as it is, but use the modelViewMatrix to reposition the sprite. There are two terms you need to understand for this:
- The modelViewMatrix. Every time you render geometry with a GLKEffect, you can set the modelViewMatrix, which is a transform that is applied to the geometry before rendering it.
- A transform. A transform is a matrix that you can use to translate, scale, or rotate geometry. You don’t need to know matrix math to create a transform, since GLKit has built-in functions you can use like GLKMatrix4Translate, GLKMatrix4Rotate, and GLKMatrix4Scale.
So basically we create a transform to translate the sprite to where we want it to go, and set the modelViewMatrix property to that transform.
Let’s see how it works. Open SGGSprite.h and declare two new properties:
@property (assign) GLKVector2 position;
@property (assign) CGSize contentSize;
The first property is what we’ll use to position the sprite, and the second we’ll use to store the size of the sprite’s texture. This will be handy to write code to position the sprite based on its size, as you’ll see later.
Next, switch to SGGSprite.m and make the following changes:
// Add in @synthesize section
@synthesize position = _position;
@synthesize contentSize = _contentSize;
// Add in initWithFile, right before creating the TexturedQuad
self.contentSize = CGSizeMake(self.textureInfo.width, self.textureInfo.height);
// Add this new method right before render
- (GLKMatrix4) modelMatrix {
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0);
return modelMatrix;
}
// Add in render, right before calling prepareToDraw
self.effect.transform.modelviewMatrix = self.modelMatrix;
The important part here is the modelMatrix method. We start out with the identity matrix, which is a fancy way of saying “this matrix will not modify the geometry in any way.”
Then we call GLKMatrix4Translate to make the matrix move any geometry “self.position.x” units to the right, and “self.position.y” units up.
We set this matrix as the modelViewMatrix – and now our geometry will be translated based on what the position is set to!
Note: Setting the modelViewMatrix directly like this means you’re always positioning sprites in screen coordinates. When we start creating a node hierarchy, we’ll want to chain modelViewMatrices together so that nodes can be positioned relative to each other. However, to keep things simple we’ll start with this.
OK let’s try this out! Switch to SGGViewController.m and add this to the bottom of viewDidLoad:
self.player.position = GLKVector2Make(0, 160);
Compile and run, and now our ninja is positioned near the center:
Hm but wait – something’s off here. We meant to center the ninja along the y-axis, but he’s off a little.
The reason he’s off is that setting the position like this is setting the bottom left corner of the sprite. To center him properly, we have to shift the sprite down half the height of the sprite.
So replace the line that sets the sprite’s position with this:
self.player.position = GLKVector2Make(0, 160-(self.player.contentSize.height/2));
Compile and run, and he’ll be centered OK:
However, I personally don’t like setting sprite positions by the bottom left corner. I find it hard to think about. I’d much rather have the position of a sprite be the center of the sprite.
Luckily, this is quite easy to fix! Open SGGSprite.m and replace the modelMatrix method with the following:
- (GLKMatrix4) modelMatrix {
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0);
modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.width/2, -self.contentSize.height/2, 0);
return modelMatrix;
}
Here we just shift the geometry by half the width and height to the lower left, so that the position matches up to the center of the sprite.
Finally, replace the line that sets the position in SGGViewController.m one last time:
self.player.position = GLKVector2Make(self.player.contentSize.width/2, 160);
Compile and run, and the ninja will be in the right spot again – but you’re setting the center of the ninja now, which feels a lot better to a Cocos2D user like me ;]
Note: With some slight modifications, you can modify this code to allow you to set an anchor point on a sprite-by-sprite basis just like you can in Cocos2D. If you look at CCNode.m and search for anchorPoint, you’ll see how you can do this.
Moving Sprites
Sprites without movement are boring, so it’s time to start making sprites that can move around the screen!
In Cocos2D we have these wonderful things called actions that we can use to easily move sprites over time. We don’t have anything built-in like that with raw OpenGL/GLKit, so will have to build our own systme.
We could implement Cocos2D’s action system for our game, but again the goal of this tutorial is to keep things simple, and there’s an easier way that will suffice for this game.
We will simply give each sprite a new property called moveVelocity. It will be a vector representing the amount a sprite should move in 1 second. For example, here’s what we might set one of the target’s moveVelocity vector to:
Each frame, we’ll call an update method on our sprites so they can update their position based on what the moveVelocity vector is set to.
Let’s code this up. Open SGGSprite.h and make the following changes:
// Add new property
@property (assign) GLKVector2 moveVelocity;
// Add new method
- (void)update:(float)dt;
Then switch to SGGSprite.m and make the following changes:
// Add in @synthesize section
@synthesize moveVelocity = _moveVelocity;
// Add new method
- (void)update:(float)dt {
GLKVector2 curMove = GLKVector2MultiplyScalar(self.moveVelocity, dt);
self.position = GLKVector2Add(self.position, curMove);
}
The update method will be called with the amount of time that has passed since the last update, in seconds.
So to figure out how much we should move the sprite this frame (in points), we multiply the move velocity (points per second) by the delta time (seconds).
We then add that amount to the current position, and update our position.
Let’s give this a quick try to make sure it works. Open SGGViewController.m and make the following changes:
// Add two new properties to private @interface
@property (strong) NSMutableArray * children;
// Synthesize the properties
@synthesize children = _children;
// At end of viewDidLoad
self.children = [NSMutableArray array];
[self.children addObject:self.player];
self.player.moveVelocity = GLKVector2Make(50, 50);
// In glkView:drawInRect, replace the [self.player render] line with the following:
for (SGGSprite * sprite in self.children) {
[sprite render];
}
// Replace update method with the following
- (void)update {
for (SGGSprite * sprite in self.children) {
[sprite update:self.timeSinceLastUpdate];
}
}
Here we create an array of children (i.e. sprites) that are in our game. This makes it easy to render and update all sprites just by looping through the list of children. Of course, right now there’s just one child (the player).
We give the player an initial move velocity – 50 points up and 50 points to the right, every second.
Compile and run, and you should see your ninja slowly moving to the upper right!
Moving Targets
In this game, we don’t actually want our ninja to move, so comment out this line:
self.player.moveVelocity = GLKVector2Make(50, 50);
Instead, we want to add some moving targets into our scene for our ninja to combat. We’ll create them offscreen to the right, and move them to the left.
To do this, add this new method to SGGViewController.m, right before the update method:
// Add new method before update method
- (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);
}
Here we create a new target and add it to the list of children so it will be rendered and updated each frame.
We then figure out where to position the target when it spawns. We place it offscreen to the right, at a random spot between the bottom and top of the screen.
Then we figure out how fast it should move. We pick a random value between 480.0/4.0 (the width of the screen in 4 seconds) and 480.0/2.0 (the width of the screen in 2 seconds), going to the left.
Next make the following changes to SGGViewContorller.m:
// Add new property in private @interface
@property (assign) float timeSinceLastSpawn;
// Synthesize property
@synthesize timeSinceLastSpawn = _timeSinceLastSpawn;
// Add to beginning of update method
self.timeSinceLastSpawn += self.timeSinceLastUpdate;
if (self.timeSinceLastSpawn > 1.0) {
self.timeSinceLastSpawn = 0;
[self addTarget];
}
Here we just add some code to call addTarget every second.
Compile and run, and now you have some targets running across the screen!
Where To Go From Here?
Here is an example project with all of the code from the tutorial so far.
Congratulations, you have made the start of a game using the lowest-level APIs on iOS – OpenGL ES 2.0 and GLKit! You have drawn sprites to the screen, positioned them where you like, and even made them move.
You’re ready for the next tutorial in the series, where you’ll let the ninja shoot stars, kill monsters, and win the game.
In the meantime, if you have any questions or comments, please join the forum discussion below!
This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more