Trong bài trước, lớp GameCanvas được xây dựng với tất cả các tương tác giữa các element chính. Bây giờ, tất cả các element gameplay được xây dựng, đây là lúc cải thiện giao diện trực quan của trò chơi bằng cách sử dụng các tập tin hình ảnh thay vì sử dụng phương thức draw/fill trên đối tượng graphics để biểu diễn các thực thể trò chơi. Tôi đã tạo ra một số hình ảnh dựa trên các tài nguyên SpriteLib sẽ được sử dụng trong trò chơi.

Image

Để truy cập và hiển thị các hình ảnh trong MIDlet, ta phải sử dụng một phần tử hay lớp Image trong giao diện người dùng cấp thấp. Lớp này lưu trữ dữ liệu hình ảnh đồ họa độc lập với thiết bị hiển thị trong một bộ nhớ đệm off-screen. Các hình ảnh hoặc ở dạng Mutable hay Immutable phụ thuộc vào cách mà chúng được tạo ra. Thông thường, các hình ảnh dạng Immutable được ta ra thông qua việc load hình ảnh từ tài nguyên như file Jar hay mạng. Các hình ảnh dạng Immutable được tạo ra thông qua việc sử dụng phương thức tĩnh createImage() của lớp Image. Tuy nhiên, một khi ảnh Immutable đã được tạo ra thì nó không thể được sửa đổi. Các hình ảnh dạng Mutable được tạo ra thông qua phương thức khởi dựng(Constructor) của lớp Image và lúc đó nó chỉ chứa các pixel trắng. Ứng dụng có thể biểu diễn image Mutable bằng cách gọi phương thức getGraphics() trên Image để có được đối tượng Graphics cho mục đích này.

Trong game này, các hình ảnh Immutable đại diện cho các thực thể game. Bây giờ, ta hãy thay đổi lớp Pad và tạo ra đối tượng Image trong constructor của nó.

Image image;
public Pad() {
try {
image = Image.createImage(“/pad.png”);
width = image.getWidth();
height = image.getHeight();
} catch (IOException e) {
e.printStackTrace();
}
}

Sau đó, cài đặt lại phương thức paint() của lớp Pad để vẽ hình ảnh:

public void paint(Graphics g) {
g.drawImage(image, x,y, Graphics.TOP | Graphics.LEFT);
}

Lưu ý: khi tạo hình ảnh, bạn nên sử dụng ảnh với định dạng PNG, bởi đây là định dạng thông dụng của các loại thiết bị. Những thiết bị đời mới có thể hỗ trợ nhiều định dạng khác như: JPEG, BMP…, tuy nhiên để an toàn bạn nên tạo ra file ảnh dạng PNG.

Tiếp theo, ta sửa đổi lớp Ball như sau:

Image image;
public Ball() {
try {
image = Image.createImage(“/ball.png”);
width = image.getWidth();
height = image.getHeight();
} catch (IOException e) {
e.printStackTrace();
}
}

public void paint(Graphics g) {
g.drawImage(image, x,y, Graphics.TOP | Graphics.LEFT);
}

Bây giờ, nếu bạn chạy ứng dụng, bạn sẽ cố một Ball đẹp, tuy nhiên trong game sử dụng nhiều Brick với màu sắc khác nhau, và mỗi Brick được cắt từ một Image chính. Bạn có thể làm điều này thông qua sử dụng phương thức setClip() trong lớp Graphics, tuy nhiên từ MIDP 2.0 trở đi, nó còn một hỗ trợ một lớp Sprite để ta làm điều này.

Sprite

Một Sprite là một thuật ngữ chung trong game. Nó tham chiếu đến một phần tử trực quan được tạo ra bởi các Image, thường chuyển động và di chuyển xung quanh các phần tử khác một cách độc lập trong game. Lớp Sprite trong MIDP 2.0 đại diện cho khái niệm này. Nó cho phép tạo ra các Sprite dựa trên các hình ảnh với nhiều frame. Nó có thể thay đổi frame, điều khiển chuyển động và kiểm tra va chạm với các phần tử khác.

Tất cả các khả năng này được sử dụng trong các thực thể của game, và ta hãy xem cách xây dựng Brick:

public static int BRICK_FRAMES = 20;
Image image = null;
Sprite sprite = null;
public Brick(){
// load image
image = Image.createImage(“/bricks.png”);
// create the sprite with 20 frames, one for each brick
sprite = new Sprite(image,image.getWidth()/BRICK_FRAMES, image.getHeight());
width = sprite.getWidth();
height = sprite.getHeight();
}

Mã này tạo ra một Sprite với 20 khung hình(frame), một cho mỗi Brick có sẵn trên hình ảnh. Trước khi vẽ Brick bạn cần thay đổi frame sẽ được sử dụng.

public void paint(Graphics g) {
if (active){
sprite.nextFrame();
sprite.setPosition(x, y);
sprite.paint(g);
}
}

Bây giờ, nếu bạn chạy Midlet, bạn sẽ được các Brick thay đổi màu sắc vào tất cả thời gian. Tuy nhiên, vấn đề đặt ra ở đây là hình ảnh bricks.png được load cho mỗi Brick được tạo ra, điều này thật sự không tối ưu. Sửa đổi mã để nó tối ưu như sau:

public Brick(Image image, int numFrames, int frameSelected){
sprite = new Sprite(image,image.getWidth()/numFrames,image.getHeight());
// set frame
sprite.setFrame(frameSelected);
// get size for collision detection
width = sprite.getWidth();
height = sprite.getHeight();
}

public void paint(Graphics g) {
if (active){
sprite.setPosition(x, y);
sprite.paint(g);
}
}

Bây giờ chỉ cần load một lần trong phương thức init () và chọn một frame sẽ được sử dụng trong vòng đời của một Brick.

// create bricks
Image bricksFrames = null;
try {
bricksFrames = Image.createImage(“/bricks.png”);
} catch (IOException e) {
e.printStackTrace();
}
Brick brick = new Brick(bricksFrames, 20, 0);
bricks = new Vector();
for (int i=0; (i*(brick.width+2)) < getWidth(); i++){
brick = new Brick(bricksFrames, 20, i);
brick.sprite.setFrame(i);
brick.x = (i*(brick.width+2));
brick.y = 20;
bricks.addElement(brick);
}

Bây giờ bạn có một hàng các Brick đẹp, mỗi hàng đều có mỗi Brick với một màu riêng. Trong bài tiếp theo, chúng ta sẽ thảo luận cách lưu điểm số của ngươi chơi(high-score).

Code

Các bài liên quan:
Phát triển game đơn giãn trên Mobile(P1)
Phát triển game đơn giãn trên Mobile(P2)
Phát triển game đơn giãn trên Mobile(P3)
Phát triển game đơn giãn trên Mobile(P5)
Phát triển game đơn giãn trên Mobile(P6)
Phát triển game đơn giãn trên Mobile(P7)