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.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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.

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:

Ninja sprite positioned incorrectly

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:

Ninja sprite positioned correctly

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.

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:

The move vector for a sprite

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!

Our ninja sprite moving across the screen

Contributors

Over 300 content creators. Join our team.