About six months ago I decided to learn objective-C and make an IOS game. It went well and I intend to release the game soon. The game’s controls use CoreMotion and the graphics use OpenGL ES and GLKit. I know there are good tutorials already out there (http://www.raywenderlich.com/) that I made use of, but I also know how useful it is to have a range of resources at your disposal. So this tutorial will start at the top for people who have never used Xcode or objective-C, and eventually it will outline the main ingredients that go into a motion-controlled IOS game. I will try to explain the parts I found hardest to grasp myself along the way.
Part 1 will teach the basics of getting an application running with Xcode 4.6 and Objective-C. Part 2 will use vertex array objects and vertex buffer objects to render a sprite. Part 3 will use motion data to move that sprite around the screen. I intend to test my own understanding by paring it back to the bare essentials, and I remember liking tutorials with screenshots so I will include a lot of them.
You should see a summary page. You can safely ignore everything but the Supported Interface Orientations. Since this is a tilt game, we probably won’t want the interface reorienting itself every time we change direction, so uncheck both landscape modes, leaving just Portrait.
Next we want to move from Summary to Build Phases and link our project to the necessary libraries. Select the drop-down for Link Binary With Libraries, you should see CoreGraphics, Foundation and UIKit already there. Add OpenGLES, GLKit and CoreMotion. OpenGL is a low-level API for rendering graphics, basically the goto graphics API for professional use, and OpenGL ES is a subset of that designed for use in embedded systems like mobile phones. GLKit is an Apple library designed to interact with OpenGL ES and make it a bit easier to work with. It also offers a collection of optimised math functions for vector and matrix operations, which will be spectacularly useful and save us a lot of time and calculation. CoreMotion, as you might guess, receives motion data from the hardware.
Now we’re ready to write some code. Go to AppDelegate.h and import <GLKit/GLKit.h> and <CoreMotion/CoreMotion.h>. You should see <UIKit/UIKit.h> already imported. It is kind of hard to explain exactly what the AppDelegate does because I don’t know exactly what it does, but essentially it is the highest level object in an IOS application. It interacts with the operating system and responds to touch and motion events. In our case, we need it to respond to events from our GLKView and GLKViewController, which we will get to shortly, so next to UIResponder (the class AppDelegate inherits from) in the pointy brackets (the protocols it follows) we need to add GLKViewControllerDelegate and GLKViewDelegate.
Now look at the implementation file AppDelegate.m. There should be a yellow warning next to @implementation AppDelegate that tells you you’re looking at an incomplete implementation. That is because GLKViewControllerDelegate and GLKViewDelegate are protocols we’ve said our AppDelegate will adhere to, which means implementing certain methods. In this case there are two methods, one for each. GLKViewDelegate requires a method for rendering each new frame:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
And GLKViewControllerDelegate requires a method for updating state information each frame:
- (void)glkViewControllerUpdate:(GLKViewController *)controller
Time to make some objects. There are three important objects we need to initialise at this point: an EAGLContext will manage various resources needed to draw using OpenGL ES, basically it’s necessary for rendering anything; our GLKView will manage a framebuffer object on our behalf, so that we have something to render to; and a GLKViewController will handle our rendering loop, doing its best to maintain a constant framerate for our animations. To see how to initialise each one you should read the documentation, but you will probably just look at my screenshot:
For those who aren’t that familiar with Objective-C I’ll explain it a bit. The line
is a bit of Obj-C magic that creates a private interface for the class, as opposed to the public one in AppDelegate.h. Since the only class at this stage that will need access to these objects is AppDelegate itself, we can put them in the private interface.
is another bit of magic that generates setter and getter methods for us. So we can access our objects with convenient dot notation, like:
self.context = ... ;
(These automatically generated methods can be overridden if necessary. As of Xcode 4.4 we no longer need to pair @property with a @synthesize in the implementation, as the compiler now does the equivalent of
@synthesize variable = _variable;
for us by default. Which was the line that told it to create accessor methods.) Assuming you’re using Xcode 4.4 or later, all you need to understand is that @property declares a variable that you can set and get.
Things get tricky with
... (nonatomic, strong) ...
These are cues for the compiler. Accessing a (nonatomic) variable is apparently faster than accessing an (atomic) variable, and as far as I can tell you would only need to consider an atomic variable if you were dealing with a multithreaded or multiprocess application.
(strong) vs (weak) is used for Automatic Reference Counting (ARC). If the variable is a pointer to an object, EG
it will be kept until there are no more strong pointers to it. So (strong) means, “add another strong pointer to this object and keep it as long as one exists”. A (weak) pointer on the other hand lets you use object without incrementing the ARC count, so it is useful for giving object_X a pointer to an object_Y that it didn’t create. For example, when my game’s Core creates a sprite object, it makes a strong pointer to it, so that it can’t be deallocated without the Core’s permission. But if the sprite object needs a pointer to the Core, I make it weak, because if the only pointer remaining to the Core is being held by a sprite, then the game is surely over and the Core should be deallocated. The only object that holds a strong pointer to my game’s Core is the AppDelegate that created it.
The third important one is (assign). If the variable is a primitive / scalar value, like an int, float, Boolean, or GLKVector2, it must be (assign).
Note that both the GLKView and GLKViewController need pointers to a delegate (our AppDelegate), the GLKViewController needs a pointer to a view (our GLKView), and the UIWindow needs a rootViewController (our GLKViewController). Also the current EAGLContext needs to be set to our context object. Also I deleted the default line that set our UIWindow’s background colour.
Lastly, there are two gl calls. glClearColor(red, green, blue, alpha) effectively sets the background colour. I’ve set it to green with no transparency (full alpha). I’ve put it in a new method, setupGL, so we can expand it later. For glClearColor to have any effect you need to call glClear(GL_COLOR_BUFFER_BIT), which sets every pixel to the glClearColor. If you comment it out, like
and then run the application in the iPhone Simulator you should see a magenta screen, like this:
Apparently that is the default background colour in IOS. Uncomment the line and run the application again and you should see this:
Obviously a vast improvement. glClear(GL_COLOR_BUFFER_BIT) should be called every frame before you draw the rest of your scene.
There ends Part 1. In Part 2 I will demonstrate using vertex array objects, vertex buffer objects, rendering sprites, and hopefully using motion data to move them around.