iPhone Game Development - Chris Craft [104]
- (void)startAnimation {
animationTimer = [NSTimer scheduledTimerWithTimeInterval:ANIMATION_INTERVAL
target:self selector:@selector(animateView) userInfo:nil repeats:YES];
}
- (void)stopAnimation {
animationTimer = nil;
}
#pragma mark Animate Layers
- (void) animateView {
[self updateView];
[self drawView];
}
Separation of game logic into updateView and drawView was the goal of using CALayer objects and the NSTimer. This allows the application to manage positional data and game physics in the method updateView. Then we can code rendering and drawing separately in the method drawView.
Now inside the method drawView we can manually change the position of each CALayer. Remember that position, velocity, and rotation puck and paddles are managed in structures that are not part of the CALayer. In the updateView method, we update the position inside of the structures independent of the layers. When this is complete and drawView is called, we render the view by updating the CALayer objects with data from these structures inside of a CATransaction:
- (void)drawView {
// Wrap these layer changes in a transaction and set the animation
// duration to 0 so we don't get implicit animation
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithDouble:0.]
forKey:kCATransactionAnimationDuration];
// Position the paddle
bottomPaddleLayer.position = bottomPaddleData.paddleLocation;
topPaddleLayer.position = topPaddleData.paddleLocation;
puckLayer.position = bottomPaddleData.puckLocation;
puckLayer.transform = CATransform3DMakeRotation(bottomPaddleData.puckAngle,
0., 0., 1.);
if (messageView) {
messageView.layer.transform = CATransform3DMakeRotation(messageRotation,
0., 0., 1.);
}
[CATransaction commit];
}
Notice that in drawView we are updating the internal CALayer of the UIView messageView. This is an example of how the UIView and CALayer can work together. This is being implemented in this manner because it gives us a smooth rotation of the messageView that will continue over the course of two separate animations. We will show the rest of the message animation a little later in the chapter.
Calculating believable physics
For a game like this, physics are necessary to emulate a realistic experience. Calculating believable physics can be a real challenge. The following methods are full of calculations, algorithms, and math, working hard to produce believable physics of the puck bouncing around on the hockey table:
- (void) updateView {
if (connectionType != CLIENT_CONNECTION) {
[self updatePuck];
}
// update the message rotation; this is only used if messageView
// is visible;
if (messageView) {
messageRotation += 0.05;
if (messageRotation > 100*3.24)
messageRotation = messageRotation - (100*3.24);
}
}
In the updateView method above, you see a call out to updatePuck. This method is really the meat of the physics engine. The function of each line in this method is not as easily discoverable as most of the others. To compensate, updatePuck is decorated with comments much more heavily than the norm (Listing 7.3).
Listing 7.3
Calculating the Next Position of the Puck
- (void)updatePuck {
// handle the puck hitting the paddle
// Calculate the distance between the centers of the puck and the paddle
double dist = distance(bottomPaddleData.puckLocation.x,
bottomPaddleData.paddleLocation.x, bottomPaddleData.puckLocation.y,
bottomPaddleData.paddleLocation.y);
if (!bottomPaddleData.intersect & dist <