OpenGL ES 19 - So You Wanna Be an iPhone Games Programmer?


OpenGL ES 19 - So You Wanna Be an iPhone Games Programmer?



Disclaimer: This post and the whole blog is just a copy of iPhone-OpenGL-ES-tutorial-Series  (seems to have )written by Simon Maurice. This blog does NOT have a written permission or any kind of permission from the original author (Simon Maurice). Please use it at your own risk. If you have any complaints regarding any content of this blog, please let me know I will remove it right away. Just hoping that it would be helpful for people who are trying to learn OpenGL. Happy coding!



Copyright 2009 Simon Maurice. All Rights Reserved.
The code provided in these pages are for educational purposes only and may not be used for commercial purposes without Simon Maurice’s expressed permission in writing. Information contained within this site cannot be duplicated in any form without Simon Maurice’s expressed permission in writing; this includes, but is not limited to, publishing in printed format, reproduced on web pages, or other forms of electronic distribution.

Source codes can be found here

Original tutorial starts from here onwards..

Sorry for the corny title but back in 1990/91, Amiga Format magazine had a series where Dave Jones, shared the code of how he wrote the first level of the game Menace. Whilst I really didn’t learn much about the coding side of the articles, I did learn a lot when it came down to how he thought as a programmer and what decisions he made when it came to actually implementing a game.

This tutorial sub-series is going to be a serious nod to Dave Jones and to that great magazine for what they did back in the days. I’m going to show you how to code a “Menace-like” game for the iPhone. Hence the title of this post, I’m pretty sure Amiga format used to splash “So you wanna be a games programmer” on their covers for one of their series with a pro game developer (they had several from memory). Like I said, this is a nod to them.

From my last post, I said that I would do a A-to-Z of programming a 2D shoot ‘em up. Well, that’s exactly what this set of tutorials is going to be about. At the end of it, we should have a working game which can be submitted to the App Store for release. Yes, that even means I’ll show you how to create levels, game graphics, and deal with iPhone issues such as what to do when the phone rings.

5_627_f42a25b206b91ed.jpg


Since this is going to be a “proper” project series, there really is going to be a few things you’re going to need. They are:


  
     1.An iPhone or iPod touch: Since most of the control will be via the 
accelerometer, you can’t avoid having a device. I won’t do “click on screen” controls.
 Well, maybe but I’m not promising anything.
  
     2.A developer certificate from Apple: Yes, you’ve got to spend the US$99 to get 
the finished code onto a device for testing. This goes back to the first requirement.
  
     3.Needless to say, you’ll also need a Mac with Apple’s Xcode tools with iPhone 
SDK 3.0 installed.
  
     4.Apple’s X11 installed: we’ll be using the GIMP for some graphics work so 
you’ll need X11. Sorry, I don’t do Photoshop. So yes, you’ll need the GIMP, Blender, 
and a couple of other pieces of free software. Nothing that will cost you money to 
get.
  
     5.Music: this is an unknown at the moment. I think all you’ll need is GarageBand.
 To be honest, I haven’t worked this out yet. Maybe I’ll use a MOD player library as
 a nod to the Amiga.

You won’t need everything at first. You’ll only need one and two once we get to the accelerometer support. I know I’ve avoided that before because I know not all of you have a device to test on. To do this properly, you’re going to need one. Just go spend the money when the time comes.

I’ll give you some warning one or two tutorials in advance on those cost-item requirements.
5_627_775ac26a855607b.jpg
BTW: Don’t ask me how to get the code onto a jailbroken iPhone. I don’t go there. Just pay the $99 and code things properly. It’s part of being a professional (even if you are working out of your bedroom).


This code will be released under the GNU GPL v3. You know that means

5_627_368d600aa80ef2d.png

if you release something based on this code you need to release the source code as well. If you don’t want to, then just contact me and we can probably make a deal but I don’t think it should come to that. I really just think it’s best if you release the source code as per the GNU GPL. I used to think that I had to keep my source code secret. Back in the days, I used to release Amiga demos as binary only but these days I think different. There really is very little in a game engine that another professional coder can’t figure out by looking at your game so I don’t think there’s much point of treating most code as a national secret.

I know I said I’d wait until I got my own game done but, in reality, I’ve not got the time at the moment to do both a tutorial series and work on my own things. I do have a life. So I think for now, I’m going to do this as an open project and write it all in public so I can kill two birds with one stone. Either that or I won’t bother as I am writing a desktop Mac app at the moment which for me is a bit more interesting because it’s not a graphics project. It’s actually something for my main hobby: beer.

But that’s another story.

The Basic Game
The game that’s going to be created is a side scrolling shoot em up. Like I said, it’s going to based on the Amiga game Menace as a nod to Dave Jones’ Amiga format tutorial series which is the first that I can remember seeing and taking it seriously. I haven’t got all the details worked out yet but I’ve pilfered enough code from the other 2D game engine I’m writing to get started and we’ll be able to finish this in a couple of weeks.

For those of you who don’t know the game, there’s a youtube video showing someone play the entire game through (AmigaLongPlay.com or something like that). If you search for “Menace Amiga Longplay” you should get it. That will show you the kind of game that we’re going to develop.

Essentially, we’re going to use OpenGL ES 1.1 in an orthographic projection. All this means is that depth is ignored so we’ll just render everything using the painter’s algorithm where objects are drawn from the rear to the front. We’ll use parallax scrolling to provide the illusion of depth.

Of course, there will be sprites, lasers, explosions, collision detection, point scoring, baddies; all the stuff you’d expect in a shoot em up game. Now how can I forget to mention end of level bosses as well!!

The Tutorial Series
I knew at the outset when I came up with the idea of doing a game and releasing the source code, I didn’t really want to just chuck the project file out there. I’d rather go through it and discuss the code. This means more work for me but I think it will be a better learning experience for those who are interested. Also, it means that if I don’t get around to releasing the game, the code is not lost (not like the other projects I’ve done over the years).

Essentially, there will be weekly posts. I’m going to post a new tutorial every Monday night, Sydney time so I’m keeping everything regular. That way I can work on this over the weekend, getting the posts ready for Monday night. I’m not sure how many tutorials will make this series up but I think it would be at least a dozen or so. Probably more.

I will do some screen casts. For the graphics development only but I’m not sure how I’m going to make that happen at the moment. I don’t think discussing code works in a screen cast but showing how to make graphics in the GIMP does translate well.

Just on the graphics side, I’m not really that crash hot on graphics. However, I do know how to do it after looking over the shoulders of real graphics artists over the years. I just do the same things as they do, just not as well so I think the techniques are correct, but let down by my lack of artistic talent. Normally I do enough just for quick and dirty prototyping, or for non-critical elements. Whilst my creations may not turn out that good, with persistence or some artistic talent on your part, you should be able to get something quite a bit better than what I can create.

After some thought, I did decide to implement it in Objective-C, even though I do prefer to code in plain old C. As much as I prefer not to use OOP, this is an iPhone series and you need to know Objective-C at some level to work on the iPhone and, besides that, many people like to use OOP by choice. I’d rather present something in Objective-C rather than the evil that is C++ which is a language I straight out refuse to use.

So, for this series, Objective-C it is. For the next series, I think I might just do it mostly in C.

Having said that, I’m not going to make everything into a class. You do see that all the time so there’s going to be no creating of a tile class and then sub-classing half the planet etc. I’ll have to use some of course but I’m not going to go crazy about it. I don’t really care what anyone says; Objective-C class creation and messaging is more expensive than a standard C function call so I want to keep all that to a minimum. We’re shooting for 60fps here after all.

The Very Short Brief for the Game
I’m not going to write a design document. This will probably evolve over time but let’s set the ground rules for what is absolutely promised to be delivered for this title:


  
     1.A full game, with graphics and audio, all developed here in public. Code 
released under the GNU GPL.
  
     2.2D side scrolling game with parallax scrolling. Running at 60 frames per 
second on the iPhone 3G. The iPod Touch and iPhone 3GS should be fine if the iPhone 
3G can hit 60fps.
  
     3.Fully handle the possibilities on the iPhone which means phone calls and other
 events which may interrupt game play like battery warnings.
  
     4.Correctness. We’ll write proper code which cleans up after itself etc. It must
 be suitable for publishing in the App Store so that means only official API code 
only. 
 

     5.We’ll produce a fun game. I hope.
  
     6.Multiple levels loaded on the fly. Right now, I’m shooting for around 5 or 6 
but we can do one fully then only the differences for the other levels I guess. 
We can work it out later. At least be able to handle multiple levels.
  
     7.Saving games, whilst not normal for arcade games, this is recognition of the 
iPhone user’s needs. Also pausing and other basic game functions.
  
     8.A high score table. Gotta have that right?
  
     9.No off the shelf game engines. I’ll tell you why below.

So that’s the brief as it stands right now. The order in which things are implemented will be exposed over time.

Why do I say no game engines? Look there’s multiple reasons why not to and of course “why to” reasons as well. Personally, I don’t believe in them. I’m not going to bag them out here or their authors. In fact, I praise the work of the guys doing open source engines for their generosity towards the programming community and giving of their time to do so.

I’m going this route because it’s what I want to do. I don’t just want to bolt graphics onto someone else’s code and not truly understand what’s happening in the background. I don’t just want to make a clone of a clone of a clone (although you’re of course not just limited to that with an engine).

I want to program and design. That’s what I want to do.

Like I’ve said before, this is only one man’s opinion. You’re free to make up your own mind. I’ve chosen this route because it’s what I want to do. If you chose the path I’m presenting here, then you’re right. If you’d rather go the engine route, then you’re also right.

The only wrong route is to start one path and then chop and change every time you strike a problem and think the other route might actually be easier.

Suck it up big boy. Make a decision and stick to it.

Killing is My Business...
...and business is good. Yeah! The old Megadeth song!

I’ve gotta give the project a name and, without thinking too hard about the title, I’m just going to call the project Draconia. Anyone who played Menace will know why.

So, now, let us begin...
In the previous post, I discussed what we’re doing here in this series. If you haven’t read it, it’s basic background material for this little series on making a side scrolling 2D shoot ‘em up in the style of Menace back on the Amiga.

As I recounted a couple of posts ago, I went straight from doing a 3D game into a 2D shoot ‘em up when I realised my 3D concept wasn’t going to translate into the iPhone’s world and that I went from zero to a working scrolling 2D engine in a couple of hours. Those couple of hours were just as frustrating as they were exciting because it was fun doing things in the old ways. Although the lack of planar graphics with hardware scrolling meant that I had to try and re-learn a few things that I hadn’t used in years.

So, today I’m covering the base project, getting the background drawn, and then scrolling the background.

Creating the Base Project
All I did was to use Apple’s default OpenGL ES template and delete the code for the spinning cube thing. Then I set up the project to run in landscape mode with viewport the size of the iPhone’s display because I thought that might be easier. Right now, having gone further, I may change that to get some smoother effects at a later date. For now, it just means that if we put a 16x16 unit quad on the display, it will be 16x16 pixels in size when displayed on the iPhone’s screen.

I’ve covered that before I think so I’m just putting the starter project here for your downloading pleasure. I think it’s all fairly self explanatory.

The project download: Draconia Start.zip

I do want to point out a couple of things.

In the project build configuration, I’ve put in a GCC definition for DEBUG whilst building a Debug configuration. This just allows me to do things like:


  1. ifdef DEBUG
   double startTime = CFAbsoluteTimeGetCurrent();
  1. endif

////// Code goes here
   frameCounter++;
  1. ifdef DEBUG
   double endTime = CFAbsoluteTimeGetCurrent();
   totalRenderingTime += (endTime - startTime);
  1. endif

So on a release build, anything that I don’t want in a finished product is not included.

The above code is another thing. It’s from the drawView method. I’ve just popped that in there so I can keep a track of frame rates. Like I said in the last post, I’m shooting for 60fps so I’d like to know where I’m at each build I do for device testing; just need to know how much headroom I have for “special effects” or how far behind I am.

It the average frame rate gets logged to the console on exit. I did some testing some time ago and found that using CFAbsoluteTimeGetCurrent() was faster than using the Unix time calls so I’m assuming it’s getting routed through there somehow. I didn’t look into it as it’s no major drama.

I did decide to make the frame counter a global variable because since I can keep a fixed frame rate, I can use that for animation times and other things. Just like we did in the good old days in the vertical blanking interrupt!

The other things I did was put the device into the landscape mode with the home button to the right and hide the status bar just like I did before.

I’ve made an executive decision not to handle rotation. I’ve found most iPhone games don’t support it anyway and it will just keep our code cleaner.

Just to make things really clear, this is the main part of the code which organises the OpenGL ES view to be in landscape mode:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glRotatef(-90.0, 0.0, 0.0, 1.0);
glOrthof(0.0, 480.0, 0.0, 320.0, -1.0, 1.0);

Basically, what the above does is just rotate the current projection around the X axis by 90ยบ. Remember, OpenGL talks in degrees whereas the C maths functions use radians.

The call to glOrthof() is where we set up the projection matrix. I think last time I used different values for that but this time, we get the range of visible X co-ordinate values as between 0 and 480, and the Y co-ordinate values as between 0 and 320.

Therefore, the co-ordinates (0, 0) are the bottom left of the iPhone’s display and (480, 320) are the top right. Again, I’ve covered that before.

Drawing and Scrolling a Background
As I’ve mentioned, we’re using parallax scrolling in this game. Parallax scrolling is where you draw multiple layers of “scenery” and scroll them at different speeds to provide an illusion of depth. If you don’t know what I mean by parallax scrolling, then head off to Wikipedia and read this: http://en.wikipedia.org/wiki/Parallax_scrolling

Sorry to my international readers, that’s the English page. There’s a nice animation there which saves me creating one.

Now, we’re going to have ultimately three layers in the game: a background, the layer with all the sprites (ie player, enemies, laser shots etc), and a foreground layer. They will be drawn in that order just like the painter’s algorithm.

The background in this first level we’re going to create is going to be just a single texture repeated over and over again rendered using a tile map. Whilst that may sound uninteresting, if you think back to classic arcade games, many used this technique. It’s because the player’s eyes are typically focused on himself, the enemies on screen, and the foreground detail.

The background is important from the position that it has to be there. Like mood music in a restaurant: it’s not important what’s playing, it just needs to be at a soft volume, in the background. It just needs to be there.

Creating a Background Texture
The first thing that we’ll do is to create the texture for the background tile. Here’s the chance for me to introduce my screen casting skills (or lack thereof) because I’m creating it in the GIMP. I think doing things in the GIMP or Blender really lends itself to using the screen casting approach. Code, on the other hand, I think works better than text.

This is my first shot so it’s probably not very good (I haven’t recorded at the time of typing) but it will show you the techniques that I would use to create this.

Find that video here:
At this point the video is not up yet. It’s crappier than what I thought my my worst attempt would be!

I will post it, if not before the next tutorial, at the same time. So that’s Monday 17th August at the latest.

In the meantime, use the texture in the starter project.

If you haven’t got the GIMP or don’t feel confident yet in using it, then the texture is in the starter project above or you can substitute your own (because mine is pretty average).

First Class to Create: The Level Class
Like I said in the last post, I’m not going to create classes for everything. I did decide to create a Level class which basically equates to a game level.It will be this class which does most of the rendering. Initially, I was just calling C functions so I didn’t create a class for this. In this case, I think a class will work quite well, especially as we can do some custom things for different levels.

First of all, right click on the Classes group and create a new sub-class of NSObject and call the class “Level” (sorry, couldn’t think of a more imaginative name).

So now, the background is just going to be an array or grid of tiles which will be scrolled across the rear of the screen at a fixed rate. In order to help you visualise what I did, have a look at this image.



This is a pretend view of the iPhone’s screen. The green gridlines represent the tiles which will contain the texture we created earlier.

The first thing I want to point out right now is that I decided to treat these as individual quads, just like I would have before. It’s quite possible to draw these as triangle strips but under a tile based engine, that’s not really going to cut it if I wanted more than a single texture for the background. In fact, I did when I was first coding this. It’s definitely required for the foreground.

Whilst drawing each as individual quads is definitely not the fastest way, I’ll worry about optimising that if I need to. Remember, things only have to be fast enough, not perfectly optimised. I have a rant about optimisations below :)

Secondly, I decided to make the grid sized 64x64 pixels. That will be 64x64 units when we’re sending vertex arrays to the GPU.

Thirdly, note that the grid is 9 tiles wide meaning there are 2.5 grids off the visible area to the right of the screen. It could only be 8 tiles wide but it’s 9 because I wanted the game engine to be a couple of tiles ahead when scrolling as we will be using that far right tile as a trigger for some events. All will be made clearer when it gets used; it’s just planning ahead.

Finally, you can see the grid is only 4 tiles high leaving space at the bottom. This is where we will put our console containing power levels, score and the fire button.

The first thing we need to do is to create the vertex array for the tile grid. Remember that we’re only doing the background at the moment. In the Level.h file you just created, the first thing you need to add in are a couple of OpenGL header files so you have access to the data types and function prototypes. Add these lines:


  1. import <OpenGLES/EAGL.h>
  1. import <OpenGLES/ES1/gl.h>
  1. import <OpenGLES/ES1/glext.h>

Yes, I know I don’t need all 3, but if you just add in all 3, you’re making life easier on yourself if you extend this code.

Next, since I was experimenting at the time, I did create some defines to allow some flexibility and store some constants. Here are the ones for the background:

// First some defines on the background
  1. define BACKGROUND_TILE_SIZE 64
  1. define BACKGROUND_COLUMNS 9
  1. define BACKGROUND_ROWS 4
  1. define BACKGROUND_SCROLL_SPEED 0.3
  1. define BACKGROUND_WRAP_X BACKGROUND_TILE_SIZE*(BACKGROUND_COLUMNS-1)-BACKGROUND_SCROLL_SPEED
  1. define BACKGROUND_TILE_COUNT BACKGROUND_ROWS*BACKGROUND_COLUMNS

BACKGROUND_TILE_SIZE is the pixel size of each background tile. They are square so they are 64x64.
BACKGROUND_COLUMNS and BACKGROUND_ROWS are exactly that. The count of the number of rows and columns for the background.
BACKGROUND_SCROLL_SPEED is a constant which controls how many pixels per frame the background scrolls. In other words 0.3 pixels per 1/60 of a second. This is where some jitter is caused by rounding errors but I’d rather fix that later than tie myself into whole numbers now. Yes, I already have a solution.
BACKGROUND_WRAP_X is the X co-ordinate in world space where we wrap the tiles to. I’ll cover this one in detail later.
BACKGROUND_TILE_COUNT is exactly that, how many quads there are in the background.

I went to all the trouble with those because I didn’t know how the tile sizes etc would play out so that way I was able to keep things fairly generic later on.

Now, in the class interface, add some instance variables like so:


   GLfloat backVerts[BACKGROUND_TILE_COUNT*8];   // Vertex array for all the 
                                                     //background tiles

   GLuint backgroundTexture;           // OpenGL holder for the texture ID
   GLbyte backSTs[8];   // Only need one quad as they are all the same.

In the interests of late night laziness, I didn’t try and eradicate duplicated vertices. I’m not that interested in trying to optimise the engine at the moment so I just created an array to hold 4 vertices for all the tiles (2 values per co-ordinate remember. Also, we don’t need to worry about the texture co-ordinates for each quad, one set of texture co-ordinates will be fine will be fine.

Finally, some methods:

- (id)initWithFile:(NSString *)filename;
- (void)scroll;
  1.
     -(void)drawBackground;
- (void)loadTexture:(NSString *)fileName intoLocation:(GLuint)location;

initWithFile receives a filename which will eventually hold the level information such as where the enemies are, how many there are, the foreground tile map etc. scroll is just called to scroll the entire level map (only background at the moment). drawBackground should not require any explanation.

At some point through this series, I’ll move the texture management into a centralised location to avoid 20 different loadTexture methods spread across a bunch of classes. Centralised texture management is a Good Thing(tm).

Creating the Background Vertex Array
I’ve just looked at my code again. Pointer arithmetic!! Someone’s going to send me hate mail if I presented this. Truth is, I like pointer arithmetic. It makes life so much easier and it’s only when you don’t understand what you’re doing do you introduce bugs. Mind you, I’m probably only good at it because I spent so many hours (years) writing assembly language code (remember HiSoft Devpac Assembler? Go you good thing!).

So, I’ll be presenting the non-pointer-arithmetic version here. It’s a bit late and I should have written this code better but it works.

Into Level.m and go to initWithFile and create the method there just like this:

- (id)initWithFile:(NSString *)filename {
   if (self = [super init]) {


       // Code for initialising the background
       GLfloat xpos = 0.0;
       GLfloat ypos = BACKGROUND_TILE_SIZE;  // Offset the base to allow for the 
                                             //console


       for (int i = 0; i < BACKGROUND_COLUMNS; i++) {
           for (int j = 0; j < BACKGROUND_ROWS; j++) {
               int idx = i*BACKGROUND_ROWS*8 + (j*8);          // Just to make the 
                                                               //code neater


               backVerts[idx] = xpos;                          // Top left
               backVerts[idx+1] = ypos + BACKGROUND_TILE_SIZE;
               backVerts[idx+2] = xpos;                        // Bottom left
               backVerts[idx+3] = ypos;
               backVerts[idx+4] = xpos + BACKGROUND_TILE_SIZE; // Bottom right
               backVerts[idx+5] = ypos;
               backVerts[idx+6] = xpos + BACKGROUND_TILE_SIZE; // Top right
               backVerts[idx+7] = ypos + BACKGROUND_TILE_SIZE;


               ypos += BACK_GRID_SIZE; // Increase the y value so we create a tile 
                                       //above
           }
           ypos = BACKGROUND_TILE_SIZE;   // Reset the Y value to the bottom 
                                           //(above the console)
           xpos += BACKGROUND_TILE_SIZE;  // Move to the next column across
       }

Basically what’s happening is that I’ve used two for loops. For each column, I build one square for each row. In other words, I’m just building the tile array starting with the tile that is at the bottom left of the screen, then moving to the one above it etc. Then I do the next column.

In other words (ie a picture), the vertices for the background tiles are calculated in this order:



Hope that makes sense!

Like I said, I’ve not bothered to worry about duplicate vertices etc at the moment. It’s not critical right now.

Then all we do is to load the texture and setup the texture co-ordinate array like so, and then complete the initialiser:


       glGenTextures(1, &backgroundTexture);
       [self loadTexture:@"bluebackground.png" intoLocation:backgroundTexture];


       // Setup the texture co-ordinates (STs)
       backSTs[0] = 0;
       backSTs[1] = 1;
       backSTs[2] = 0;
       backSTs[3] = 0;
       backSTs[4] = 1;
       backSTs[5] = 0;
       backSTs[6] = 1;
       backSTs[7] = 1;
   }
   return self;
}


Drawing the Background
This can be done without too much thought. Just a loop through each tile and drawing each tile individually.

- (void)drawBackground {


   // Draw that bad boy
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   glEnable(GL_TEXTURE_2D);


   glTexCoordPointer(2, GL_BYTE, 0, backSTs);
   GLfloat *v = backVerts;
   glBindTexture(GL_TEXTURE_2D, backgroundTexture);
   for (int i = 0; i <= BACKGROUND_TILE_COUNT; i++) {
       glVertexPointer(2, GL_FLOAT, 0, v);
       glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       v = &backVerts[i*8];
   }


   glDisable(GL_TEXTURE_2D);
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

There really is nothing new in there. You should be up to date with these calls by now and, if not, go back over my original tutorials.

Before we leave here right now, we just need to copy and paste in the loadTexture method. Here it is to save you guys looking it up:

- (void)loadTexture:(NSString *)fileName intoLocation:(GLuint)location {
   CGImageRef textureImage = [UIImage imageNamed:fileName].CGImage;


   if (textureImage == nil) {
       NSLog(@"Could not open image: %@", fileName);
       return;
   }


   NSInteger texWidth = CGImageGetWidth(textureImage);
   NSInteger texHeight = CGImageGetHeight(textureImage);


   GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4);


   CGContextRef textureContext = CGBitmapContextCreate(textureData,
                                                       texWidth, texHeight,
                                                       8, texWidth * 4,
                                                   CGImageGetColorSpace(textureImage),
                                                   kCGImageAlphaPremultipliedLast);


   CGContextTranslateCTM(textureContext, 0, texHeight);
   CGContextScaleCTM(textureContext, 1.0, -1.0);


   CGContextDrawImage(textureContext, CGRectMake(0.0, 0.0, (float)texWidth, 
(float)texHeight), textureImage);

   CGContextRelease(textureContext);


   glBindTexture(GL_TEXTURE_2D, location);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, 
GL_RGBA, GL_UNSIGNED_BYTE, textureData);


   free(textureData);


   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

I’ll do something cooler with texture management later on.

Our First Look at the Background
Righto, now that we’ve done all that, let’s at least draw the background to make sure that everything is fine at the moment. It’s going to be easier to debug this without the scrolling code added in.

Switch over to EAGLView.h and add an import for Level.h at the top like so:


  1. import "Level.h"

Then add an instance variable for the level in the class’ interface:

Level *currentLevel;

Now into EAGLView.m.
In the initWithCoder method, add this at the end somewhere:


currentLevel = [[Level alloc] initWithFile:@"no file"];

That just creates the level. The filename is not important because we haven’t created it yet. That will come later (I’ve done something already for what I was developing and it’s actually been kinda fun to work out a file format for a level).

Finally, in the drawView method, we can add the call to draw the background. So drawView will look like this:

- (void)drawView {


  1. ifdef DEBUG
   double startTime = CFAbsoluteTimeGetCurrent();
  1. endif


   [EAGLContext setCurrentContext:context];
   glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
   glViewport(0, 0, backingWidth, backingHeight);


   [currentLevel drawBackground];            // Add this line here


   glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
   [context presentRenderbuffer:GL_RENDERBUFFER_OES];


   frameCounter++;
  1. ifdef DEBUG
   double endTime = CFAbsoluteTimeGetCurrent();
   totalRenderingTime += (endTime - startTime);
  1. endif


}

Two more final things to do: add the texture to the project. Add the CoreGraphics.framework to the project for loading the texture. See tutorial #5 for both of these if you haven’t already.

Hit Build & Go and (after ignoring the warning about the missing scroll method in Level.m), if I’ve done my job, you’ll get this:



It may not look exactly like that depending on the background texture I include in the project. I’m already not liking this pattern and I think it’s a little bright so I’m pretty sure that I’m going to change it.

Note the black space at the bottom. That’s where our console will go.

Scroll that Bad Boy
Now to implement the scroll functionality. This is where those tiles over the right hand side of the display come into play. To implement scrolling, I decided the quickest and easiest way to do so would be to go through my vertex array for my background tiles and just change the X co-ordinate for each tile, decreasing the X co-ordinate by the BACKGROUND_SCROLL_SPEED value. Remember, the X co-ordinate 0 is at the left hand side of the display.

After a few frames of scrolling, we would end up with this:



Notice that I’ve changed the colour of the first column of tiles. You need to keep an eye on these ones.

Now, when the X value of the leading vertices of those red tiles (ie the left most vertices, they’re the leading edge) reach a value less than -BACKGROUND_TILE_SIZE (negative value, that is -64 in this case), we now know the tile is no longer visible. The grid will look like this:


So now what? Here’s what I decided to do. At this point in the scrolling code when we detect that the leading edge of that tile is less than the negative value of the tile size, we literally change the X value of that tile to be at the rear of the grid.

So our code will do this to the red tiles:



That’s what’s happening when I refer to wrapping.

So, you can see I didn’t need 9 columns for the background but, like I said, I am planning to use the current column as a trigger in some cases and this allows me to have different triggers for the foreground to the background if I want. The amount of memory and time to deal with those extra two columns isn’t worth losing sleep over.

That’s the basic scroll loop, so let’s have a look at the code:

- (void)scroll {
   // Now Scroll the background. We only scroll the background if we have needed to 
//scroll the
   //    foreground.
   for (int i = 0; i < BACKGROUND_TILE_COUNT; i++) {
       if (backVerts[i*8] < -BACK_GRID_SIZE) {
           backVerts[i*8] = BACKGROUND_WRAP_X;
           backVerts[i*8+2] = BACKGROUND_WRAP_X;
           backVerts[i*8+4] = BACKGROUND_WRAP_X + BACKGROUND_TILE_SIZE;
           backVerts[i*8+6] = BACKGROUND_WRAP_X + BACKGROUND_TILE_SIZE;
       } else {
           backVerts[i*8] -= BACKGROUND_SCROLL_SPEED;
           backVerts[i*8+2] -= BACKGROUND_SCROLL_SPEED;
           backVerts[i*8+4] -= BACKGROUND_SCROLL_SPEED;
           backVerts[i*8+6] -= BACKGROUND_SCROLL_SPEED;
       }
   }
}

For the sake of simplicity, I just went through every tile. You can optimise this if you like but we’re likely to later on. That’s the first thing, it’s just a for loop. For each tile, we test to see if the leading edge is far enough off the left hand side of the display. If it is, we change the X co-ordinates only to the wrap co-ordinate, specified by BACKGROUND_WRAP_X for the leading edge, and BACKGROUND_WRAP_X + BACKGROUND_TILE_SIZE for the trailing edge of the tile (obviously, they cannot be the same co-ordinate.

If you remember, we defined BACKGROUND_WRAP_X as the following:


  1. define BACKGROUND_WRAP_X BACKGROUND_TILE_SIZE*(BACKGROUND_COLUMNS-1)-BACKGROUND_SCROLL_SPEED

Our tile size is 64 and we have 9 columns. So to get the new leading edge X co-ordinate, we just multiply the tile size by the column count less one. ie 64 x (9-1) = 512. Now, since all the other tiles are scrolling anyway, we still need to scroll the “wrapped” tile otherwise we would get a gap between the tile that just wrapped and the tile immediately before it.

The other half of the loop is executed when the leading edge isn’t detected as being far enough off screen. The tile’s X co-ordinates are just decrements by the scroll speed value.

Add that to the Level.m file and then switch back to EAGLView.m where you can add the following to the drawView method:


   [currentLevel scroll];
   [currentLevel drawBackground];

Do you scroll first, then draw second or draw first then scroll for the next frame? It doesn’t matter. I’ve decided to scroll first, then draw. Like I mentioned, new tiles create new triggers so I get the scroll out of the way first.

Hit Build & Go and you should get the scrolling background:



Now of course this looks identical in static pictures!!

That is scrolling okay and looking smooth but if you put a texture up there with say a border, you’d see some of the jitters I was talking about. That’s all fixable but we’ll do it later after we’ve decided if we need to optimise this code and how we can do it.

Some Cleanup Code. Yes, Really!
You guys who have followed these tutorials know that I haven’t really written cleanup code. Well, since we are making a real game in this, I can’t get away with that here. So we need to add a dealloc method to the Level class to clean up the texture.

- (void)dealloc {
   glDeleteTextures(1, &backgroundTexture);
   [super dealloc];
}

I don’t think I’ve ever included a glDeleteTextures before!! :) Remember you call glGenTextures() to create a texture “handle” within OpenGL. Then, when we load the texture, OpenGL takes control of it so to speak, so, when we’re done with that texture, we need to tell OpenGL to release that memory that it’s controlling. We don’t have access to the GPU’s memory so we can’t release it ourselves. That’s where glDeleteTextures() tells OpenGL to free that memory up.

You don’t need to check that the texture is actually allocated. Anything with a value of zero is ignored as the texture ID zero is never allocated for us, they all start at 1 (depending on implementation of course). Objective-C initialises all instance variables to nil or zero (hopefully) so we don’t need to check that something is allocated there. Just call it.

For now, we’ll clean up the level in the EAGLView dealloc method with a call like so:

[currentLevel release];

In reality, we’ll move that elsewhere later on when we add code for multiple levels.

Some Parting Comments Before Concluding
Look, I know I’ve said several times not to worry about optimising the code just yet. The reason is because this tutorial is aimed at those people who have emailed me and asked “how to make a tile engine”. So this has so far been more of a concept than what it may turn out to be. For these people, they need to get the concepts first, the speed comes later.

If you already knew this stuff, then great. At some point I’m sure something will come out of this that you didn’t know. If you’re in my target audience, then chances are if I wrote 100% optimised code you may not have learnt a thing except how to cut and paste.

For everyone though, I want to hit that speed barrier. I want it to fall below 60fps which is what I’ve promised to deliver. Then I get to talk about how I go through and optimise code. What steps do I take. What my thought processes are. How it gets done. How to measure the results.

When you learn to optimise yourself by learning from someone else, you can then take that knowledge and apply it elsewhere. If you get served up 100% pre-optimised code but don’t really know how to optimise, when are you going to learn? How will you learn?

We want to hit that barrier at some point. That’s how we learn.

Like I’ve said before, I present this information in a way that I think I would like to learn and, in many ways, is how I learned.

Conclusion for this Tutorial
It’s actually at this point I’m going to switch from what I did in that night a couple of weeks ago. Originally, I got parallax scrolling working, then went in the hunt for some software for making a tile map. I actually created the tiles and then used a hand coded array which I’m not going to make you guys do.

In the next instalment, I’ll introduce some software I’ve found (free software of course!) and go through creating a tile map for us to render the foreground.

In the interim, here’s the completed project for now. This is where we’ll start from next time.

File download: Draconia01.zip

Until then, take care.
Hooroo!
Simon Maurice

Copyright 2009 Simon Maurice. All Rights Reserved.
The code provided in these pages is free software under the terms of the GNU General Public Licence version 3. Please refer to this link for the full licence: http://www.gnu.org/licenses/gpl-3.0.txt

No comments:

Post a Comment