All of the source code presented in this series is based on the Oolong engine. I will refer to the examples when it is appropriate so that everyone can look the code up or try it on its own. This tip covers the very simple basics of a iP* app. Here is the most basic piece of code to start a game:
// “View” for games in applicationDidFinishLaunching
// get screen rectangle
CGRect rect = [[UIScreen mainScreen] bounds];
// create one full-screen window
_window = [[UIWindow alloc] initWithFrame:rect];
// create OpenGL view
_glView = [[EAGLView alloc] initWithFrame: rect pixelFormat:GL_RGB565_OES depthFormat:GL_DEPTH_COMPONENT16_OES preserveBackBuffer:NO];
// attach the view to the window
[_window addSubView:_glView];
// show the window
[_window makeKeyAndVisible];
The screen dimensions are retrieved from a screen object. Erica Sadun compares the UIWindow functionality to a TV set and the UIView to actors in a TV show. I think this is a good way to memorize the functionality. In our case EAGLView, that comes with the Apple SDK, inherits from UIView and adds all the OpenGL ES functionality to it. We attach this view than to the window and make everything visible.
Oolong assumes a full-screen window that does not rotate. It is always in widescreen view. The reason for this is that otherwise the accelerometer usage -to drive a camera with the accelerometer for example- wouldn't be possible.
There is a corresponding dealloc method to this code that frees all the allocated resources again.
The anatomy of a Oolong engine example uses mainly two files. A file with "delegate" in the name and the main application file. The main application file has the following methods:
- InitApplication()
- QuitApplication()
- UpdateScene()
- RenderScene()
The first pair of methods do one-time device dependent resource allocations and deallocations, while the UpdateScene() prepares scene rendering and the last method actually does what the name says. If you would like to extend this framework to handle orientation changes, you would add a pair of methods with names like InitView() and ReleaseView() and handle all orientation dependent code in there. Those methods would always been called when the orientation changes -only once- and at the start of the application.
One other basic topic is the usage of C++. In Apple speak this is called Objective-C++. Cocoa Touch wants to be addressed with Obj-C. So native C or C++ code is not possible. For game developers there is lots of existing C/C++ code to be re-used and its usage makes games easier to port to several platforms (quite common to launch an IP on several platforms at once). The best solution to this dilemma is to use Objective-C where necessary and then wrap to C/C++.
If a file has the postfix *.mm, the compiler can handle Objective-C, C and C++ code pieces at the same time to a certain degree. If you look in Oolong for files with such a postfix you will find many of them. There are whitepapers and tutorials available for Objective-C++ that describe the limitations of the approach. Because garbage collection is not used on the iP* device I want to believe that the challenges to make this work on this platform are smaller. Here are a few examples on how the bridge between Objective-C and C/C++ is build in Oolong. In our main application class in every Oolong example we bridge from the Objective-C code used in the "delegate" file to the main application file like this:
// in Application.h
class CShell
{
..
bool UpdateScene();
// in Application.mm
bool CShell::UpdateScene()
..
// in Delegate.mm
static CShell *shell = NULL;
if(!shell->Update()) printf(“Update error\n”);
An example on how to call an Objective-C method from C++ can look like this (C wrapper):
// in PolarCamera.mm -> C wrapper
void UpdatePolarCamera()
{
[idFrame UpdateCamera];
}
-(void) UpdateCamera
{
..
// in Application.mm
bool Cshell::UpdateScene()
{
UpdatePolarCamera();
..
The idea is to retrieve the id for a class and then use this id to address a function in the class from the outside.
If you want to see all this in action, open up the skeleton example in the Oolong Engine source code. You can find it at
Examples/Renderer/Skeleton
Now that we are at the end of this tip I would like to refer to a blog that my friend Canis wrote. He talks about memory management here. This blog entry applies to the iP* platforms quite well:
http://www.wooji-juice.com/blog/cocoa-6-memory.html
4 comments:
A couple things.
Identifiers that start with underscores are reserved for the implementation. Unfortunately, even Apple starts identifiers with underscores in their code samples.
A better solution would be to use trailing underscores for member variables.
For more details, Google has an Objective-C Style Guide that is quite good.
http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml
I'm not a big fan of Singletons. The Objective-C way to do this would be to declare a formal protocol.
http://en.wikipedia.org/wiki/Objective-C#Protocols
You could create a protocol called GameApplicationDelegate that was very similar to UIApplicationDelegate in Cocoa.
(Login Required for this link.)
http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html
I believe I do not understand what you said here: would any of your suggestions would make the code run faster or add functionality that it does not already available?
I get your style guide. I believe I just followed the style that was in the wizard created example.
Why would I want to create a protocl with the name GameApplicationData?
My apologies, I wasn't clear enough.
CShell is the main class in the Oolong Engine. It owns no data and only touches static memory. It has a lifetime that is equivalent to the lifetime of the application.
This is a very C++ way of building a framework.
The Objective-C way of building an extensible framework looks like this:
@interface AppController : NSObject '<'GameApplicationDelegate'>'
{
}
@end
With the GameApplicationDelegate looking like this:
@protocol GameApplicationDelegate
- (void)update;
- (void)render;
@end
Now, think about this from the client's perspective. To build their game, the client conforms to the GameApplicationDelegate protocol.
The framework is extensible because the client can pick and choose which protocols to conform to.
For example, another protocol that the client may want to conform to is UIAccelerometerDelegate. This would look like this:
@interface AppController : NSObject '<'GameApplicationDelegate,
UIAccelerometerDelegate' >'
{
}
@end
In this way, new functionality is added to the framework in cohesive, decoupled slices.
The client gets to pick and choose which protocol to conform to based on which features they would like to support.
Hey Parveen, thanks for explaining this to me.
I see what you mean. In case I would follow your advice I could still drop-in C++ code but would have an easier access to the Cocoa touch elements.
Thanks for bringing this up!
- Wolfgang
Post a Comment