Applying Sprite Animation to Mobile Games
Now that you have a basic
understanding of the fundamental types of animation, you’re probably
wondering which is best for games. I’ve already alluded to the fact that
cast-based animation is more efficient and often gives you more control
over game graphics, but the truth is that most games use a combination
of the two animation techniques. Each technique offers its own unique
benefits, so combining the techniques gives you capabilities that would
be hard to get by using one of them alone.
A good example of how games
often require the use of more than one animation technique is an
animation of a person walking. You obviously need to be able to alter
the position of the person so that he appears to be moving across a
landscape. This requires cast-based animation because you need to move
the person independently of the background on which he appears. However,
if we let it go at this, the person would appear to just be sliding
across the screen because he isn’t making any movements that simulate
walking. To effectively simulate walking, the person needs to move his
arms and legs as a real person does when walking.
This
requires frame-based animation because you need to show a series of
frames of the leg and arm movements. The end result is an object that
can both move and change its appearance, which is where the two
animation techniques come together.
Sprites are incredibly
important in virtually all two-dimensional games because they provide a
simple, yet effective means of conveying movement while also enabling
objects to interact with one another. By modeling the objects in a game
as sprites, you can create some surprisingly interesting games in which
the objects interact with each other in different ways. The simplest
example of a sprite used in a game is Pong, which involves a total of
three sprites: the ball and the two paddles (vertical bars) along each
side of the screen. All these objects must be modeled as sprites because
they all move and interact with each other. The ball flies around on
its own and bounces off the paddles, which are controlled by each of the
two players.
As games get more complex,
the role of sprites changes slightly, but their importance only
increases. For example, a tank battle game would obviously use sprites
to model the tanks and bullets that they shoot at each other. However,
you could also use sprites to represent stationary objects such as walls
and buildings. Even though the stationary objects don’t move, they
benefit from being modeled as sprites because you can detect a collision
between them and a tank and limit the tank’s movement accordingly.
Similarly, if a bullet strikes a building, you would want to destroy it
or make it ricochet off the building at an angle; modeling the building
as a sprite allows you to detect the bullet collision and respond
accordingly.
Working with the Layer and Sprite Classes
I mentioned earlier
that the MIDP 2.0 API includes support for sprite animation. The two
primary classes that make sprite animation possible in MIDP programming
are the Layer and Sprite classes. The Layer class models a general graphical object known as a layer,
which serves as the foundation for sprites and other graphical game
objects. You can think of any discrete visual element in a game as being
a distinct layer. From a programming perspective, the Layer class keeps track of information such as the position, width, height, and visibility of a visual element.
It’s important to note that the Layer class is an abstract class, which means you can never directly create an instance of a Layer object. Instead, you create instances of objects derived from Layer, such as Sprite or your own Sprite-derived class. Subclasses of the Layer class must implement their own paint() methods so that they can be drawn.
Construction Cue
The initial location of a layer is (0,0), which is interpreted relative to the coordinate system of the Graphics object that is passed into the layer’s paint() method. |
Following are the methods defined in the Layer class, all of which are important when it comes to working with layers and sprites:
getX()— Gets the X position of the layer’s upper-left corner
getY()— Gets the Y position of the layer’s upper-left corner
getWidth()— Gets the layer’s width
getHeight()— Gets the layer’s height
setPosition()— Sets the XY position of the layer’s upper-left corner
move()— Moves the layer by a specified XY amount
isVisible()— Gets the layer’s visibility
setVisible()— Sets the layer’s visibility
paint()— Overridden in derived layer subclasses
The Sprite class builds on the Layer
class by providing additional functionality required of animated
two-dimensional graphical objects. The primary functional additions in
the Sprite class are as follows:
Sprites are based on images, and support multiple frame images.
The image(s) for a sprite can be transformed (rotated, mirrored, and so on).
You can define a reference pixel, which serves as the basis for sprite transformations and positioning.
For sprites with multiple frame images, the sequence in which the images are displayed can be set precisely.
Collisions can be detected between sprites through the use of rectangle, shrunken rectangle, or image data collision detection.
As you can see, the Sprite class offers a considerable number of features for mobile game programming. For now, let’s focus
on the basics of getting a sprite up and running. To create a sprite
based on a single image, just pass a newly created Image object into the Sprite constructor, like this:
Sprite monsterSprite = new Sprite(Image.createImage("/Monster.png"));
In this example, a monster
image is used as the basis for creating a monster sprite. The problem
is, if you placed this code by itself into a MIDlet, you would get
compile errors because an I/O exception is not being caught. This
exception can be thrown by the createImate() method in response to a failure loading an image. Here’s how to package the code in a try-catch clause to get it running smoothly:
try {
monsterSprite = new Sprite(Image.createImage("/Monster.png"));
monsterSprite.setPosition(0, 0);
}
catch (IOException e) {
System.err.println("Failed loading image!");
}
Even though the Layer
class defaults the position of every layer to (0,0), it’s still a good
idea to get in the habit of initializing the position of all your
sprites, as this code shows. With your sprite loaded and ready, all you
must do to move it around on the screen is call its setPosition() or move() methods. Then you follow that code with a call to the paint() method to draw the sprite. The following steps show exactly how to accomplish these tasks:
1. | Following is an example of using the setPosition() method to center a sprite on the screen:
monsterSprite.setPosition((getWidth – monsterSprite.getWidth()) / 2,
(getHeight – monsterSprite.getHeight()) / 2);
This code uses the canvas width and height in conjunction with the
sprite’s width and height to center the sprite on the screen.
|
2. | Moving
a sprite works a little differently in that you simply provide the
amount you’d like to move the sprite in each direction:
monsterSprite.move(-5, 10);
In this example, the sprite is moved 5 pixels to the left and 10
pixels down. Negative offset values indicate left or up directions,
whereas positive values indicate right or down.
|
3. | Because every Sprite object has an image associated with it, a paint() method is provided that paints the sprite at its current position. All you have to do is pass the paint() method a Graphics object, like this:
This code assumes that you have a Graphics object named g, but you always have a Graphics object handy in game painting code.
|
You’ve now seen the essentials of sprite animation with the MIDP API. All you’re missing is a quick look at the GameCanvas class, which is uniquely suited to animation thanks to its double-buffered graphics support.