Game Development Reference
In-Depth Information
In Listing 5-6, the first thing we do in
viewTapped:
is to cast the passed in
UIGestureRecognizer
to
UITapGestureRecognizer
. We then find the point where the tap occurred in the root view by calling
locationInView:
. Once we have the point where the tap occurred, we simply set the
moveToPoint
property on the
viper
object. Notice that we did not have to interrupt any existing animation—we
simply update the state of the
viper
object and, when its
updateLocation
task is called again, it will start
moving toward this new point. This is a very simple example, but I hope it illustrates an advantage that
frame-by-frame animations have over the predefined animations we looked at in the last chapter.
We have looked at how this simple animation is set up and how it is driven by repeated calls to
updateScene
from Listing 5-4. We should take a little time and understand the classes responsible for
making these repeated calls.
Understanding CADisplayLink and NSRunLoop
In general, to create a smooth-looking animation, a picture has to be updated at least 25 times a
second to avoid the eyes seeing each individual frame. That means our game must find a way to call
updateScene
at 25 times a second to achieve a smooth animation. This could be done by creating an
NSTimer
that calls
updateScene
at any rate we choose. If we do that, however, our code that updates
the scene will not necessarily be in sync with the hardware's native refresh rate of the screen. The
framework Core Animation provides a class specifically designed for creating the type of animation
we are trying to create:
CADisplayLink
.
In Listing 5-3, we created a
CADisplayLink
, specifying that it should call
updateScene
whenever
the screen is ready to redraw. We then added the
CADisplayLink
to the same run loop that called
viewDidLoad
by calling
currentRunLoop
on the class
NSRunLoop
. The class
NSRunLoop
manages a
thread to produce the kind of loop shown in Figure
5-1
. An
NSRunLoop
is responsible for processing
input from the user and from the
CADisplayLink
and scheduling when the corresponding task in
Example01Controller
should be called. We don't have to understand too much about how
NSRunLoop
works; we just have to understand that the tasks
viewTapped:
and
updateScene
will be called by the
same thread so we don't have to worry about multi-threaded complexity.
As mentioned,
CADisplayLink
—working with our main threads
NSRunLoop
—causes the task
updateScene
to be called once for each time the screen redraws. The property duration of
CADisplayLink
reports, in seconds, the time between each screen redraw. A little investigation shows
that this property reports a slightly different value each time
updateScene
is called, but hovers around
the value 0.0166648757. This value is just shy of 60 frames per second, so we are well above the
required 25 frames per second.
In more complex applications, it is entirely likely that the state of the game cannot be updated in
1.5 milliseconds; there may simply be too much bookkeeping. If this is the case, you can set the
frameInterval
property of
CADisplayLink
to value greater than one. This will cause
CADisplayLink
to skip screen redraws. Figure
5-6
shows four different scenarios that can occur when using the
CADisplayLink
.