3.Tạo một Sprite chuyển động

Cấu trúc game và background đã được tạo. Bây giờ chúng ta phải chuyển động các nhân vật. Sự chuyển động được thực hiện bằng cách sử dụng lớp Sprite. Khái niệm Sprite cũng gần giống với TileLayer. Sprite sử dụng một chuỗi các khung(frame) hình ảnh nguồn cho chuyển động. Sprite đòi hỏi một hình ảnh nguồn và tạo ra một hoặc nhiều frame. Giống như tile, frame cũng nên có kích thước bằng nhau. Xem hình 4 dưới đây:

Sử dụng hình ảnh nhân vật như là hình ảnh nguồn, chúng ta có thể tạo nhân vật đi bộ bằng Sprite. Trong hình ảnh nguồn, mỗi frame có một số, bắt đầu từ 0 và đếm lên. Sprite có một chuỗi frame xác định thứ tự để hiển thị các frame. Chuỗi frame mặc định cho một Sprite mới chỉ bắt đầu từ 0 và đếm lên qua các frame có sẵn. Xem hình 5:

Sprite characterSprite = new Sprite(characterImg, characterWidth, characterHeight);

Trong đó, chacracterWidth và characterHeight là giống nhau trong mỗi frame riêng biệt.

Chúng ta sẽ tạo một phương thức createCharacterSprite() để tạo ra đối tượng characterSprite.

...
public void createCharacterSprite() {
 final int characterWidth = 28;
final int characterHeight = 54;
 characterSprite = new Sprite(charImg, characterWidth, characterHeight);
characterSprite.setPosition(charX, chary);
}
...

Các frame này di chuyển theo đường tròn hay đường dẫn trình tự, ví dụ frame 0 tiếp đến là 1, 2, 3 và quay trở lại 0.

Để di chuyển sang frame tiếp theo, sử dụng phương thức nextFrame(). Ngược lại, để di chuyển trở lại frame trước đó, sử dụng phương thức prevFrame().

Để xác định một chuỗi frame khác với mặc định, bạn sử dụng setFrameSequence(int[] sequence) để thiết lập chuỗi frame theo mong muốn của bạn.

Bạn có thể jump đến một điểm nào đó trong chuỗi frame hiện tại bằng cách sử dụng phương thức setFrame().

Để thay đổi vị trí Sprite trên màn hình, sử dụng phương thức setPosition(int x,int y). Để render đối tượng Sprite sử dụng phương thức paint(Graphics g).

Chuyển đổi sử dụng Sprite

Trong lớp Sprite, ta có thể chuyển đổi các frame nguồn. Điều này rất cần thiết, như nhân vật sẽ đi bộ bên phải hay bên trái tùy theo yêu cầu.

Xem hình 4 ở trên, ta thấy nhân vật chuyển động theo hướng tay phải. Để làm cho nhân vật đi bộ theo hướng tay trái, ta chỉ cần đảo ngược các frame các frame. Để thực hiện công việc đó, ta sử dụng phương thức setTransform().

Để cuộn các frame sử dụng tùy chọn phản chiếu(mirror) hay tùy chọn 90 độ hay sử dụng cả hai theo yêu cầu. Hằng số trong lớp Sprite liệt kê các khả năng có thể. Để cuộc Sprite theo hướng trái:

characterSprite.setTransform(Sprite.TRANS_MIRROR);

Trong khi chuyển đổi, pixel tham chiếu của Sprite không được di chuyển. Sprite cũng có khái niệm về điểm ảnh(pixel) tham chiếu.

Pixel tham chiếu được định nghĩa bằng cách xác định vị trí của nó trong các frame không chuyển đổi của Sprite, sử dụng defineReferencePixel(int x, int y).

Pixel tham chiếu của một Sprite nằm ở vị trí (0,0) trong không gian tọa độ của Sprite, ngay gốc trên bên trái theo mặc định. Vị trí của pixel tham chiếu cũng được chuyển đổi khi sự chuyển đổi được áp dụng. Vị trí của Sprite được điều chỉnh để pixel tham chiếu vẫn ở cùng một chỗ.

Kiểm tra va chạm sử dụng Sprite

Bây giờ chúng ta đã gần như thực hiện với các phần trò chơi, việc còn lại là cần phải kiểm tra va chạm một phần của trò chơi. Sprite lớp cung cấp hai phương thức va chạm:

  • Va chạm với Sprite

boolean collidesWith(Sprite s, boolean pixelLevel) kiểm tra va chạm với các spirte cho trước.

  • Va chạm với TiledLayer

boolean collidesWith(TiledLlayer s, boolean pixelLevel) kiểm tra va chạm với đối tượng TiledLayer. Phương thức này trả về true nếu nó va chạm với một tile không trống (non-empty) trong TiledLayer.

Sử dụng LayerManager

Game ví dụ này đã thực hiện xong; đó là ta đã xác định cấu trúc game, định nghĩa TileLayers cho background, collisionLayer để phát hiện va chạm, Sprite cho các nhân vật chuyển động, sự chuyển đổi và phát hiện va chạm.

Vậy sử dụng LayerManager để làm gì?

Như bạn biết thì Sprite và TiledLayer là lớp con của Layer. Game có thể có nhiều TiledLayer và Sprite. Việc quản lý chúng rất phức tạp.

Với LayerManager, bạn có thể tổ chức và quản lý các layer. Lớp này cung cấp các phương thức  để bổ xung, xóa hay chèn các layer vào game, chẳng hạn như: append(Layer layer), remove(Layer layer), insert(Layer layer, int position). Ngoài ra, LayerManager còn duy trì một danh sách có thứ tự của việc bổ xung, xóa hay chèn các layer.

Code của nó được thay đổi như sau:

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.TiledLayer;
import javax.microedition.lcdui.game.LayerManager;
import javax.microedition.lcdui.game.Sprite;

import java.io.IOException;

public class MyGameCanvas extends GameCanvas implements Runnable {
    /*character, elements image*/
    private Image charImg,elementImg;
    /*character coordinates*/
    private int charX;
    private int charY;   
    /*character movement along x axis*/
    private final int dx = 1;   
    /*screen Boundary*/
    private final int screenWidth = 160;
    private final int screenHeight = 160;
    private final int tileSize = 16;   
    /*Define Sprite Frame*/
    private final int charWidth = 28;
    private final int charHeight = 54;   
    /*Define character walk Direction*/   
    private int charDirection, prevDirection;   
    /*define character state*/
    private int charState;
    private final int charWalk = 1;
    private final int charStop = 2; 
    private final int charCollide = 3; 
    private final int charJump = 4;  
    /*character jump sequence*/
    private int charJumpReg_Y[] = {8,12,14,16,18,16,14,12,8};
    private int charJumpReg_X[] = {5,6,7,8,8,8,7,6,5};
    private int charJumpCounter;
    /*screen refresh value*/
    private final int frameDelay = 30;   
    /*MyGameMidlet*/
    private MyGameMidlet midlet;
    /*MyGameThread */
    private Thread myGameThread;
    /*background TileLayer*/
    private TiledLayer backgroundLayer;
    /*collision TileLayer*/
    private TiledLayer collisionLayer;
    /*character Sprite*/
    private Sprite characterSprite;
    /*LayerManager */
    private LayerManager layerManager;

    public MyGameCanvas(MyGameMidlet midlet) { 
        super(true);
        this.midlet = midlet;
        loadCharImg();
        createBackgroundScreen();       
        createCharacterSprite();
        createLayerManager();
        initialiseNewGame();
        setFullScreenMode(true);       
        myGameThread = new Thread(this);
        myGameThread.start();
    }

    private void loadCharImg()
    {
        try {
            /* load character image from resource */
            charImg = Image.createImage("/charImg.png");          
            elementImg = Image.createImage("/elements.png");           
        } catch (IOException ioex) {
            /*System.out.println("loadCharImg "+ioex);*/
            ioex.printStackTrace();
        }
    }        

    private void createBackgroundScreen()
    {       
        final int noRows = 10;
        final int noCols = 10;
        int[] gridCells  = {          
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // sky
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // sky
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // sky
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // sky
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // sky
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // sky
            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // earth
            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // earth
            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // earth
            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // earth           
        };

        backgroundLayer =  new                     
                                   TiledLayer(noCols, noRows, elementImg,tileSize,tileSize);       
        /* set the background with the images */
        for (int i = 0; i < gridCells .length; i++) {
            int column = i % gridCellsLength;
            int row = (i - column)/gridCellsLength;           
            backgroundLayer.setCell(column, row, gridCells [i]);
        }
        /* set the location of the background*/
        backgroundLayer.setPosition(0, 0);

       final int fireIndex = 3;
       final int  no_Collision_Cols = 1;        
       final int  no_Collision_Rows = 1;        
        int[] collisionCells  = {
            fireIndex, /* collision Index*/
        };

        collisionLayer =  new TiledLayer(no_Collision_Cols, no_Collision_Rows, elementImg,tileSize,tileSize);       
        /* set the background with the images  */
        collisionLayer.setCell(0, 0, collisionCells [0]);       
        /* set the location of the background */
        collisionLayer.setPosition(4*tileSize, screenHeight-(5*tileSize));
    }

    private void createCharacterSprite()
    {
        characterSprite = new Sprite(charImg,charWidth,charHeight); 
        characterSprite.setFrame(0);
    }

    private void createLayerManager()
    {
        layerManager =  new LayerManager();               
        layerManager.append(characterSprite);
        layerManager.append(collisionLayer);
        layerManager.append(backgroundLayer);
    }

    private void initialiseNewGame() {       
        charX = 0;
        charY = screenHeight-charHeight-(4*tileSize);
        charJumpCounter = 0;
        charDirection = RIGHT_PRESSED;
        prevDirection = charDirection;
        charState = charStop;
        characterSprite.setTransform(Sprite.TRANS_NONE);
        characterSprite.setPosition(charX, charY);
    }

    public void run() {
        /* get graphics object for this canvas */
        Graphics g = getGraphics();
        /*infinite loop*/
        while (true) {
            /*Check for user key presses*/
            getUserInput();            
            /*check for collision*/
            checkCharCollision();
            /*render Screen*/
            renderScreen(g);
            /*controls refresh rate*/
            try {
                Thread.sleep(frameDelay);
            } catch (Exception e) {
            }
        }
    }

    private void getUserInput() {
        /*get keys state*/
        int keyState = getKeyStates();
        /*calculate the position for x axis*/
        if(charState!=charCollide)
        calculateCharMovement(keyState);               
    }

    private void calculateCharMovement(int keyState) {
        /*check which way to move and change direction*/
        if ((keyState & FIRE_PRESSED) !=0 ) {
            if(charState!=charJump) {
                charState = charJump;           
                characterSprite.setFrame(0);
            }
        }else  if ((keyState & LEFT_PRESSED) != 0) {
                charX = Math.max(0,charX-dx);           
                charDirection = keyState;
                charState = charWalk;

        } else if ((keyState & RIGHT_PRESSED) != 0) {
                charX = Math.min(screenWidth-charWidth, charX+dx);
                charDirection = keyState;
                charState = charWalk;               
        }else {
                if(charState!=charJump) {
                    charState = charStop;
                    characterSprite.setFrame(0);
                }
        }     

        if(charState == charWalk) {
            if(charDirection == RIGHT_PRESSED) {
                if(prevDirection == LEFT_PRESSED)
                    characterSprite.setTransform(Sprite.TRANS_NONE);
                characterSprite.nextFrame();               
            } else if(charDirection == LEFT_PRESSED) {
                characterSprite.setTransform(Sprite.TRANS_MIRROR);
                characterSprite.nextFrame();               
            }  
            characterSprite.setPosition(charX, charY);
        }else
        if(charState == charJump)  {           
            if(charJumpCounter>1, 20 + screenHeight>>1, 17);
            g.drawString("Press key 0 to restart.", screenWidth>>1, 40 + screenHeight>>1, 17);
        }
        /*paint off-screen buffer to screen*/
        flushGraphics();
    }

    protected void keyPressed(int keyCode)
    {
        if(keyCode == 48)
        {
            initialiseNewGame();
            return;
        }
    }
}

Code

Các bài liên quan
Game API – Phần 1
Game API – Phần 2
Game API – Phần 3