Monday, February 20, 2012

Rude Gestures

In this post I'm changing the default OpenGL Game project to rotate based on user gestures instead of automatically.  This is actually super easy to do once you know how, but if you're like me and you don't know much about Objective C or Xcode, there are a couple of annoying gotchas.  I'm assuming you are running a Mac with Lion and already have downloaded Xcode and the iOS software development kit.  If not you can find both in the Mac app store.

To create the project, open Xcode, and choose File->New->New Project... from the Xcode menu bar.  Choose iOS Application on the upper left, and the OpenGL Game option in the middle of the window, then press the Next button.

On the next screen, name your project whatever you want.  I have no idea how to use an Xcode storyboard, so I leave that un-checked, but I like the idea of automatic reference counting.

You should end with a new OpenGL project with the project file opened on the screen.  I recommend taking a snapshot of your project now, so if you mess anything up you can revert.  Go to File->Create Snapshot... in the menu bar and follow the instructions.

You can run the project (Command-R) and see two cubes rotating around each other.  One is rendered using GLKit, which among other things is Apple's re-implementation of the OpenGL ES 1.x fixed functionality render pipeline, and the other is rendered using the OpenGL ES 2.0 vertex and fragment shaders.

Now to add gesture support we need to go to the interface builder and edit the nib file.  The Project navigator should be opened on the left side of the screen, showing a tree view of the files and folders that make up your project.  If it's not opened, you can show it by pressing Command-1 on the keyboard.  Once it's opened select the file with the .xib extension by single clicking on it.  You will see a big blank view.  Find the tap gesture recognizer in the object library in the lower right of the window.  You can filter for it by typing "gesture" in the little text box at the very bottom of the window.

Now drag the tap gesture recognizer from the object library to the blank view in the middle of the screen.  It's possible to drag the recognizer other places, such as the list of objects to the right of the view, but those are bad, wrong places that will cause the tap gesture recognizer to silently fail.  I say again, drag the tap gesture recognizer to the blank white area in the middle of your window, and nowhere else.  Once you drag it over, it will appear in the list of objects in the nib file.  

Now we need to add an action to our view controller.  Activate the assistant editor by pressing option-command-enter on your keyboard, or by clicking the little tuxedo button in the upper right of the window.  The view controller .h file should appear in a new pane on the right side of the window.

Now right click and drag from the tap gesture recognizer in the list of objects in the nib file (NOT from the object library) to the area in the view controller .h file between @interface and @end.  In the little context dialog box that pops up, select an Action connection and name it something meaningful.  I changed the type to UIPanGestureRecognizer, but I'm not sure it makes a difference.

Once you hit the Connect button you will see a new IBAction declaration, which is basically analogous to an event handler.  

The skeleton of its implementation was also automatically added to the view controller .m file, and that's the file we are going to edit next.

Get rid of the assistant editor by pressing command-enter on your keyboard or by going to the View->Standard Editor->Show Standard Editor menu option in the menu bar.  Then select the view controller .m file in the project navigator to the left of your window.  The first thing we want to do is comment out the last line in the update function which increments the rotation value.  The update function is called by the framework before every frame and it's this line that causes the cubes to automatically rotate around each other.

Now we need to update the rotation value ourselves, in the new pan gesture event handler that Xcode added for us at the bottom of the view controller file.  Add these two lines to the empty function:

    CGPoint translation = [sender translationInView:self.view];
    _rotation = translation.x / 100.0f;

The first line gets the amount that the user panned in both the x and y directions.  The second line sets our rotation value equal to the amount the user slide his finger back or forth.  We divide by 100 because the rotation value is in radians.  2*pi radians (a little over 6) is equivalent to a full 360 degree rotation, so if every frame the user drags their finger 4 or 5 pixels the cubes will rotate almost all the way around.  We want to slow that down so it looks better and is easier to control.

 Now run the project again.  When the simulator comes up the cubes will be stationary until you click and drag your mouse or trackpad across the screen.  Then you make the cubes rotate back and forth.

This post is very much the chronicle of me fumbling around in the dark with Xcode and iOS development. I'm writing this in the hopes of helping other people avoid the same pitfalls and blind alleys I stumbled on.  If you find any mistakes or know of a safer, saner, or more interesting way to do this, please let me know.

PS - One of the many cool things I learned how to do while writing this was how to take screenshots on the Mac.  Pressing Command-Shift-4 allows you to select an area of your screen to which will automatically appear as an image on your desktop.  Pressing Command-Shift-4, then the space bar will allow you to click on a window to take a screenshot of the entire window.

No comments:

Post a Comment