Monday, August 31, 2009

a ping-pong game (1)

One of the goals that I postponed long ago was learning OpenGL and creating games with it. I started learning OpenGL, and started building a toy project on the process.
I decided to build a ping-pong game which will be simply a ball moving and hitting around. A simple 2D toy project.
I chose to develop on my mac with XCode & Objective-C. XCode is a simple IDE that works fast and Objective-C is a Object Oriented programming language much simpler that C++. A mac comes with a OpenGL so on a Mac there is not any prerequisite to start developing. Although the source code is written on such platform it wont be hard to port to other platforms.
Code is on svn here.
I have also tagged bar and bars versions which I will be blogging about now.
Setting Up the OpenGL
This is how (code snippets from main.m) :
1:  glutInit(&argc, argv);
glutInit function initializes the GLUT. Parameters are for the underlying Windowing System.
1:       glutInitWindowPosition(400,100);
2: glutInitWindowSize(400,300);
We initialize the windows size & position.
1:       glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
We use a single buffer & rgb values for now.
1:       glutCreateWindow("Intro");
This sets the title of the window.
1:       glClearColor(0.0,0.0,0.0,0.0);
This function is like setting the background color.
Setting the Callback Functions
OpenGL is designed with the "Hollywood Principle"; "Don't call me, I'll call you...". To draw on screen and listen to events you register a set of callback functions (from BarCallbacks.m);
1:       glutDisplayFunc(display);
2: glutReshapeFunc(reshape);
3: glutKeyboardFunc(keyHandler);
4: glutKeyboardUpFunc(keyUp);
Display callback function is for actually drawing on screen. Reshape function is for redisplaying after the window moves or resizes. Keyboard functions are for detecting key strokes of the user.
These callback function are 'C' functions and this means that you can not use Objective-C methods directly. To use the Objective-C methods you have to use a static variable. I set up a pattern where the 'C' callback functions delegate to Objects and their appropriate methods. This way I can take advantage of the object oriented paradigm.
I created a BarCallbacks Object which is simply responsible of drawing rectangles on screen. Here is the display function and its delegate;
1:  static BarCallbacks * workingCallback;
2: void display() {
4: [workingCallback display];
5: glFlush();
6: }
7: -(void) display {
8: // loads the identity matrix
9: glLoadIdentity();
10: // floating point red, green, blue values
11: // 0 to 1 probably but may change I guess 3if ?
12: glColor3f(0.0,0.0,1.0);
13: glRectf(box.x - box.xr, box.y - box.yr, box.x + box.xr, box.y + box.yr);
14: }
workingCallback is the static variable used to delegate from the C function to the Object method.
glClear function (line 3) clears the screen with the color we previously specified. Then we draw the rectangle. glColor3f (line 12) sets the drawing color to blue and glRectf draws the rectangle to specified coordinates.
Another thing we need to setup is the perspective. There are two types of perspectives. I used the Orthogonal Perspective which is I think is easier to use for this kind of app.
1:  glOrtho(0, x, y, 0, 0, 1);
x and y are the width and height of the window. Since this will be a 2D app. I gave the Z-index 1. This function is called from the reshape function.
After setting up the callbacks OpenGL system, machine is more appropriate I think, is started with;
1:  glutMainLoop();
Our rectangle will be displayed, if you put break points you will observe that user key-strokes are caught. I tagged all the code up to this point as bar.
With the BarCallbacks Object we created, I wanted to make some walls where our ball will bounce around. Here are the changes I made to delegating system so that we can use multiple BarCallbacks;
1:  static NSMutableArray * callbacks;
2: void display() {
4: for( int i = 0; i < callbacks.count; i++) {
5: BarCallbacks * cb = [callbacks objectAtIndex:i];
6: [cb display];
7: }
8: glFlush();
9: }
I changed the static variable to an mutable array. Then looped on on callback methods and delegated.
Here is my three walls;
1:       [[BarCallbacks alloc] initWithRect:390 y1:10 w:10 h:280];
2: [[BarCallbacks alloc] initWithRect:0 y1:10 w:10 h:280];
3: [[BarCallbacks alloc] initWithRect:0 y1:0 w:400 h:10];
And how it looks;Next we need a ball, a platform and some collision detection.