Author: Quan To
Student ID: s3298400
Sprite does help a lot in J2ME animation control. However, sometimes, you’ll need a more complex animation than just one animated sprite. For example, in the game Bomberman, an explosion will need several animated parts: the chop, the body, the center, and except from the center, the other parts need images for up, down, left, right. Moreover, when the bomb explodes, each part needs to display the strength of the explosion changing over time, and all of the animation have to be consistent with each other.
Sprite does not seem to be a good solution for this problem. Firstly, one explosion may come up with a dozen animated parts, which means the layer manager have to manage that number of layers. And when multiple explosions happen at the same time, it is easy to lead to Stack Overflow or Out Of Memory Exception.
Fortunately, the regularly used for managing multiple tiles, TiledLayer, is capable of being animated. However, it is not simple setting the tile into another image. This article is going to demonstrate how to properly use n animated TiledLayer.
Let’s take the explosion in Bomberman as an example:
Let’s say I have this image as the resource:
The resource is divided into 4 parts representing for 4 phases of the explosion, let’s say: initializing, increasing, exploding, destroying. Each phase has 9 images. We’ll have some “private static final” variable for these images. Using private static final variable will save a little bit more memory than using private final variable.
Code:
private static final int MAP_WIDTH = 19;
private static final int MAP_HEIGHT = 19;
private static final int FRAME_WIDTH = 24;
private static final int FRAME_HEIGHT = 24;
private static final int EXPLODE_CENTER = 1;
private static final int[] EXPLODE_LEFT = {2, 3};
private static final int[] EXPLODE_RIGHT = {4, 5};
private static final int[] EXPLODE_UP = {6, 7};
private static final int[] EXPLODE_DOWN = {8, 9};
Additionally, we’ll need one more constant indicating the index of the start of each phase, and one more indicating the interval of the animation.
Code:
private static final int INTERVAL = 60;
private static final int[] PHASE_INDEXES = {0, 9, 18, 27};
Using an array to keep track of the position and function of each tile. The TiledLayer also needs to keep track of the animated indexes (the indexes of the animated tiles) and values of the tiles at those indexes (this value indicates the role of the tile, for example: center, left, right…). One more variable indicate the spread of the explosion
private int explosionMap[];
private int animatedIndexes[];
private int tileRoles[];
private LayerManager lM;
private int spread;
Now we need to override the TiledLayer class. The class needs to implement Runnable interface to control its animation. I’m going to demonstrate a very simple explosion tile.
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.LayerManager;
import javax.microedition.lcdui.game.TiledLayer;
public class ExplosionTiledLayer extends TiledLayer implements Runnable {
private static final int INTERVAL = 60;
private static final int MAP_WIDTH = 19;
private static final int MAP_HEIGHT = 19;
private static final int FRAME_WIDTH = 24;
private static final int FRAME_HEIGHT = 24;
private static final int[] EXPLODE_LEFT = {2, 3};
private static final int[] EXPLODE_RIGHT = {4, 5};
private static final int EXPLODE_CENTER = 1;
private static final int[] EXPLODE_UP = {6, 7};
private static final int[] EXPLODE_DOWN = {8, 9};
private static final int[] PHASE_INDEXES = {10, 19, 28, 37};
private int explosionMap[];
private int animatedIndexes[];
private int tileRoles[];
private int spread;
private LayerManager lM;
public ExplosionTiledLayer(Image image, LayerManager lM, int mapX, int mapY, int spread) {
super(MAP_WIDTH, MAP_HEIGHT, image, FRAME_WIDTH, FRAME_HEIGHT);
this.lM = lM;
this.spread = spread;
initExplosionMap(mapX, mapY);
}
private void initExplosionMap(int x, int y) {
//Need to keep track of the number of animated tiles
int count = 0;
//Initialize map
explosionMap = new int[MAP_WIDTH * MAP_HEIGHT];
//Init with 0's
for (int i = 0; i 0 && go > 1; i--) {
explosionMap[MAP_WIDTH * i + x] = EXPLODE_UP[0];
go--;
count++;
}
explosionMap[MAP_WIDTH * i + x] = EXPLODE_UP[1];
count++;
//From center down
go = spread;
for (int i = y; i 1; i++) {
explosionMap[MAP_WIDTH * i + x] = EXPLODE_DOWN[0];
go--;
count++;
}
explosionMap[MAP_WIDTH * i + x] = EXPLODE_DOWN[1];
count++;
//From center to the left
go = spread;
for (int i = x; i > 0 && go > 1; i--) {
explosionMap[MAP_WIDTH * y + i] = EXPLODE_LEFT[0];
go--;
count++;
}
explosionMap[MAP_WIDTH * y + i] = EXPLODE_LEFT[1];
count++;
//From center to the right
go = spread;
for (int i = x; i 1; i++) {
explosionMap[MAP_WIDTH * y + i] = EXPLODE_RIGHT[0];
count++;
go--;
}
explosionMap[MAP_WIDTH * y + i] = EXPLODE_LEFT[1];
count++;
//Center
explosionMap[MAP_WIDTH * y + x] = EXPLODE_CENTER;
count++;
//There are 4 duplicates as all directions start from center
count -= 4;
/*Set cells*/
//The number of animated tiles has been counted
animatedIndexes = new int[count];
tileRoles = new int[count];
count = 0;
for (int i = 0; i < explosionMap.length; i++) {
int col = i % MAP_WIDTH;
int row = (i - col) / MAP_HEIGHT;
if (explosionMap[i] != 0) {
int animatedIndex;
if (explosionMap[i] != 0) {
//Initialize with PHASE_INDEXES[0]: Phase 0
animatedIndex = createAnimatedTile(explosionMap[i] + PHASE_INDEXES[0]);
setCell(col, row, animatedIndex);
//Have to save the animated indexes and the role of that tile
animatedIndexes[count] = animatedIndex;
tileRoles[count] = explosionMap[i];
count++;
}
}
}
}
//Override run() method of Runnable interface
public void run() {
//Loop through every phases
for (int j = 0; j < PHASE_INDEXES.length; j++) {
for (int i = 0; i < animatedIndexes.length; i++) {
setAnimatedTile(animatedIndexes[i], tileRoles[i] + PHASE_INDEXES[j]);
}
//Sleep a while
Utilities.sleepThread(INTERVAL);
}
//Rollback
for (int j = PHASE_INDEXES.length - 2; j >= 0; j--) {
for (int i = 0; i < animatedIndexes.length; i++) {
setAnimatedTile(animatedIndexes[i], tileRoles[i] + PHASE_INDEXES[j]);
}
//Sleep a while
Utilities.sleepThread(INTERVAL);
}
lM.remove(this);
}
}
How to use ExplosionTiledLayer:
Precondition: Availability of an instance of LayerManager, position of the explosion on the map, the image for the tiles and the spread of the bomb (How far will the bomb reach.)
Example Code:
ExplosionTiledLayer explosion = new ExplosionTiledLayer(image, layermanager, 4, 5, 3);
layerManager.append(explosion);
/*
Or:
layerManager.insert(explosion, position);
with position is an index between 0 and layerManager.getSize()
*/
Thread thread = new Thread(explosion);
//Let the animation begins
thread.start();