Pages

Showing posts with label Objective-C. Show all posts
Showing posts with label Objective-C. Show all posts

Monday, September 7, 2009

a ping-pong game (2)

Continuing where we left from part 1.
First we need a platform that the user control. Our platform will be a BarCallbacks with key handler. To control the movement of the objects we will introduce a new object 'World' which will handle the movement code. The world object is a timer object that will tick every few seconds and recalculate the coordinates of the registered vectors and redisplay the screen. Also the VectorBox object now will have distance and angle making it a real vector. VectorBox object here :
1:  @interface VectorBox : NSObject {
2: float x, y, xr, yr, distance, angle;
3: }
4: @property float x, y, xr, yr, distance, angle;
5: -(id) initWithX:(float)_x y:(float)_y xr:(float)_xr yr:(float)_yr;
6: -(id) initWithX:(float)_x y:(float)_y xr:(float)_xr yr:(float)_yr distance:(float) _d angle:(float) _a;
7: @end
Distance will determine how many pixels will the object travel with the tick of the clock. Angle is at what angle will the object move. The init function without the distance and angle will set these values to zero.
And the World object :

1:  @interface World : NSObject {
2: NSMutableArray * boxes;
3: NSUInteger milis;
4: }
5: @property (readonly) NSUInteger milis;
6: @property (readonly) NSMutableArray * boxes;
7: -(id) addVectorBox:(VectorBox *) vbox;
8: -(id) startTickingEveryMilis:(NSUInteger) milis;
9: +(id) world;
10: void worldTick(int value);
11: @end
The 'boxes' parameter will hold the vectors that will be animated. The 'milis' parameter is the number of milliseconds that our code will wait before firing to animate our vectors. addVectorBox method adds new vectors and startTickingEveryMilis method sets the timer. The world object is to be used as a 'singleton', one copy of it should be available at any time, and to access that instance we will use the static 'world' method. worldTick function is the our glut timer callback function. Its registered first as the startTickingEveryMilis method is called. worldTick function :
1:  void worldTick(int value) {
2: World * world = [World world];
3: int len = [world.boxes count];
4: for( int i = 0; i < len; i++) {
5: VectorBox * vbox =[world.boxes objectAtIndex:i];
6: vbox.x += vbox.distance*cos(vbox.angle);
7: vbox.y += vbox.distance*sin(vbox.angle);
8: }
9: glutPostRedisplay();
10: glutTimerFunc(world.milis,worldTick,1);
11: }
It traverses of the boxes array and moves them using a simple trigonometric function and updates the coordinate values. Then the glutPostRedisplay function is called so the screen is redrawn. Using the glutTimerFunc we reregister our callback function. PlatformCallbacks extends from the BarCallbacks it just adds the key handler code to it.
1:  -(void) keyHandler:(unsigned char) key x:(int) x y:(int) y {
2: if(key =='d') {
3: box.angle = 0;
4: box.distance = distance;
5: } else if(key =='a') {
6: box.angle = M_PI;
7: box.distance = distance;
8: }
9: }
10: -(void) keyUp:(unsigned char) key x:(int) x y:(int) y {
11: box.distance = 0;
12: }
Glut calls the keyHandler function as long as the key is pressed. So when the 'd' key is pressed we will set the registered vectors distance to move right. keyUp function is called when the key is released so we will use it to set the vectors distance to zero thus making it stop.
Now we need to implement our bouncing ball. First thing I am going to do is refactoring the code so that we may have a common base class for BarCallbacks and BallCallbacks. I will call it the BaseCallbacks. All my callbacks will extend from this base. OpenGL does not have circle driving function by default so we need to come up with a approximate formula :
 @implementation BallCallbacks
-(void) display {
// loads the identity matrix
glLoadIdentity();
glColor3f(1.0,0.0,0.0);
glBegin(GL_POLYGON);
for (int i = 0; i < 360; i++) {
float x1 = (cos((M_PI*i)/180) * box.xr) + box.x;
float y1 = (sin((M_PI*i)/180) * box.yr) + box.y;
glVertex3f(x1,y1,0);
}
glEnd();
}
@end
Using glBegin function we start drawing vertex to vertex. Our circle is actually a polygon with 360 vertexes. We end drawing with glEnd function. After initializing with distance and angle our ball will start moving. As its now our ball will pass through our walls. To make it bounce we need to detect collision and change the angle appropriately. We will be checking for collisions at every tick of the clock in the World object :
 void worldTick(int value) {
World * world = [World world];
int len = [world.boxes count];
for( int i = 0; i < len; i++) {
VectorBox * vbox =[world.boxes objectAtIndex:i];
vbox.x += vbox.distance*cos(vbox.angle);
vbox.y += vbox.distance*sin(vbox.angle);
}
for( int i = 0; i < len; i++) {
VectorBox * vbox =[world.boxes objectAtIndex:i];
if( !vbox.changesAngleAfterCollision) {
continue;
}
for( int j = 0; j < len; j++) {
if( i == j) {
continue;
}
VectorBox * obox =[world.boxes objectAtIndex:j];
[vbox detectCollision:obox];
}
}
glutPostRedisplay();
glutTimerFunc(world.milis,worldTick,1);
}
We check for collision with objects which changes its angle after collision. In our case we only need to check collision of ball and the other objects.
 -(id) detectCollision:(VectorBox *) obox {
// check if this object collides with other from bottom
if(obox.x + obox.xr > x && obox.x - obox.xr < x ) {
if(obox.y + obox.yr > y + yr && obox.y - obox.yr < y + yr) {
angle = 2*M_PI - angle;
}
}
if(obox.x + obox.xr > x && obox.x - obox.xr < x ) {
if(obox.y + obox.yr > y - yr && obox.y - obox.yr < y - yr) {
angle = 2*M_PI - angle;
}
}
if(obox.y + obox.yr > y && obox.y - obox.yr < y ) {
if(obox.x + obox.xr > x + xr && obox.x - obox.xr < x + xr) {
angle = 3*M_PI - angle;
}d
}
if(obox.y + obox.yr > y && obox.y - obox.yr < y ) {
if(obox.x + obox.xr > x - xr && obox.x - obox.xr < x - xr) {
angle = 3*M_PI - angle;
}
}
return self;
}
This works pretty smoothly most of the time but sometime ball gets stuck.
This is how it looks like in the end :

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() {
3: glClear(GL_COLOR_BUFFER_BIT);
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.
MainLoop
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() {
3: glClear(GL_COLOR_BUFFER_BIT);
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.

Wednesday, May 20, 2009

Moving the keyboard out of the way

After happily doing a little work for iphone, being content for that I was finished with it, I made realize that the text boxes near the bottom of the screen disappeared when the keyboard appeared after saying "hmmmm...", I started searching for a solution. Apples iphone developer library suggest this solution and here I am going to write about how I used it in my project. 
Here is how the problem looks like  in a simple case(a before),
What we are going to do is, we are going to define UIScrollView and put our view inside it and scroll it up when the keyboard shows up. In order to do it in a OO fashion I defined a base class that will handle the scroll details.
#import <UIKit/UIKit.h>

@interface BaseViewController : UIViewController <UITextFieldDelegate> {
bool keyboardShown;

UITextField * activeField;

UIScrollView * scrollView;

UIView * mainView;
}

@property (nonatomic, retain) IBOutlet UIScrollView * scrollView;
@property (nonatomic, retain) IBOutlet UIView * mainView;

@end
I will bind the mainView with the original view which contains the text field. The scrollView will be inside a proxy view. The implementation;
#import "BaseViewController.h"

@implementation BaseViewController

@synthesize mainView, scrollView;

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
return [theTextField resignFirstResponder];
}


- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}

- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
if (keyboardShown)
return;

NSDictionary* info = [aNotification userInfo];

// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;

// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [scrollView frame];
viewFrame.size.height -= keyboardSize.height;
scrollView.frame = viewFrame;

// Scroll the active text field into view.
CGRect textFieldRect = [activeField frame];
[scrollView scrollRectToVisible:textFieldRect animated:YES];
keyboardShown = YES;

}


// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];

// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;

// Reset the height of the scroll view to its original value
CGRect viewFrame = [scrollView frame];
viewFrame.size.height += keyboardSize.height;
scrollView.frame = viewFrame;
[scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
keyboardShown = NO;
}

- (void)viewDidLoad {
[super viewDidLoad];
keyboardShown = false;
scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollView.scrollEnabled = NO;
scrollView.pagingEnabled = NO;
[scrollView setContentSize:CGSizeMake(320, 480)];
[scrollView addSubview:mainView];
[self registerForKeyboardNotifications];
}

@end
The example is here under KeyboardLift. And this is the after screenshot;

Thursday, March 12, 2009

a Java developers Objective-C comments

This my language flame post ;)
So last month I have started developing for iPhone. In order to do so you have to use Objective-C because by default iPhone does not come with Java Virtual Machine. These are the things I most stumbled on Objective-C:
1. Square brackets !
When procedural language people see Lisp code first thing they say is : "What's with all that parentheses ?". Objective-C allthough being procedural, object oriented, language makes me say "What's with all that square brackets ?". You use square brackets to send messages to methods they are soon all around your code and look ugly.
2. Defining Classes and Properties
Objective-C adopts the single inheritance model like Java but, you have to define your classes in "the old header" files and implement them in a seperate, C file. There is no one public class for one file enforcement by the language so you are free to mess your class definitions and go "Where did I defined that ?".
If you want to define a property to use in the interface builder, you have to define it in three places. One in class definition, one to mark it as UI thing and the last is to generate getters, setters.
3. Memory Management Model and When Things Go Wrong
Hardest problems to spot for a Java guy like me was the pointer (or memory) access problems. You have to know what "retain" and "release" does before you start doing anything.
And Objective-C does not have the Java's detailed stack traces so when things go wrong you can have serious headaces and need to hit your head to walls. 
Still its realy fun to develop for the iPhone. Where else you can build a application that responds to your swings ?