Đôi khi bạn chỉ cần hiển thị một phần của hình ảnh. Ví dụ như, khi bạn muốn hiển thị một số hình ảnh động, tốt hơn hết là đặt các frame hình vào trong một bức ảnh duy nhất chứ không nên vẽ thành nhiều file ảnh khác nhau. Điều này sẽ giúp bạn tiết kiệm bộ nhớ quý giá của thiết bị khi bạn chỉ phải load một đối tượng Image.
Chúng ta vẫn sẽ dùng mẫu đã viết từ bài trước, Loading Images Into Your Game.
Tôi cũng sẽ dùng kết quả cùa bài nay cho bài học kế tiếp.

Spinning Earth 16 x 16 pixels
Và đây là bộ ảnh cho animation trên. Bạn hãy download file ảnh bên dưới về máy để dùng, nếu bạn vẽ đẹp hơn thì hãy tự vẽ một bộ rồi chia sẻ cho anh em ^^ Nhớ copy nó vào thư mục res cho project của chúng ta.

Spinning Earth animation with all frames in a single image.
Dimensions: 144 x 16 pixels
Frame Size: 16 x 16 pixels
Còn đây là bản đồ thế giới, bạn có thể dựa vào nó mà vẽ.
Juggling Frames (tạm dịch: chia frame)
Những gì chúng ta sắp làm ở đây là chỉ vẽ một frame một lúc từ ảnh bạn vừa down về. Tất cả frame chúng ta sẽ vẽ đều ở trong khung hình chữ nhật. Hãy xem hình minh họa dưới đây:
Hình chữ nhật màu đỏ là đại diện cho vùng cắt, những gì không nằm trong vùng cắt thì không hiển thị trên màn hình. Số frame ghi trong ảnh là chỉ số của frame hiện thời đang hiển thị. Vị trí X giảm dần mỗi lần 16 đơn vị để nhảy frame từ phải sang trái. Đây là kĩ thuật clipping rectangle, những gì được cắt thì hiển thị và những gì không được cắt thì không hiển thị.
Bây giờ bạn copy file earthstrip.png vừa down được vào folder res chứa ảnh. Sau đó bật netbean và mở project ra. Chúng ta tiếp tục làm việc với clsCanvas.java, trong hàm load( ) bạn thay earth.png bằng earthstrip.png
Hãy thêm vài giá trị vào trong hàm run( ) bên dưới lời khai báo iKey:
Thêm đoạn code sau vào trong vòng lặp chính, sau khi check phím bấm và trước khi gọi phương thức vẽ.
Có rất nhiều thứ trong đoạn code trên. Đầu tiên, chúng ta lưu trữ thời gian hệ thống hiện thời bằng mili giây vào biến lCurrTick. Chúng ta dùng nó để kiểm tra thời gian tính từ lần cuối thay đổi frame ( lStart ) có lớn hơn thời gian delay đặt ra ( lDelay ) hay không và nếu có thì chuyển frame. Và khi chuyển frame, chúng ta đặt lStart bằng thời gian hiện tại cho khoảng thời gian delay vòng lặp kế tiếp.
Chúng ta có 9 frame cho animation này, chỉ số frame bắt đầu bằng 0, tức là frame cuối có chỉ số là 8. Vì thế, ta phải kiểm tra xem frame hiện tại, đang được lưu trong biến frameIndex có phải là frame cuối hay không. Nếu không thì tăng lên 1 frame. Ở frame cuối ta trở về frame đầu tiên.
Khi ta đã xác định được đến lúc chuyển frame và frame nào sẽ được hiển thị tiếp, chúng ta có thể tính toán vị trí X của ảnh để frame muốn hiển thị được xuất hiện trên vùng cắt. Dưới đây là công thức tính toán:
...và chỉnh sửa chỗ phương thức drawImage( ) như sau
Chú ý rằng chúng ta gọi setClip( ) 2 lần. Lần thứ nhất gọi để lấy full màn hình thiết bị, lần thứ 2 gọi để xác định vị trí trái đất được vẽ. Kích thước vùng cắt có hiệu lực cho đến khi bạn set vùng cắt khác với tham số mới. Nếu không reset clip được cặt bạn sẽ không thể vẽ lên những nơi khác trên màn hình vì hình nằm ngoài vùng cắt sẽ không được hiển thị. Bởi vậy bạn nên gọi setClip( ) full màn hình ở đầu đoạn code thực hiện việc vẽ vời.
Cuối cùng thì clsCanvas.java sẽ như thế này.
Bạn có thể chạy MIDlet được rồi. Bạn sẽ nhìn thấy một quả địa cầu xoay tròn và ở dưới là vị trí X của ảnh. Bạn cũng có thể thay đổi tốc độ xoay của trái đất bằng cách thay đổi giá trị biến iDelay trong hàm run( ). Tăng giá trị, nó sẽ quay chậm đi và ngược lại. Hãy thử xem.
Chúc các bạn thành công, và tiếp tục với bài học tới.
Chúng ta vẫn sẽ dùng mẫu đã viết từ bài trước, Loading Images Into Your Game.
Tôi cũng sẽ dùng kết quả cùa bài nay cho bài học kế tiếp.
Reintroducing Earth: Animation
Trong bài học này, chúng ta sẽ hiển thị trái đất đang quay giống như hình dưới đây:

Spinning Earth 16 x 16 pixels
Và đây là bộ ảnh cho animation trên. Bạn hãy download file ảnh bên dưới về máy để dùng, nếu bạn vẽ đẹp hơn thì hãy tự vẽ một bộ rồi chia sẻ cho anh em ^^ Nhớ copy nó vào thư mục res cho project của chúng ta.

Spinning Earth animation with all frames in a single image.
Dimensions: 144 x 16 pixels
Frame Size: 16 x 16 pixels
Juggling Frames (tạm dịch: chia frame)
Những gì chúng ta sắp làm ở đây là chỉ vẽ một frame một lúc từ ảnh bạn vừa down về. Tất cả frame chúng ta sẽ vẽ đều ở trong khung hình chữ nhật. Hãy xem hình minh họa dưới đây:

Hình chữ nhật màu đỏ là đại diện cho vùng cắt, những gì không nằm trong vùng cắt thì không hiển thị trên màn hình. Số frame ghi trong ảnh là chỉ số của frame hiện thời đang hiển thị. Vị trí X giảm dần mỗi lần 16 đơn vị để nhảy frame từ phải sang trái. Đây là kĩ thuật clipping rectangle, những gì được cắt thì hiển thị và những gì không được cắt thì không hiển thị.
Bây giờ bạn copy file earthstrip.png vừa down được vào folder res chứa ảnh. Sau đó bật netbean và mở project ra. Chúng ta tiếp tục làm việc với clsCanvas.java, trong hàm load( ) bạn thay earth.png bằng earthstrip.png
// try to load the image file imgEarth = Image.createImage("/images/earthstrip.png");
Hãy thêm vài giá trị vào trong hàm run( ) bên dưới lời khai báo iKey:
public void run() {
int iKey = 0;
int imgX = 50; // x coordinate of the image
int frameIndex = 0; // current frame to be drawn
long lDelay = 250; //time to pause between frames in milliseconds
long lStart = 0; //time we last changed frames in milliseconds
long lCurrTick = 0; // current system time in milliseconds;
Thêm đoạn code sau vào trong vòng lặp chính, sau khi check phím bấm và trước khi gọi phương thức vẽ.
if ((iKey & GameCanvas.FIRE_PRESSED) != 0){ isRunning = false; } lCurrTick = System.currentTimeMillis(); if ((lCurrTick-lStart) >= lDelay){ lStart = lCurrTick; // save the current time if (frameIndex < 8) { frameIndex++; // skip to the next frame } else { frameIndex = 0; // go back to first frame } imgX = 50 - (frameIndex * 16); // compute x relative to clip rect } //restore the clipping rectangle to full screen g.setClip(0, 0, getWidth(), getHeight()); //set drawing color to black g.setColor(0x000000);
Có rất nhiều thứ trong đoạn code trên. Đầu tiên, chúng ta lưu trữ thời gian hệ thống hiện thời bằng mili giây vào biến lCurrTick. Chúng ta dùng nó để kiểm tra thời gian tính từ lần cuối thay đổi frame ( lStart ) có lớn hơn thời gian delay đặt ra ( lDelay ) hay không và nếu có thì chuyển frame. Và khi chuyển frame, chúng ta đặt lStart bằng thời gian hiện tại cho khoảng thời gian delay vòng lặp kế tiếp.
Chúng ta có 9 frame cho animation này, chỉ số frame bắt đầu bằng 0, tức là frame cuối có chỉ số là 8. Vì thế, ta phải kiểm tra xem frame hiện tại, đang được lưu trong biến frameIndex có phải là frame cuối hay không. Nếu không thì tăng lên 1 frame. Ở frame cuối ta trở về frame đầu tiên.
Khi ta đã xác định được đến lúc chuyển frame và frame nào sẽ được hiển thị tiếp, chúng ta có thể tính toán vị trí X của ảnh để frame muốn hiển thị được xuất hiện trên vùng cắt. Dưới đây là công thức tính toán:
image_X = X_of_clipping_rectangle - (frame_index * frame_width);
setClip( )
Bạn chắc cũng chú ý thấy chúng ta vừa gọi một phương thức mới, setClip( ). Phương thức này nằm trong lớp Graphics, cho chúng ta định vị vị trí và kích thước vùng cắt. Hai biến đầu chỉ ra tọa độ x và y, hai biến sau chỉ ra chiều dài và chiều rộng của vùng cắt.
Hãy vẽ thêm một chút.
Thêm đoạn code sau vào vòng lặp chính ngay trước lời gọi drawImage( )
Hãy vẽ thêm một chút.
Thêm đoạn code sau vào vòng lặp chính ngay trước lời gọi drawImage( )
g.drawString("Frame : " + Integer.toString(frameIndex), 2, 68, Graphics.TOP | Graphics.LEFT);
g.drawString("X : " + Integer.toString(imgX), 2, 88, Graphics.TOP | Graphics.LEFT);
//clip the drawing area to a single frame
g.setClip(50, 50, 16, 16);
...và chỉnh sửa chỗ phương thức drawImage( ) như sau
g.drawImage(imgEarth, imgX, 50, Graphics.TOP | Graphics.LEFT);
Chú ý rằng chúng ta gọi setClip( ) 2 lần. Lần thứ nhất gọi để lấy full màn hình thiết bị, lần thứ 2 gọi để xác định vị trí trái đất được vẽ. Kích thước vùng cắt có hiệu lực cho đến khi bạn set vùng cắt khác với tham số mới. Nếu không reset clip được cặt bạn sẽ không thể vẽ lên những nơi khác trên màn hình vì hình nằm ngoài vùng cắt sẽ không được hiển thị. Bởi vậy bạn nên gọi setClip( ) full màn hình ở đầu đoạn code thực hiện việc vẽ vời.
Cuối cùng thì clsCanvas.java sẽ như thế này.
package MyGame;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
public class clsCanvas extends GameCanvas implements Runnable {
private boolean isRunning = true;
private Graphics g;
private midMain fParent;
private Image imgEarth;
public clsCanvas(midMain m) {
super(true);
fParent = m;
setFullScreenMode(true);
}
public void start(){
Thread runner = new Thread(this);
runner.start();
}
public void load(){
try{
// try to load the image file
imgEarth = Image.createImage("/images/earthstrip.png");
}catch(Exception ex){
// exit the app if it fails to load the image
isRunning = false;
return;
}
}
public void unload(){
// make sure the object get's destroyed
imgEarth = null;
}
public void run() {
int iKey = 0;
int imgX = 50; // x coordinate of the image
int frameIndex = 0; // current frame to be drawn
long lDelay = 250; //time to pause between frames in milliseconds
long lStart = 0; //time we last changed frames in milliseconds
long lCurrTick = 0; // current system time in milliseconds;
load();
g = getGraphics();
while(isRunning){
iKey = getKeyStates();
if ((iKey & GameCanvas.FIRE_PRESSED) != 0){
isRunning = false;
}
lCurrTick = System.currentTimeMillis();
if ((lCurrTick-lStart) >= lDelay){
lStart = lCurrTick; // save the current time
if (frameIndex < 8) {
frameIndex++; // skip to the next frame
} else {
frameIndex = 0; // go back to first frame
}
imgX = 50 - (frameIndex * 16); // compute x relative to clip rect
}
//restore the clipping rectangle to full screen
g.setClip(0, 0, getWidth(), getHeight());
//set drawing color to black
g.setColor(0x000000);
//fill the whole screen
g.fillRect(0, 0, getWidth(), getHeight());
// set drawing color to white
g.setColor(0xffffff);
//display the key code last pressed
g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);
g.drawString("Frame : " + Integer.toString(frameIndex), 2, 68, Graphics.TOP | Graphics.LEFT);
g.drawString("X : " + Integer.toString(imgX), 2, 88, Graphics.TOP | Graphics.LEFT);
//clip the drawing area to a single frame
g.setClip(50, 50, 16, 16);
//draw the image
g.drawImage(imgEarth, imgX, 50, Graphics.TOP | Graphics.LEFT);
flushGraphics();
try{
Thread.sleep(30);
} catch (Exception ex){
}
}
g = null;
unload();
fParent.destroyApp(false);
fParent = null;
}
}
Bạn có thể chạy MIDlet được rồi. Bạn sẽ nhìn thấy một quả địa cầu xoay tròn và ở dưới là vị trí X của ảnh. Bạn cũng có thể thay đổi tốc độ xoay của trái đất bằng cách thay đổi giá trị biến iDelay trong hàm run( ). Tăng giá trị, nó sẽ quay chậm đi và ngược lại. Hãy thử xem.

Chúc các bạn thành công, và tiếp tục với bài học tới.
Không có nhận xét nào:
Đăng nhận xét