This is part four of a series on making iOS games.
A little while ago, I created a fairly generic OpenGL ES 2.0 game template (Xcode project) for a 2D game. It used standard vertex and fragment shaders. For this project I wanted to use GLKit effects, a GLKViewController (and it’s view) and storyboards.
So I used Ray Wenderlich’s excellent digital book “iOS 5 by Tutorials” to come up to speed. http://www.raywenderlich.com/store/ios-5-by-tutorials. Chapter 8&9 are the ones.
Ray’s tutorials will give you a good grounding in OpenGL for iOS 5, if you need that grounding. A bit OpenGL background might make this blog piece more interesting, because I won’t be explaining a lot of basic OpenGL things much (eg things like quads, shaders, vertexes, vertex structs, buffers etc).
In addition, Ray just recently came out with another tutorial on his excellent site which expands some on his previous OpenGL tutorials: http://www.raywenderlich.com/9743/how-to-create-a-simple-2d-iphone-game-with-opengl-es-2-0-and-glkit-part-1 and http://www.raywenderlich.com/9776/how;g-to-create-a-simple-2d-iphone-game-with-opengl-es-2-0-and-glkit-part-2.
Worth reading before reading the rest of this post
More challenging stuff
After setting up the initial story board, GLKViewController and GLKView, the next step for me was to add VAOs and VBOs (both of which Ray avoids in his most recent post).
Its fairly easy.
Here is my main VBO/VAO setup:
glGenVertexArraysOES(1, &vertexArray); glBindVertexArrayOES(vertexArray); glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER,spriteCount * 4 * sizeof(SmallVertex), vertices,GL_DYNAMIC_DRAW); glGenBuffers(1, &indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER,spriteCount * 6 * sizeof(GLushort), indices,GL_STATIC_DRAW); //vertex glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_SHORT, GL_FALSE,sizeof(SmallVertex), (const GLvoid *) offsetof(SmallVertex, Position)); //colour glEnableVertexAttribArray(GLKVertexAttribColor); glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_FALSE,sizeof(SmallVertex), (const GLvoid *) offsetof(SmallVertex, Color)); //texture glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT,GL_FALSE, sizeof(SmallVertex),(const GLvoid *) offsetof(SmallVertex, TexCoord));
One can scale the base case and just do hundreds of draw calls (each draw call with its own setup) – one for each sprite/image, but that might get slow, even if you packaged up into different VBOs/VAOs. If you had really complex shapes (3D models), you might have them in different draw calls because the shaders might make your scaling and transformations easier. The number of separate moving objects might be relatively low in a scene.
I just went straight to batching.
Batching is nicely described in Philip Rideout’s book, listed in the resources below. It’s basically taking all your images (which are each 2 triangles forming a quad) and copying the location of the sprite (the four corners), the colour, the locations of the square piece of real estate you are grabbing from the texture atlas (see a future blog), into one big array. Also, copying the indexes into that array that describe the triangles, into another big array. So 6 indexes for every 4 vertices, since you need to map out 2 triangles.
With a VBO this means copying the data a first time, and then later copying any changed data (which often will only be a small portion of the whole buffer) with this call: glBufferSubData.
Here is a napkin on my batching.
Source: Me with Napkin
Each update loop there is a simple process, as shown in the napkin above, of updating attributes, be they position, colour or texture and then sub buffer copying. The index buffer does not need to be updated because regardless of which sprite is being drawn, it always has the same indices(indexing into the vertex data), since its always the same shape – a quad.
Here is the loading of the VBO/VAO and the drawing for one sprite layer.
glBindVertexArrayOES(vertexArray); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferSubData(GL_ARRAY_BUFFER, 0,spriteCount * 4 * sizeof(SmallVertex), vertices); glDrawElements(GL_TRIANGLES, spriteCount * 6, GL_UNSIGNED_SHORT, 0);
The base effect setup and other transformations are called earlier.
glBufferData first copy
With the first copy (glBufferData – as opposed to later copies with glBufferSubData) I found that its important to have set the vertices to have some value even if that value is zero. I couldn’t just malloc or calloc and do the sub buffer copy call later on, the data wouldn’t be copied or perhaps the whole buffer is made invalid because of strange data.
You don’t have to copy the whole buffer each time, you can set the initial size of the buffer, but not actually repeatedly copy or draw the full buffer. When you call drawElements you pass only the number of sprites (multiplied by 6, for the six indices making up a quad) you want to show, and the rest of the buffer will be ignored. This way the you can ratchet up the number of sprites in the game and not have to create another VBO for them. If the sprites aren’t moving, or changing then don’t update and copy, just draw.
I am using two types of VBO/VAOs, one type for particles (see a future blog post) and one type for everything else.
(next – the non OpenGL parts of the template, and lessons)
iPhone 3D programming – Rideout – widely described as the best book on OpenGL for the iPhone. I think its good too, but wading through C++ is very dull, OpenGL ES 1 and 2.
Learning iOS game programming – Michael Daley – good reference – (OpenGL ES 1.0 only).
In the past he had some nice videos on the topic – currently he has some useful tools to sell. http://www.71squared.com/
iOS 5 by tutorials, Ray Wenderlich – chapter 8, is a nice place to start things for OpenGL ES 2.0. (nothing on multiple sprites/particles).
Apple WWDC talks on OpenGL 2010 and 2011
Jeff LaMarche’s website has some good openGL stuff: http://iphonedevelopment.blogspot.com/