You are on page 1of 23

Shortly: Multithreading and

Animation
• Display has a mechanism for executing your
code in the user-interface thread. It has a
method, callSerially(), that accepts a
Runnable. When the user-interface thread is
ready, meaning when it has finished servicing
all repaint requests, it will execute the run()
method of the Runnable from the user-interface
thread.
Shortly: Multithreading and
Animation
public class AnimationCanvas You’d kick off the animation
extends Canvas by calling start(), which in
implements Runnable { turn would simply call run().
public start() {
run(); Inside run(), you update your
} state and call repaint() to
public void paint(Graphics g) { request the painting of a new
// Paint a frame of the animation. frame.
}
public void run() {
// Update our state. Then you use callSerially() to
// Now request a paint of the new frame. request that you get called
repaint(); again when the painting is
Display.callSerially(this); done.
}
}
J2ME: The Game API
Overview
• The Game API builds on the Canvas and
Graphics classes
• The entire API is composed of five classes in
the javax.microedition.lcdui.game package:
• GameCanvas, that provides methods for
animation and key polling
• 4 classes, that deal with layers, which can
be used to compose scenes from several
different elements
Polling for Key States
• public int getKeyStates() - returns current state of the keys.
Example
Graphics g = getGraphics();
while(true) {
// Check for user input.
int ks = getKeyStates();
if ((ks & UP_PRESSED) != 0)
moveUp();
else if ((ks & DOWN_PRESSED) != 0)
moveDown();
// ...
// Update game state.
// Draw stuff using g.
flushGraphics();
}
Understanding Layers
• Layers are graphic elements that can be combined to
create a complete scene.
• Class javax.microedition.lcdui.game.Layer
• It has a location, a size, and can be visible or invisible:
• public final int getX()
• public final int getY()
• public final int getWidth()
• public final int getHeight()
• public void setPosition(int x, int y)
• Layer can be moved relatively to current position:
• public void move(int dx, int dy)
• The layer’s visibility is accessed using getVisible() and
setVisible()
Managing Layers
• Game API includes LayerManager, a class
that handles most of the details for you
• Layers have an index, which indicates their
position front to back. A position of 0 is on top,
closest to the user
• Layers may be added to the bottom of the list
using this method:
• public void append(Layer l)
• You can add a layer at a specific location using:
• public void insert(Layer l, int index)
• getSize(), getLayerAt(), remove()
View window
• LayerManager includes the concept of a view
window, which is the rectangular portion of the
scene that will be drawn
• The assumption is that the overall scene is
larger than the screen of the device, so only a
portion will be drawn at any time.
• You can set the view window:
public void setViewWindow(int x, int y, int width, int height)
• To draw the scene:
public void paint(Graphics g, int x, int y)
TiledLayer

• A tiled layer is made from a palette of tiles,


which come from a single image that is divided
into equal-sized pieces.

• A TiledLayer is drawn on a Graphics object


using the paint() method inherited from Layer
Creating and Initializing a TiledLayer
• public TiledLayer(int columns, int rows,Image image, int
tileWidth, int tileHeight)
- columns, rows, source image and tile dimensions.
• To retrieve the tile dimensions, use getCellWidth() and
getCellHeight().
• The number of columns and rows in a TiledLayer can be
retrieved with getColumns() and getRows().
• To assign a tile to a cell, use this method:
• public void setCell(int col, int row, int tileIndex)
• To assign the same tile to a range of cells:
• public void fillCells(int col, int row, int numCols, int
numRows, int tileIndex)
Image backgroundImage =
Image.createImage("/background_tiles.png");
TiledLayer background = new TiledLayer(8,
4, backgroundImage, 48, 48);
background.setPosition(12, 0);
int[] map = {
1, 2, 0, 0, 0, 0, 0, 0,
3, 3, 2, 0, 0, 0, 5, 0,
3, 3, 3, 2, 4, 1, 3, 2,
6, 6, 6, 6, 6, 6, 6, 6
};
for (int i = 0; i < map.length; i++) {
int column = i % 8;
int row = (i - column) / 8;
background.setCell(column, row, map[i]);
}
Sprite
• While a TiledLayer uses a palette of tiles to fill a
large area, a Sprite uses a palette of tiles to
animate a layer that is the same size as a
single tile
• As a TiledLayer, a Sprite is created from a
source image that is divided into equally sized
frames:
public Sprite(Image image, int frameWidth, int frameHeight)
• The total number of frames contained in the
Sprite is returned from getRawFrameCount().
• Like any other Layer, Sprites are rendered
when the paint() is called
Animating Sprites
• Sprite animation is all about frame sequences.

• This image is 192×48 pixels. If it is created with


a 48×48-pixel frame size, there are four frames.
The default frame sequence is { 0, 1, 2, 3 }
• The following method changes the current
frame sequence: public void setFrameSequence(int[]
sequence)
• The code below shows how you could create a
new Sprite and set its frame sequence:
int[] runningSequence = { 0, 1, 2 };
Image quatschImage =
Image.createImage("/quatsch.png");
Sprite quatsch = new Sprite(quatschImage, 48,
48);
quatsch.setFrameSequence(runningSequence);

• To move forward and backward in the


sequence, use nextFrame() and prevFrame()
• To jump directly to a particular frame use:
• public void setFrame(int sequenceIndex)
• If the Sprite’s frame sequence is { 2, 3, 1, 9 }, then calling
setFrame(1) would result in the Sprite’s current frame being
set to 3.
Transforming Sprites
• The following method applies a transformation
to a Sprite: public void setTransform(int transform)
Sprite constants: TRANS_NONE, TRANS_ROT90, TRANS_ROT180,
TRANS_ROT270, TRANS_MIRROR, TRANS_MIRROR_ROT90,
TRANS_MIRROR_ROT180, TRANS_MIRROR_ROT270

• To adjust the location of the reference point:


• public void defineReferencePixel(int x, int y)
Handling Collisions
The Game API supports 2 techniques for collision
detection:
1. The implementation can compare rectangles representing a
sprite and another sprite. A collision has occurred if the
rectangles intersect. This is a quick way to test for collisions,
but it may produce inaccurate results for nonrectangular
shapes.
2. The implementation can compare each pixel of the sprite and
another sprite. If an opaque pixel in the sprite overlaps an
opaque pixel in the other sprite, a collision has occurred. This
technique involves more computation but produces a more
accurate result.
• A Sprite has a collision rectangle that is used
for collision detection.
• By default at (0,0) and width and height as a
Sprite's. To change it:
• public void defineCollisionRectangle(int x, int y, int
width, int height);
• Sprite is capable of detecting collisions with
other Sprites, TiledLayers, and Images:
• public final boolean collidesWith(Sprite s, boolean
pixelLevel)
• public final boolean collidesWith(TiledLayer t, boolean
pixelLevel)
• public final boolean collidesWith(Image image, int x,
int y, boolean pixelLevel)
Putting it all together
Let's see as an example small game, which is:
– Using an animation loop in GameCanvas
– Polling for key state using GameCanvas
– Using a LayerManager to maintain multiple layers
– Creating a Sprite and TiledLayers
– Animating a Sprite, including changing frame
sequences and transformations
– Using an animated tile in a TiledLayer

You might also like