Gravity Tutorial for iPhone, Part 5
24249 단어 gravity
For the final installment in this series, we’ll add accelerometer support to the ball, allowing you to tilt the device to change the direction of “down”. Note, although you can rotate the device in the iPhone simulator, this does not simulate acceleration. The only way you’ll be able to see this in action is on a physical device. This means that you’ll have to pay $99 sign up for and be accepted to the iPhone Developer program, and figure out how to provision apps to your device. I’m not going to cover that here, so I’ll assume that you’ve figured that out on your own. OK, let’s jump in.
So far, we have gravity in the ball, affecting its y velocity. If we know which way the device is tilted, we can change that to affect the velocity on the x axis as well, proportional to the degree of tilt. For example, if you tilted the device 90 degrees to the left, gravity should only affect the x velocity. If you turn it upside down, it will affect only the y velocity again, but in the opposite direction. In most cases, it will not be exactly straight, so there will be some x velocity and some y, proportional to the angle of tilt.
To respond to acceleration, we need to do three things:
1. Have a class specify the UIAccelerometerDelegate protocol. This is similar to implementing an interface in ActionScript. But less strict. It’s telling the application that you expect this class to respond to acceleration, and it may have some of the methods associated with this protocol. But unlike an interface, you are not required to implement all or even any of the methods.
2. Set the class as a delegate for accelerometer events. Think of this as adding an event listener.
3. Create a method to respond to the events. Here is where you will change gravity.
OK, step 1, specifying the protocol. We’ll have the Ball class do this directly, as that’s where gravity is. You just put the protocol name in angle brackets after the class name and and super classes, like so:
http://www.bit-101.com/blog/?p=1824
1
2
@interface Ball : NSObject <UIAccelerometerDelegate> {
...
I’m not really sure what this does to be honest, as it seems you can be a delegate without specifying the protocol (although you will get a warning), and you can specify the protocol without being a delegate. But you’re supposed to do it, so do it.
Step 2. Set self as a delegate. We’ll do that in the init method in Ball.m:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (id) init
{
self = [super init];
if
(self != nil) {
position = CGPointMake(100.0, 100.0);
velocity = CGPointMake(5.0, 5.0);
radius = 20.0;
color = [UIColor greenColor].CGColor;
bounce = -0.9f;
gravity = 0.5f;
dragging = NO;
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
}
return
self;
}
sharedAccelerometer is a class (static) property of the UIAccelerometer class, I assume it’s like getInstance of a Singleton. You are calling setDelegate on that object, passing self (this). Simple enough.
Step 3. Create a method which will be called on acceleration. Again, right in Ball.m:
?
1
2
3 - (
void
)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
NSLog(@
"x: %f, y: %f"
, acceleration.x, acceleration.y);
}
You don’t need to declare this, as you specified the protocol, which, come to think of it, is probably why you set the protocol.
The method is called accelerometer and passes a pointer to a UIAccelerometer object, and a pointer to a UIAcceleration object. It’s this last one we’re interested in, as that will tell us the amount of tilt.
Here I’ve just put in a log statement so you can see that it’s working. The acceleration parameter passed in will contain x, y, and z properties, which will range from -1.0 to +1.0. We’re only interested in x and y. x will be -1.0 when the device is tilted 90 degrees to the left, 0.0 when it’s upright and +1.0 when tilted to the right. Likewise, y will be -1.0 when upright, and vary towards 0.0 on either side, and then to +1.0 when the device is upside down.
So, rather than a single value for gravity, we’ll need x and y values for gravity. We can store these in a point object. Declare it in Ball.h:
?
1 CGPoint acceleratedGravity;
Leave gravity there, as that will affect the overall strength of gravity and generally stays the same. This new variable will change constantly according to tilt.
You can also initialize this in the init method:
?
1 acceleratedGravity = CGPointMake(0.0, gravity);
Note that the x value is initialized to 0.0 and y to gravity, indicating no rotation.
Now we can change the acceleration method to alter this variable:
?
1
2
3
4 - (
void
)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
acceleratedGravity.x = acceleration.x * gravity;
acceleratedGravity.y = -acceleration.y * gravity;
}
Note that the y value is reversed, otherwise the ball would fall “up”.
Finally, we just need to alter the update method in Ball.m to utilize acceleratedGravity instead of just gravity:
?
1
2
3
4
5
6
7
8
9 - (
void
)update {
if
(dragging)
return
;
velocity.x += acceleratedGravity.x;
velocity.y += acceleratedGravity.y;
position.x += velocity.x;
position.y += velocity.y;
...
And that’s it! Pretty simple actually. Now you tilt the device and the ball will always fall “down”. It will settle in a corner, or on the “top” of the device, or whatever direction is down. Amazing just how easy that was. Less than 10 lines of code in all.
And that brings us to the end of this series. Bear in mind that I’m a beginner at this stuff, and was writing this for my own selfish reasons – to learn the language and drill it into my head. I’ve already seen several places where the code in this project is not up to snuff at all. I may go back and correct it at some point, but I’ll just say for now to take what I’ve written here as a jump start. It will get you up and running, but you really need to learn more about the right way to do things, like I am already doing. And if you find something that indicates I did something incorrectly, it’s probably right.