Thứ Bảy, 1 tháng 9, 2012

Làm việc với lớp TiledLayer

Có chuyện gì xảy ra với blog của Devlin không biết, sáng nay tôi vào không được. Hi vọng blog không sao chứ nếu có sao thì tiếc lắm... Nếu còn vào được tôi sẽ copy hết sang rồi dịch sau. Đúng là dại quá. Sau đây là bài học về TiledLayer, tôi chôm được trên mạng.

P/S: À mà thôi, đã vào lại được Devlinsblog, tôi tranh thủ copy bài luôn. Bài này cứ để Tiếng Anh, khi nào xong của Devlin tôi sẽ dịch sau.

A common technique used in video games is to have a large scrolling background formed by a grid of smaller, reusable images. In MIDP 1.0, such a feature would need to be implemented from scratch or with a third-party library. However, MIDP 2.0 provides the convenient TiledLayer class to address this specific need. In this article we will look at a simple example of how to use the TiledLayer class in a Java ME application.
TiledLayer is part of the javax.microedition.lcdui.game package that was introduced with MIDP 2.0. The constructor for the TiledLayer class accepts five parameters: the number of columns and rows in the grid, an Image object containing the tiles, and the width and height in pixels of each tile.
TiledLayer(int columns, int rows, Image image, int tileWidth, int tileHeight)
The image can have multiple rows of tiles. The tileWidth and tileHeight properties are used to break up the image into individual tiles. The tiles are numbered starting with 1 and incrementing in row-major order. Index 0 is reserved for transparency; TiledLayer won’t draw anything for cells with index 0.



For our example application, we’ll start by creating a class that extends GameCanvas. Extending GameCanvas will allow us to take advantage of the off-screen buffer that this class provides.
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
public class DemoCanvas extends GameCanvas {
Next, we’ll add an instance variable for our TiledLayer object, along with the customary setter and getter methods.
public TiledLayer getTiledLayer() {
    return tiledLayer;
}
public void setTiledLayer(TiledLayer tiledLayer) {
    this.tiledLayer = tiledLayer;
}
// Instance variables
private TiledLayer tiledLayer;  // The TiledLayer object
We will also add some constants to store the size of our tiles in pixels, and the background color. Remember that the 0 index tiles are transparent, so we will be responsible for clearing the background ourselves.
// Constants
public static final int TILE_WIDTH = 16;
public static final int TILE_HEIGHT = 16;
public static final int BACKGROUND_COLOR = 0x00000000;
We will need to provide some data for the grid. For the purposes of this example, we can hard code some data into a two-dimensional array. In a real-life application, you would probably read this data from a resource, generated from some sort of map editor software.
public byte[][] getGridData() {      // Create and populate a two-dimensional
    // array with sample grid data
    byte gridData[][] = {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,1,2,2,2,3,0,0,0,0,10,10,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,10,10,0,0,0,1,2,2,3,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,1,2,2,3,0,0,0,0,0,4,5,6,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0},
        {7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9}
    };      
    return gridData;
}
Now, we will add a method to create and initialize our TiledLayer object. To initialize the cells, we will simply iterate through the byte array we created above and call the TiledLayer.setCell() for each value.
public void initTiledLayer() {

    // Get the grid data
    byte[][] gridData = getGridData();

    // Derive number of columns and rows
    // from grid data array sizes
    int rows = gridData.length;
    int columns = gridData[0].length;

    // Get the tile Image object
    Image tileImage;
    try {
        tileImage =
        Image.createImage("/tiledlayer-tiles.png");
    } catch(java.io.IOException e) {
        System.err.println("Unable to create image");
        e.printStackTrace();
        return;
    }

    // Create a TiledLayer object
    TiledLayer tiledLayer = new TiledLayer(
    columns, rows, tileImage,
    TILE_WIDTH, TILE_HEIGHT);

    // Transfer grid data to TiledLayer object
    for(int y = 0;  y < rows; y++) {
        for(int x = 0; x < columns; x++) {
            int tileIndex = gridData[y][x];
            if(tileIndex > 0)
                tiledLayer.setCell(x, y, tileIndex);
        }
    }

    // Set the TiledLayer instance variable
    setTiledLayer(tiledLayer);

}
Next, we need a method to draw the background. In addition to rendering the TiledLayer object, it will also need to clear the background. Note that since GameCanvas provides an off-screen buffer, there is no need to override the paint() method; GameCanvas already provides an adequate implementation.
public void drawBackground() {
    
    // Get off-screen buffer
    Graphics g = getGraphics();

    // Clear the background
    g.setColor(BACKGROUND_COLOR);
    g.fillRect(0, 0, getWidth(), getHeight());

    // Paint the tiled layer
    TiledLayer tiledLayer = getTiledLayer();
    if(tiledLayer != null) {
        tiledLayer.paint(g);
    }

}
Now we’ll add a custom constructor to call our initialization method, and to perform the initial drawing of the off-screen buffer. Since we’ll be overriding the keyPressed() method in the next step, we will pass false for the suppressKeyEvents parameter of the GameCanvas constructor.
protected DemoCanvas() {

    // We will be overriding keyPressed,
    // so do not suppress key events
    super(false);

    // Initialize the TiledLayer
    initTiledLayer();

    // Draw to the off-screen buffer
    drawBackground();

}
Lastly, we’ll implement an extremely simple keyPressed() method that will scroll the background when the direction keys are pressed using the convenient TiledLayer.move() method. It will also force a repaint of the off-screen buffer and flush it to the display.
protected void keyPressed(int keyCode) {

    // Handle scrolling in response to key presses
    TiledLayer tiledLayer = getTiledLayer();
    if(tiledLayer != null) {

        // Get game action from key code
        int gameAction = getGameAction(keyCode);

        switch(gameAction) {
            case UP:
                tiledLayer.move(0, -16);
                break;
            case DOWN:
                tiledLayer.move(0, 16);
                break;
            case LEFT:
                tiledLayer.move(-16, 0);
                break;
            case RIGHT:
                tiledLayer.move(16, 0);
                break;
        }

        // Repaint the off-screen buffer
        paintBackground(getGraphics());

        // Flush the off-screen buffer to the display
        flushGraphics();

    }

}

TiledLayer Example

1 nhận xét: