iPhone Tutorial: Using the iPhone's accelerometer with Cocos2D
In this part of the tutorial I'll explain how to use the iPhone's built in gyroscope to control the tilt of the labyrinth board that we created in the earlier iPhone tutorial. In Cocos2D this is very easy to do but before we start it's worth explaining what kind of information you're going to get from the accelerometer. The accelerometer measures force - direction and strength. The phone will experience forces for two reasons. Firstly, gravity is always applying a downwards force on the phone towards the centre of the earth. The phone will also experience forces if you apply them to it with your hands. By waving the phone around or shaking it. In Cocos2D we access these forces by overriding a method in our HelloWorldLayer. When we do this we gain access to three variables: x-force, y-force and z-force. From the diagram you can see the directions of these components. Using these forces we can gain some idea about what's happening to the phone. If the phone is being held quite still, then the only force acting will be gravity. Knowing this we can find the exact orientation of the phone.
For our labyrinth game we're only going to be using the x and y force components and we're going to wire up these componentes directly to the worlds gravity.
Setting up the project:
Open up the labyrinth project we've been working on. Firstly we need to make a few tweaks. In the AppDelegate file change the shouldAutorotateToInterfaceOrientation to look like this:
// Supported orientations: Landscape. Customize it for your own needs - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return interfaceOrientation == UIInterfaceOrientationLandscapeLeft; }
This means that the screen will only support one landscape mode. If you didn't do this when you tilted the phone away from you the screen would rotate to that direction.
Next we're going to disable retina support. ShapeWorkshop can support retina displays but you would need to create separate hd images. For this tutorial we're not going to do that so it's best for retina to be disabled. In the app delegate in the application method find this line and make sure it's set to "NO":
// Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices if( ! [director_ enableRetinaDisplay:NO] ) CCLOG(@"Retina Display Not supported");
Finally, we need to delete the code which simulates tilting by clicking on the screen. In the HelloWorldLayer remove the following code:
- (void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; [self updateBallForce:touch]; } - (void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; [self updateBallForce:touch]; } -(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { b2Vec2 force (0,0); _ballForce = force; NSLog(@"Force: %f, %f", _ballForce.x, _ballForce.y); } - (void) updateBallForce: (UITouch *) touch { // Get the location of the touch CGPoint location = [touch locationInView:touch.view]; // Find the position relative to the centre of the screen // remember the screen is 480x320 with the origin at the // top left hand corner float xRel = location.x - 240; // Make it so y-up is positive negative float yRel = -location.y + 160; // Scale xRel and yRel so they vary between 0 and 1 xRel = xRel / 240; yRel = yRel / 160; // Now apply a force to the ball float maxForce = 15 ; // Check that there is a ball if(_ball != Nil) { b2Body * ballBody; // The body is stored as a pointer wrapped in an NSValue // get a reference to the NSValue NSValue * bodyValue = _ball.physicsLink; // Populate ballBody with the reference [bodyValue getValue:&ballBody]; // Set the linear damping. This means that if the ball // will gradually come to rest if we don't tilt the // phone. ballBody->SetLinearDamping(0.5); // Make sure the body is awake ballBody->SetAwake(YES); // Create a new force depending on where we've touched on the screen b2Vec2 force (xRel * maxForce * ballBody->GetMass(), yRel * maxForce* ballBody->GetMass()); _ballForce = force; } } // -(void) update: (ccTime) dt { // . . . . . // Delete this bit: if(_ball != Nil) { // Apply the force to the ball bodyValue = _ball.physicsLink; [bodyValue getValue:&ballBody]; if(_ballForce.x!=0 && _ballForce.y != 0) { ballBody->ApplyForce(_ballForce, ballBody->GetWorldCenter()); } } // . . . . . . //}
Adding the gyroscope:
Now we need to add code for the gyroscope. First add the following to the init method in HelloWorldLayer to activate the accelerometer in Cocos2D:
self.isAccelerometerEnabled = YES;
Next add the following function:
// Override the accelerometer method -(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { if(_ball != Nil) { // Get a pointer to the ball's physics body b2Body * ballBody; NSValue * bodyValue = _ball.physicsLink; [bodyValue getValue:&ballBody]; // Make sure the body is awake (if it's not awake it won't be affected by gravity) ballBody->SetAwake(YES); } // Set the gravity by the tilt angle b2Vec2 gravity ( 20 * acceleration.y, - 20 * acceleration.x); NSLog(@"Gravity: %f, %f", gravity.x, gravity.y); _world->SetGravity(gravity); NSLog(@"Acceleration: %f, %f, %f", acceleration.x, acceleration.y, acceleration.z); }
Run the project on an iPhone or iPod Touch and the ball should now be controlled by the accelerometer.
I hope you've enjoyed this tutorial. If you have any feedback or comments about future tutorials you'd like to see please post them below or on the forum.
Add new comment