TiledLayerpublic class TiledLayer extends Layer A TiledLayer is a visual element composed of a grid of cells that
can be filled with a set of
tile images. This class allows large virtual layers to be created
without the need for an
extremely large Image. This technique is commonly used in 2D
gaming platforms to create
very large scrolling backgrounds,
Tiles
The tiles used to fill the TiledLayer's cells are provided in a
single Image object which
may be mutable or immutable. The Image is broken up into a series
of equally-sized tiles;
the tile size is specified along with the Image. As shown in the
figure below, the same
tile set can be stored in several different arrangements depending
on what is the most
convenient for the game developer.
Each tile is assigned a unique index number. The tile located in
the upper-left corner
of the Image is assigned an index of 1. The remaining tiles are
then numbered consecutively
in row-major order (indices are assigned across the first row, then
the second row, and so on).
These tiles are regarded as static tiles because there is
a fixed link between
the tile and the image data associated with it.
A static tile set is created when the TiledLayer is instantiated;
it can also be updated
at any time using the {@link #setStaticTileSet} method.
In addition to the static tile set, the developer can also define
several animated tiles.
An animated tile is a virtual tile that is dynamically associated
with a static tile; the appearance
of an animated tile will be that of the static tile that it is
currently associated with.
Animated tiles allow the developer to change the appearance of a
group of cells
very easily. With the group of cells all filled with the animated
tile, the appearance
of the entire group can be changed by simply changing the static
tile associated with the
animated tile. This technique is very useful for animating large
repeating areas without
having to explicitly change the contents of numerous cells.
Animated tiles are created using the {@link #createAnimatedTile}
method, which returns the
index to be used for the new animated tile. The animated tile
indices are always negative
and consecutive, beginning with -1. Once created, the static tile
associated with an
animated tile can be changed using the {@link #setAnimatedTile}
method.
Cells
The TiledLayer's grid is made up of equally sized cells; the number
of rows and
columns in the grid are specified in the constructor, and the
physical size of the cells
is defined by the size of the tiles.
The contents of each cell is specified by means of a tile index; a
positive tile index refers
to a static tile, and a negative tile index refers to an animated
tile. A tile index of 0
indicates that the cell is empty; an empty cell is fully
transparent and nothing is drawn
in that area by the TiledLayer. By default, all cells contain tile
index 0.
The contents of cells may be changed using {@link #setCell} and
{@link #fillCells}. Several
cells may contain the same tile; however, a single cell cannot
contain more than one tile.
The following example illustrates how a simple background can be
created using a TiledLayer.
In this example, the area of water is filled with an animated tile
having an index of -1, which
is initially associated with static tile 5. The entire area of
water may be animated by simply
changing the associated static tile using setAnimatedTile(-1,
7) .
Rendering a TiledLayer
A TiledLayer can be rendered by manually calling its paint method;
it can also be rendered
automatically using a LayerManager object.
The paint method will attempt to render the entire TiledLayer
subject to the
clip region of the Graphics object; the upper left corner of the
TiledLayer is rendered at
its current (x,y) position relative to the Graphics object's
origin. The rendered region
may be controlled by setting the clip region of the Graphics object
accordingly.
|
Fields Summary |
---|
private int | cellHeightthe overall height of the TiledLayer grid | private int | cellWidththe overall cell width of the TiledLayer grid | private int | rowsThe num of rows of the TiledLayer grid. | private int | columnsthe num of columns in the TiledLayer grid | private int[] | cellMatrixint array for storing row and column of cell
it contains the tile Index for both static and animated tiles | javax.microedition.lcdui.Image | sourceImageSource image for tiles | private int | numberOfTilesno. of tiles | int[] | tileSetXX co-ordinate definitions for individual frames into the source image | int[] | tileSetYY co-ordinate definitions for individual frames into the source image | private int[] | anim_to_staticTable to map from animated Index to static Index
0th location is unused.
anim --> static Index
-1 --> 21
-2 --> 34
-3 --> 45
for now keep 0 the location of the table empty instead of computing
-index make index +ve and access this Table. | private int | numOfAnimTilestotal number of animated tiles. This variable is also used as
index in the above table to add new entries to the anim_to_static table.
initialized to 1 when table is created. |
Constructors Summary |
---|
public TiledLayer(int columns, int rows, javax.microedition.lcdui.Image image, int tileWidth, int tileHeight)Creates a new TiledLayer.
The TiledLayer's grid will be rows cells high and
columns cells wide. All cells in the grid are initially
empty (i.e. they contain tile index 0). The contents of the grid may
be modified through the use of {@link #setCell} and {@link #fillCells}.
The static tile set for the TiledLayer is created from the specified
Image with each tile having the dimensions of tileWidth x tileHeight.
The width of the source image must be an integer multiple of
the tile width, and the height of the source image must be an integer
multiple of the tile height; otherwise, an IllegalArgumentException
is thrown;
The entire static tile set can be changed using
{@link #setStaticTileSet(Image, int, int)}.
These methods should be used sparingly since they are both
memory and time consuming.
Where possible, animated tiles should be used instead to
animate tile appearance.
// IllegalArgumentException will be thrown
// in the Layer super-class constructor
super(columns < 1 || tileWidth < 1 ? -1 : columns * tileWidth,
rows < 1 || tileHeight < 1 ? -1 : rows * tileHeight);
// if img is null img.getWidth() will throw NullPointerException
if (((image.getWidth() % tileWidth) != 0) ||
((image.getHeight() % tileHeight) != 0)) {
throw new IllegalArgumentException();
}
this.columns = columns;
this.rows = rows;
cellMatrix = new int[rows][columns];
int noOfFrames =
(image.getWidth() / tileWidth) * (image.getHeight() / tileHeight);
// the zero th index is left empty for transparent tile
// so it is passed in createStaticSet as noOfFrames + 1
// Also maintain static indices is true
// all elements of cellMatrix[][]
// are set to zero by new, so maintainIndices = true
createStaticSet(image, noOfFrames + 1, tileWidth, tileHeight, true);
|
Methods Summary |
---|
public int | createAnimatedTile(int staticTileIndex)Creates a new animated tile and returns the index that refers
to the new animated tile. It is initially associated with
the specified tile index (either a static tile or 0).
The indices for animated tiles are always negative. The first
animated tile shall have the index -1, the second, -2, etc.
// checks static tile
if (staticTileIndex < 0 || staticTileIndex >= numberOfTiles) {
throw new IndexOutOfBoundsException();
}
if (anim_to_static == null) {
anim_to_static = new int[4];
numOfAnimTiles = 1;
} else if (numOfAnimTiles == anim_to_static.length) {
// grow anim_to_static table if needed
int new_anim_tbl[] = new int[anim_to_static.length * 2];
System.arraycopy(anim_to_static, 0,
new_anim_tbl, 0, anim_to_static.length);
anim_to_static = new_anim_tbl;
}
anim_to_static[numOfAnimTiles] = staticTileIndex;
numOfAnimTiles++;
return (-(numOfAnimTiles - 1));
| private void | createStaticSet(javax.microedition.lcdui.Image image, int noOfFrames, int tileWidth, int tileHeight, boolean maintainIndices)create the Image Array.
cellWidth = tileWidth;
cellHeight = tileHeight;
int imageW = image.getWidth();
int imageH = image.getHeight();
sourceImage = image;
numberOfTiles = noOfFrames;
tileSetX = new int[numberOfTiles];
tileSetY = new int[numberOfTiles];
if (!maintainIndices) {
// populate cell matrix, all the indices are 0 to begin with
for (rows = 0; rows < cellMatrix.length; rows++) {
int totalCols = cellMatrix[rows].length;
for (columns = 0; columns < totalCols; columns++) {
cellMatrix[rows][columns] = 0;
}
}
// delete animated tiles
anim_to_static = null;
}
int currentTile = 1;
for (int locY = 0; locY < imageH; locY += tileHeight) {
for (int locX = 0; locX < imageW; locX += tileWidth) {
tileSetX[currentTile] = locX;
tileSetY[currentTile] = locY;
currentTile++;
}
}
| public void | fillCells(int col, int row, int numCols, int numRows, int tileIndex)Fills a region cells with the specific tile. The cells may be filled
with a static tile index, an animated tile index, or they may be left
empty (index 0 ).
if (numCols < 0 || numRows < 0) {
throw new IllegalArgumentException();
}
if (col < 0 || col >= this.columns || row < 0 || row >= this.rows ||
col + numCols > this.columns || row + numRows > this.rows) {
throw new IndexOutOfBoundsException();
}
if (tileIndex > 0) {
// do checks for static tile
if (tileIndex >= numberOfTiles) {
throw new IndexOutOfBoundsException();
}
} else if (tileIndex < 0) {
// do animated tile index check
if (anim_to_static == null ||
(-tileIndex) >= numOfAnimTiles) {
throw new IndexOutOfBoundsException();
}
}
for (int rowCount = row; rowCount < row + numRows; rowCount++) {
for (int columnCount = col;
columnCount < col + numCols; columnCount++) {
cellMatrix[rowCount][columnCount] = tileIndex;
}
}
| public int | getAnimatedTile(int animatedTileIndex)Gets the tile referenced by an animated tile.
Returns the tile index currently associated with the
animated tile.
animatedTileIndex = - animatedTileIndex;
if (anim_to_static == null || animatedTileIndex <= 0
|| animatedTileIndex >= numOfAnimTiles) {
throw new IndexOutOfBoundsException();
}
return anim_to_static[animatedTileIndex];
| public int | getCell(int col, int row)Gets the contents of a cell.
Gets the index of the static or animated tile currently displayed in
a cell. The returned index will be 0 if the cell is empty.
if (col < 0 || col >= this.columns || row < 0 || row >= this.rows) {
throw new IndexOutOfBoundsException();
}
return cellMatrix[row][col];
| public final int | getCellHeight()Gets the height of a single cell, in pixels.
return cellHeight;
| public final int | getCellWidth()Gets the width of a single cell, in pixels.
return cellWidth;
| public final int | getColumns()Gets the number of columns in the TiledLayer grid.
The overall width of the TiledLayer, in pixels,
may be obtained by calling {@link #getWidth}.
return columns;
| public final int | getRows()Gets the number of rows in the TiledLayer grid. The overall
height of the TiledLayer, in pixels, may be obtained by
calling {@link #getHeight}.
return rows;
| public final void | paint(javax.microedition.lcdui.Graphics g)Draws the TiledLayer.
The entire TiledLayer is rendered subject to the clip region of
the Graphics object.
The TiledLayer's upper left corner is rendered at the
TiledLayer's current
position relative to the origin of the Graphics object. The current
position of the TiledLayer's upper-left corner can be retrieved by
calling {@link #getX()} and {@link #getY()}.
The appropriate use of a clip region and/or translation allows
an arbitrary region
of the TiledLayer to be rendered.
If the TiledLayer's Image is mutable, the TiledLayer is rendered
using the current contents of the Image.
if (g == null) {
throw new NullPointerException();
}
if (visible) {
int startColumn = 0;
int endColumn = this.columns;
int startRow = 0;
int endRow = this.rows;
// calculate the number of columns left of the clip
int number = (g.getClipX() - this.x) / cellWidth;
if (number > 0) {
startColumn = number;
}
// calculate the number of columns right of the clip
int endX = this.x + (this.columns * cellWidth);
int endClipX = g.getClipX() + g.getClipWidth();
number = (endX - endClipX) / cellWidth;
if (number > 0) {
endColumn -= number;
}
// calculate the number of rows above the clip
number = (g.getClipY() - this.y) / cellHeight;
if (number > 0) {
startRow = number;
}
// calculate the number of rows below the clip
int endY = this.y + (this.rows * cellHeight);
int endClipY = g.getClipY() + g.getClipHeight();
number = (endY - endClipY) / cellHeight;
if (number > 0) {
endRow -= number;
}
// paint all visible cells
int tileIndex = 0;
// y-coordinate
int ty = this.y + (startRow * cellHeight);
for (int row = startRow;
row < endRow; row++, ty += cellHeight) {
// reset the x-coordinate at the beginning of every row
// x-coordinate to draw tile into
int tx = this.x + (startColumn * cellWidth);
for (int column = startColumn; column < endColumn;
column++, tx += cellWidth) {
tileIndex = cellMatrix[row][column];
// check the indices
// if animated get the corresponding
// static index from anim_to_static table
if (tileIndex == 0) { // transparent tile
continue;
} else if (tileIndex < 0) {
tileIndex = getAnimatedTile(tileIndex);
}
g.drawRegion(sourceImage,
tileSetX[tileIndex],
tileSetY[tileIndex],
cellWidth, cellHeight,
Sprite.TRANS_NONE,
tx, ty,
Graphics.TOP | Graphics.LEFT);
}
}
}
| public void | setAnimatedTile(int animatedTileIndex, int staticTileIndex)Associates an animated tile with the specified static tile.
// checks static tile
if (staticTileIndex < 0 || staticTileIndex >= numberOfTiles) {
throw new IndexOutOfBoundsException();
}
// do animated tile index check
animatedTileIndex = - animatedTileIndex;
if (anim_to_static == null || animatedTileIndex <= 0
|| animatedTileIndex >= numOfAnimTiles) {
throw new IndexOutOfBoundsException();
}
anim_to_static[animatedTileIndex] = staticTileIndex;
| public void | setCell(int col, int row, int tileIndex)Sets the contents of a cell.
The contents may be set to a static tile index, an animated
tile index, or it may be left empty (index 0)
if (col < 0 || col >= this.columns || row < 0 || row >= this.rows) {
throw new IndexOutOfBoundsException();
}
if (tileIndex > 0) {
// do checks for static tile
if (tileIndex >= numberOfTiles) {
throw new IndexOutOfBoundsException();
}
} else if (tileIndex < 0) {
// do animated tile index check
if (anim_to_static == null ||
(-tileIndex) >= numOfAnimTiles) {
throw new IndexOutOfBoundsException();
}
}
cellMatrix[row][col] = tileIndex;
| public void | setStaticTileSet(javax.microedition.lcdui.Image image, int tileWidth, int tileHeight)Change the static tile set.
Replaces the current static tile set with a new static tile set.
See the constructor {@link #TiledLayer(int, int, Image, int, int)}
for information on how the tiles are created from the
image.
If the new static tile set has as many or more tiles than the
previous static tile set,
the the animated tiles and cell contents will be preserve. If
not, the contents of
the grid will be cleared (all cells will contain index 0) and
all animated tiles
will be deleted.
// if img is null img.getWidth() will throw NullPointerException
if (tileWidth < 1 || tileHeight < 1 ||
((image.getWidth() % tileWidth) != 0) ||
((image.getHeight() % tileHeight) != 0)) {
throw new IllegalArgumentException();
}
setWidthImpl(columns * tileWidth);
setHeightImpl(rows * tileHeight);
int noOfFrames =
(image.getWidth() / tileWidth) * (image.getHeight() / tileHeight);
// the zero th index is left empty for transparent tile
// so it is passed in createStaticSet as noOfFrames + 1
if (noOfFrames >= (numberOfTiles - 1)) {
// maintain static indices
createStaticSet(image, noOfFrames + 1, tileWidth, tileHeight, true);
} else {
createStaticSet(image, noOfFrames + 1, tileWidth,
tileHeight, false);
}
|
|