Thứ Năm, 21 tháng 3, 2013

Đến nơi tận cùng

Một khi bạn nhận ra rằng những người xung quanh bạn thật quá vô tình thì bạn cũng không còn trông mong gì họ nữa. Cách đây khoảng 2 năm gì đó tôi cũng quan tâm nhiều người lắm nhưng bây giờ thì điều đó không còn tồn tại nữa. Dù tôi biết là cho đến ngày tận cùng thì tôi vẫn nhớ tới họ. Liệu rồi sau này tôi còn quan tâm đến ai nữa không.
Tôi từng nhớ một câu nói rất hay, không biết có đúng hay không: tốt với tất cả thì cũng như không tốt với ai cả. Chuyện này cũng không phải là tại mình, những suy nghĩ của họ thì không phải mình có thể điều khiển được. Tôi không phải muốn mọi người phải trả lại những tình cảm mà tôi bỏ ra, nhưng vô tình quá đã khiến trái tim người ta tan nát.
Một hôm chợt thức dậy và phát hiện ra rằng, hình như mình đã già rồi mà không có một người bạn được gọi là tri kỉ. Có phải là mình xấu xa lắm không, nhưng nghĩ kĩ lại thì không phải như vậy. Có những người thật sự xấu hơn mình nhiều nhưng họ vẫn có cặp có đôi, và một câu hỏi tại sao lại xuất hiện. Tôi từng nhìn thấy cuộc sống thật nhàm chán khi mỗi ngày đều tìm kiếm hạnh phúc bằng việc giúp đỡ người khác, tại sao không có ai muốn nhìn thấy tôi hạnh phúc, muốn làm tôi hạnh phúc, tất nhiên không phải là người trong gia đình tôi.
Thật chất có nhiều câu chuyện làm tôi cảm thấy điều đó cũng bình thường như là final fantasy 7 vậy, dù Tifa có tốt với cloud đến đâu thì sao, cuối cùng cũng không là gì cả, chỉ chuốc thêm đau khổ về mình. Tôi cũng vậy, đã biết là vô vọng thì tại sao lại như vậy, dù cuộc sống này có đến đâu thì tôi cũng có những người thân bên cạnh. Những người cuối cùng vẫn không bỏ rơi tôi...

A new World

Dù cho bản đồ của mình có rộng lớn đến đâu chăng nữa thì cũng không thể nào chỉ đi trong đó cho đến hết trò chơi. Bây giờ chúng ta cần làm cho nó rộng lớn hơn nhiều nữa, và chúng ta sẽ tạo một class mới gọi là hole.java, nó như sau: hole.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\hole.java

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class hole {
    //những điểm thông giữa 2 map.
int[][] hole={{0,3,5},{1,5,4}};
//những vị trí bản đồ mà từ đó sẽ thoát ra.
int[] out={1,0};
//kiểm tra xem có phải đang trong điểm chuyển không.
boolean inhole=true;
//bản đồ hiện tại.
int m;
//hàm kiểm tra xem có nằm trong điểm chuyển hay không.
public boolean check(int nm,int x,int y,map m){
    //lần lượt check qua từng điểm chuyển.
for(int i=0;i<hole.length;i++){
   int nx=x/16;
   int ny=y/16;
if(nm==hole[i][0]&&nx==hole[i][1]&&ny==hole[i][2]){
    this.m=out[i];
return true;}
}
return false;}
//hàm chuyển map.
public void change(int nm,int x,int y,map m){
if(check(nm,x,y,m)){
if(!inhole){
    inhole=true;
m.m=hole[this.m][0];
m.load("m"+hole[this.m][0]+".mbd",hole[this.m][1],hole[this.m][2],m.w,m.h);}}
else{
inhole=false;}
}
}
Nhưng khi thay đổi map thì nhân vật phụ cũng thay đổi luôn đó, cho nguyên nhân đó nên chúng ta cũng thay đổi cách tạo nhân vật phụ, nhân vật dẫn chuyện cũng nên thay đổi, nhưng ở đây tôi không nói nhiều về chuyện đó. Chúng ta chỉ thay đổi một chút để game không bị lỗi, file map.java thành thế này. map.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\map.java
import java.io.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class map {
    //các thông số gồm có số cột, số hàng trong map, kích thước mỗi tile tw, th.
    //tọa độ map (x,y), phím bấm k, số lần di chuyển nhỏ.
    int mw,mh,tw,th,x,y,k,step=0,w,h,m=0;
    //chứa dữ liệu các tile trong map.
    byte[] map;
    //hình ảnh dùng làm tile
    Image tiles,other;
    //dùng class TiledLayer có sẵn trong GameCanvas để tạo map.
    TiledLayer tl;
    //mốc thời gian di chuyển lần cuối
    long lm=0;
    //thêm một biến other ot dùng làm nhân vật phụ.
        other[] ot;
        //thêm biến vẽ nhân vật dẫn thoại.
        trail tr=new trail();
        //tạo biến check để chuyển map
        hole hl=new hole();
    //hàm khởi tạo sẽ tải hình ảnh làm tile vào class map.
    public map(String im){
    try{
    this.tiles=Image.createImage("/"+im);
    }catch(Exception e){}}
    //hàm load dùng tải file nguồn và phân tích các thông số trong đó vào map.
    //bây giờ ta có thông số xác định tọa độ ban đầu nhân vật, tại cột c, dòng r và so sánh với độ dài rộng màn hình.
public void load(String fn,int c,int r,int w,int h){
    //file nguồn là dữ liệu với cấu trúc: [số cột]-[số hàng]-[tile width]-[tile height]-[dữ liệu map (số cột * số hàng)]
try{
InputStream is=getClass().getResourceAsStream("/"+fn);
mw=is.read();
mh=is.read();
tw=is.read();
th=is.read();
map=new byte[is.available()];
is.read(map);
is.close();
//tạo TiledLayer tl từ những gì đã nhận được.
tl=new TiledLayer(mw,mh,tiles,tw,th);
//lát gạch vào TiledLayer tl.
for(int i=0;i<mh;i++){
for(int j=0;j<mw;j++){
tl.setCell(j, i, map[i*mw+j]);
}}
this.w=w;
this.h=h;
x=w/2-c*tw;
y=h/2-r*th;
//tạo file ảnh cho nhân vật phụ và khởi tạo ot ở vị trí cột 4 và hàng 6 trên map.
other=Image.createImage("/other.png");
ot=new other[3];
for(int i=0;i<ot.length;i++)
{
    Random d=new Random();
    int cot=Math.abs(d.nextInt())%mw,rot=Math.abs(d.nextInt())%mh;
    ot[i]=new other(other,cot*16,rot*16);
Thread.sleep(50);}
}catch(Exception e){}} 
//hàm paint dùng vẽ tl lên Graphics g, và nhận giá trị phím bấm vào hàm move.
void paint(Graphics g,int k){
    move(k);
    g.setColor(0);
    g.drawRect(x,y,tl.getWidth(),tl.getHeight());
tl.paint(g);
//vẽ nhân vật phụ lên map.
for(int i=0;i<ot.length;i++)
ot[i].paint(g,this);
//vẽ nhân vật dẫn chuyện ra màn hình.
tr.paint(g,this,k);
//dùng hàm để chuyển map khi phát hiện đúng điều kiện.
hl.change(this.m, w/2-x, h/2-y, this);
}
//hàm move nhận giá trị phím bấm k và kiểm tra điều kiện để thay đổi tọa độ của map.
void move(int k){
    //do mỗi lần di chuyển ta cho chuyển 16 pixel, nên ta cần chia ra 4 lần di chuyễn nhỏ để hình ảnh mượt hơn.
    //nếu thấy không cần bước di chuyển nhỏ nào nữa thì cho di gán di chuyển tiếp
if(step==0){
    //gán tọa độ cũ vào các biến gx gy.
    int gx=x,gy=y;
    //tính tọa độ mới.
    switch(k){
        case -1:
            gy+=16;
    break;
    case -2:
            gy-=16;
    break;
    case -3:
            gx+=16;
    break;
    case -4:
            gx-=16;
    break;}
    //kiểm tra xem toa6 độ mới có chạm tường không.
if(check(w/2-gx,h/2-gy)){
this.k=k;
step=4;}
}else{
    //nếu còn lượt di chuyển nhỏ step!=0, thì xét đến thời gian di chuyển lần cuối.
    if(lm<=curr()-30){
    step--;
    switch(this.k){
        case -1:
            y+=th/4;
    break;
        case -2:
            y-=th/4;
    break;
        case -3:
            x+=tw/4;
    break;
        case -4:
            x-=tw/4;
    break;}
    lm=curr();}
}
//gán vị trí cho tl.
tl.setPosition(x, y);
}
  long curr(){return System.currentTimeMillis();}
  //hàm kiểm tra xem có chạm tường không, tường là gạch số 3.
  boolean check(int a,int b){
      if(a<0||b<0||a>mw*16-16||b>mh*16-16){return false;}
 return tl.getCell(a/16, b/16)<3;}
}

Kết quả chúng ta như sau:


Thoại dẫn truyện


Diều quan trọng nhất trong game RPG chính là cốt truyện, như vậy thì không thể nào mà không có lời thoại nào, trong bài này chúng ta sẽ tạo ra một loạt những nhân vật dẫn thoại và cho họ tham gia vào trò chơi.
Chúng ta tạo ra một class mới gọi là trail.java như sau :
trail.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\trail.java


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class trail {
    //vị trí của những nhân vật dẫn chuyện.
int[][] pos={{2,3},{12,23},{19,6}};
//mỗi nhân vật dẫn chuyện nói một câu.
String[] say={"hello world, this is role playing game","i am holyeyed, nice to meet you","in the end of world, we will found out how love is"};
//biến xác định xem đã có đang nói hay không.
boolean load=false;
//câu nói của nhân vật đang nói.
String lstr;
//vị trí của nhân vật đang nói.
int lpos=-1;
//hình ảnh vẽ nhân vật dẫn chuyện.
Image im;
//biến thời gian mốc load lời thoại.
long lload=0;
public trail(){
try{
im=Image.createImage("/trail.png");}catch(Exception e){}}
//hàm paint này sẽ vẽ lên map nhưng nhân vật dẫn chuyện.
public void paint(Graphics g,map m,int k){
    //mỗi lần lập lại cho nhân vật đang nói là -1, tức là không ai đang nói.
    lpos=-1;
    //dùng for để vẽ ra từng nhân vật trong mảng, đồng thời so sánh xem nhân vật nào đang ở gần, để gán là nhân vật hiện hành.
for(int i=0;i<pos.length;i++){
g.drawRegion(im,i*16,0,16,16,0,pos[i][0]*16+m.x,pos[i][1]*16+m.y,0);
//tính vị khoảng cách giữ nhân vật chính và nhân vật dẫn.
int nx=Math.abs(pos[i][0]*16+m.x-m.w/2),ny=Math.abs(pos[i][1]*16+m.y-m.h/2);
//nếu cách nhau 1 ô, <=16 thì gán vào.
if(nx<=16&&ny<=16)lpos=i;
}
//nếu phím bấm là -5 thì xét tiếp.
if(k==-5){
    //nếu như cách mốc thời gian là 150millis thì cho thực hiện kiểm tra.
    if(lload<System.currentTimeMillis()-150){
        //nếu gần nhân vật dẫn lpos!=-1 và load =false thì cho load.
if(!load&&lpos!=-1){
load=true;
lstr=say[lpos];}
//ngược thì thì bỏ load.
else{
load=false;}
//gán mốc thời gian load và unload.
    lload=System.currentTimeMillis();}}
//nếu như đã load thì vẽ câu nói ra màn hình.
if(load){
g.setColor(0x0000ff);
g.fillRect(0,0,m.w,m.h/5);
g.setColor(0xf0f0f0);
g.drawString(lstr,0,0,Graphics.TOP|Graphics.LEFT);}
}
}


Và chúng ta sẽ sử dụng chúng trong file map.java như thế này, ở đây tôi dùng ảnh này để vẽ nhân vật dẫn chuyện :
map.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\map.java

import java.io.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;

public class map {
    //các thông số gồm có số cột, số hàng trong map, kích thước mỗi tile tw, th.
    //tọa độ map (x,y), phím bấm k, số lần di chuyển nhỏ.
    int mw,mh,tw,th,x,y,k,step=0,w,h;
    //chứa dữ liệu các tile trong map.
    byte[] map;
    //hình ảnh dùng làm tile
    Image tiles,other;
    //dùng class TiledLayer có sẵn trong GameCanvas để tạo map.
    TiledLayer tl;
    //mốc thời gian di chuyển lần cuối
    long lm=0;
    //thêm một biến other ot dùng làm nhân vật phụ.
        other[] ot;
        //thêm biến vẽ nhân vật dẫn thoại.
        trail tr=new trail();
    //hàm khởi tạo sẽ tải hình ảnh làm tile vào class map.
    public map(String im){
    try{
    this.tiles=Image.createImage("/"+im);
    }catch(Exception e){}}
    //hàm load dùng tải file nguồn và phân tích các thông số trong đó vào map.
    //bây giờ ta có thông số xác định tọa độ ban đầu nhân vật, tại cột c, dòng r và so sánh với độ dài rộng màn hình.
public void load(String fn,int c,int r,int w,int h){
    //file nguồn là dữ liệu với cấu trúc: [số cột]-[số hàng]-[tile width]-[tile height]-[dữ liệu map (số cột * số hàng)]
try{
InputStream is=getClass().getResourceAsStream("/"+fn);
mw=is.read();
mh=is.read();
tw=is.read();
th=is.read();
map=new byte[is.available()];
is.read(map);
//tạo TiledLayer tl từ những gì đã nhận được.
tl=new TiledLayer(mw,mh,tiles,tw,th);
//lát gạch vào TiledLayer tl.
for(int i=0;i<mh;i++){
for(int j=0;j<mw;j++){
tl.setCell(j, i, map[i*mw+j]);
}}
this.w=w;
this.h=h;
x=w/2-c*tw;
y=h/2-r*th;
//tạo file ảnh cho nhân vật phụ và khởi tạo ot ở vị trí cột 4 và hàng 6 trên map.
other=Image.createImage("/other.png");
ot=new other[3];
for(int i=0;i<ot.length;i++)
{ot[i]=new other(other,(i+1)*12*16,(i+1)*13*16);
Thread.sleep(50);}
}catch(Exception e){}}
//hàm paint dùng vẽ tl lên Graphics g, và nhận giá trị phím bấm vào hàm move.
void paint(Graphics g,int k){
    move(k);
    g.setColor(0);
    g.drawRect(x,y,tl.getWidth(),tl.getHeight());
tl.paint(g);
//vẽ nhân vật phụ lên map.
for(int i=0;i<ot.length;i++)
ot[i].paint(g,this);
//vẽ nhân vật dẫn chuyện ra màn hình.
tr.paint(g,this,k);
}
//hàm move nhận giá trị phím bấm k và kiểm tra điều kiện để thay đổi tọa độ của map.
void move(int k){
    //do mỗi lần di chuyển ta cho chuyển 16 pixel, nên ta cần chia ra 4 lần di chuyễn nhỏ để hình ảnh mượt hơn.
    //nếu thấy không cần bước di chuyển nhỏ nào nữa thì cho di gán di chuyển tiếp
if(step==0){
    //gán tọa độ cũ vào các biến gx gy.
    int gx=x,gy=y;
    //tính tọa độ mới.
    switch(k){
        case -1:
            gy+=16;
    break;
    case -2:
            gy-=16;
    break;
    case -3:
            gx+=16;
    break;
    case -4:
            gx-=16;
    break;}
    //kiểm tra xem toa6 độ mới có chạm tường không.
if(check(w/2-gx,h/2-gy)){
this.k=k;
step=4;}
}else{
    //nếu còn lượt di chuyển nhỏ step!=0, thì xét đến thời gian di chuyển lần cuối.
    if(lm<=curr()-30){
    step--;
    switch(this.k){
        case -1:
            y+=th/4;
    break;
        case -2:
            y-=th/4;
    break;
        case -3:
            x+=tw/4;
    break;
        case -4:
            x-=tw/4;
    break;}
    lm=curr();}
}
//gán vị trí cho tl.
tl.setPosition(x, y);
}
  long curr(){return System.currentTimeMillis();}
  //hàm kiểm tra xem có chạm tường không, tường là gạch số 3.
  boolean check(int a,int b){
 return tl.getCell(a/16, b/16)<3;}
}

Kết quả :


Vậy thôi, dù sao thì cũng đơn giản, câu thoại là câu đơn và chưa có hàm vẽ để tự động ngắt dòng. Chúng ta tiếp tục thêm vào trail.java như sau :
trail.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\trail.java


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class trail {
    //vị trí của những nhân vật dẫn chuyện.
int[][] pos={{2,3},{12,23},{19,6}};
//mỗi nhân vật dẫn chuyện nói một câu.
String[] say={"hello world, this is role playing game","i am holyeyed, nice to meet you, if you want to follow me, please visit me in http://holyeyed.99k.org, or http://j2medraw.blogspot.com. nice to see you there....","in the end of world, we will found out how love is"};
//biến xác định xem đã có đang nói hay không.
boolean load=false;
//câu nói của nhân vật đang nói.
String lstr;
//vị trí của nhân vật đang nói.
int lpos=-1;
//hình ảnh vẽ nhân vật dẫn chuyện.
Image im;
//biến thời gian mốc load lời thoại.
long lload=0;
public trail(){
try{
im=Image.createImage("/trail.png");}catch(Exception e){}}
//hàm paint này sẽ vẽ lên map nhưng nhân vật dẫn chuyện.
public void paint(Graphics g,map m,int k){
    //mỗi lần lập lại cho nhân vật đang nói là -1, tức là không ai đang nói.
    lpos=-1;
    //dùng for để vẽ ra từng nhân vật trong mảng, đồng thời so sánh xem nhân vật nào đang ở gần, để gán là nhân vật hiện hành.
for(int i=0;i<pos.length;i++){
g.drawRegion(im,i*16,0,16,16,0,pos[i][0]*16+m.x,pos[i][1]*16+m.y,0);
//tính vị khoảng cách giữ nhân vật chính và nhân vật dẫn.
int nx=Math.abs(pos[i][0]*16+m.x-m.w/2),ny=Math.abs(pos[i][1]*16+m.y-m.h/2);
//nếu cách nhau 1 ô, <=16 thì gán vào.
if(nx<=16&&ny<=16)lpos=i;
}
//nếu phím bấm là -5 thì xét tiếp.
if(k==-5){
    //nếu như cách mốc thời gian là 150millis thì cho thực hiện kiểm tra.
    if(lload<System.currentTimeMillis()-150){
        //nếu gần nhân vật dẫn lpos!=-1 và load =false thì cho load.
if(!load&&lpos!=-1){
load=true;
lstr=say[lpos];}
//ngược thì thì bỏ load.
else{
load=false;}
//gán mốc thời gian load và unload.
    lload=System.currentTimeMillis();}}
//nếu như đã load thì vẽ câu nói ra màn hình.
if(load){
g.setColor(0x0000ff);
g.fillRect(0,0,m.w,m.h/5);
g.setColor(0xf0f0f0);
wrap(g,lstr,0,0,m.w,m.h);}
}
//hàm dùng để chia str thành nhiều đoạn nhỏ cho vừa màn hình.
public void wrap(Graphics g,String lstr,int x,int y,int w,int h){
Font f=g.getFont();
int fh=f.getHeight();
if(!lstr.endsWith(" "))lstr+=" ";
int sa=0,ns=lstr.indexOf(" ",sa),line=0;
while(ns!=-1){
    String dstr=lstr.substring(sa,ns+1);
int d=f.stringWidth(dstr);
if(d>=w-f.stringWidth("ww") ||ns==lstr.length()-1){
g.drawString(dstr,x,y+fh*line,Graphics.TOP|Graphics.LEFT);
line++;
sa=ns+1;}
ns=lstr.indexOf(" ",ns+1);}}
}

Kết quả như sau :




Thứ Bảy, 9 tháng 3, 2013

Giới hạn di chuyển

bài này không nói gì nhiều, chỉ có những chú thích trong file source. Trong ví dụ, đã xuất hiện được 3 nhân vật thông qua mảng other, và thêm điều hiện để kiểm tra xem có chạm tường hay không, nếu chạm thì không đi được nữa thế thôi. File map.java map.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\map.java
import java.io.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;

public class map {
    //các thông số gồm có số cột, số hàng trong map, kích thước mỗi tile tw, th.
    //tọa độ map (x,y), phím bấm k, số lần di chuyển nhỏ.
    int mw,mh,tw,th,x,y,k,step=0,w,h;
    //chứa dữ liệu các tile trong map.
    byte[] map;
    //hình ảnh dùng làm tile
    Image tiles,other;
    //dùng class TiledLayer có sẵn trong GameCanvas để tạo map.
    TiledLayer tl;
    //mốc thời gian di chuyển lần cuối
    long lm=0;
    //thêm một biến other ot dùng làm nhân vật phụ.
        other[] ot;
    //hàm khởi tạo sẽ tải hình ảnh làm tile vào class map.
    public map(String im){
    try{
    this.tiles=Image.createImage("/"+im);
    }catch(Exception e){}}
    //hàm load dùng tải file nguồn và phân tích các thông số trong đó vào map.
    //bây giờ ta có thông số xác định tọa độ ban đầu nhân vật, tại cột c, dòng r và so sánh với độ dài rộng màn hình.
public void load(String fn,int c,int r,int w,int h){
    //file nguồn là dữ liệu với cấu trúc: [số cột]-[số hàng]-[tile width]-[tile height]-[dữ liệu map (số cột * số hàng)]
try{
InputStream is=getClass().getResourceAsStream("/"+fn);
mw=is.read();
mh=is.read();
tw=is.read();
th=is.read();
map=new byte[is.available()];
is.read(map);
//tạo TiledLayer tl từ những gì đã nhận được.
tl=new TiledLayer(mw,mh,tiles,tw,th);
//lát gạch vào TiledLayer tl.
for(int i=0;i<mh;i++){
for(int j=0;j<mw;j++){
tl.setCell(j, i, map[i*mw+j]);
}}
this.w=w;
this.h=h;
x=w/2-c*tw;
y=h/2-r*th;
//tạo file ảnh cho nhân vật phụ và khởi tạo ot ở vị trí cột 4 và hàng 6 trên map.
other=Image.createImage("/other.png");
ot=new other[3];
for(int i=0;i<ot.length;i++)
{ot[i]=new other(other,(i+1)*12*16,(i+1)*13*16);
Thread.sleep(50);}
}catch(Exception e){}} 
//hàm paint dùng vẽ tl lên Graphics g, và nhận giá trị phím bấm vào hàm move.
void paint(Graphics g,int k){
    move(k);
tl.paint(g);
//vẽ nhân vật phụ lên map.
for(int i=0;i<ot.length;i++)
ot[i].paint(g,this);}
//hàm move nhận giá trị phím bấm k và kiểm tra điều kiện để thay đổi tọa độ của map.
void move(int k){
    //do mỗi lần di chuyển ta cho chuyển 16 pixel, nên ta cần chia ra 4 lần di chuyễn nhỏ để hình ảnh mượt hơn.
    //nếu thấy không cần bước di chuyển nhỏ nào nữa thì cho di gán di chuyển tiếp
if(step==0){
    //gán tọa độ cũ vào các biến gx gy.
    int gx=x,gy=y;
    //tính tọa độ mới.
    switch(k){
        case -1:
            gy+=16;
    break;
    case -2:
            gy-=16;
    break;
    case -3:
            gx+=16;
    break;
    case -4:
            gx-=16;
    break;}
    //kiểm tra xem toa6 độ mới có chạm tường không.
if(check(w/2-gx,h/2-gy)){
this.k=k;
step=4;}
}else{
    //nếu còn lượt di chuyển nhỏ step!=0, thì xét đến thời gian di chuyển lần cuối.
    if(lm<=curr()-30){
    step--;
    switch(this.k){
        case -1:
            y+=th/4;
    break;
        case -2:
            y-=th/4;
    break;
        case -3:
            x+=tw/4;
    break;
        case -4:
            x-=tw/4;
    break;}
    lm=curr();}
}
//gán vị trí cho tl.
tl.setPosition(x, y);
}
  long curr(){return System.currentTimeMillis();}
  //hàm kiểm tra xem có chạm tường không, tường là gạch số 3.
  boolean check(int a,int b){
 return tl.getCell(a/16, b/16)<3;}
}

File other.java : other.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\other.java
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.util.*;

public class other {
    //bao gồm tọa độ của other (x,y), khung hình hiện tại f, phía di chuyển k, step
  int x,y,f=0,k=1;
  //dùng Sprite để tạo hình ảnh nhân vật phụ
  Sprite other;
  //mốc thời gian lần di chuyển cuối và lần đổi hướng cuối.
  long lm,lc;
  //hình ảnh dùng tạo Sprite
  Image im;
  //biến ngẫu nhiên dùng để tạo hướng ngẫu nhiên.
  Random rd=new Random();
  //hàm khởi tạo nhân các tham số hình ảnh và tọa độ ban đầu của other.
  public other(Image im,int x,int y){
  this.x=x;
  this.y=y;
  this.im=im;
  other=new Sprite(im,16,16);
  other.setFrame(f);
  }
  //hàm paint dùng vẽ nhân vật other lên Graphics g.
  void paint(Graphics g,map m){
  move(m);
  other.paint(g);
  }
  //hàm move nhân giá trị là biến map m, sử dụng để điều hướng nhân vật phụ.
  void move(map m){
      //so sánh mốc chuyển hướng cuối cùng để cho cứ 1.5s thì chuyển hướng.
  if(lc<m.curr()-1500){
      //với phép chia dư này, k chỉ nhận giá trị từ 1-4.
  k=Math.abs(rd.nextInt()%4)+1;
  lc=m.curr();}
  //so sánh với lần di chuyển cuối cùng, nếu cách 0.5s thì cho di chuyển.
  if(lm<m.curr()-200){
      f=f%2==0?(k-1)*2+1:(k-1)*2;
      other.setFrame(f);
      //gán tọa độ cũ vào các biến gx, gy.
      int gx=x,gy=y;
      //tính tọa độ mới.
      switch(k){
          case 1:
              y-=4;
      break;
          case 2:
              y+=4;
      break;
          case 3:
              x-=4;
      break;
          case 4:
              x+=4;
      break;}
      lm=m.curr();
      //tọa độ của nhân vật phụ được hình thành từ tọa độ của bản đồ và tọa độ của nhân vật phụ.
  
     //kiểm tra xem tọa độ mới có chạm tường không, nếu có thì cho quay lại tọa độ cũ.
 if(!m.check(x, y))
  {x=gx; y=gy;}
  }
  other.setPosition(x+m.x,y+m.y);
  }
}

Có vài sai sót nhỏ đã được chỉnh lại cho đúng, tại lúc khởi tạo nhân vật phụ trong map.java không phải thêm w/2- và h/2- mà xác định trực tiếp qua cột và hàng mà nhân vật phụ xuất hiện. source jar

một nhân vật phụ họa trong trò chơi

Một nhân vật chạy tới chạy lui, cũng khá là hay và vui mắt, điều này còn làm cho trò chơi trở nên sinh động hơn. Chúng ta sẽ thêm một lớp other.java như sau. other.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\other.java
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.util.*;

public class other {
    //bao gồm tọa độ của other (x,y), khung hình hiện tại f, phía di chuyển k, step
  int x,y,f=0,k=1;
  //dùng Sprite để tạo hình ảnh nhân vật phụ
  Sprite other;
  //mốc thời gian lần di chuyển cuối và lần đổi hướng cuối.
  long lm,lc;
  //hình ảnh dùng tạo Sprite
  Image im;
  //biến ngẫu nhiên dùng để tạo hướng ngẫu nhiên.
  Random rd=new Random();
  //hàm khởi tạo nhân các tham số hình ảnh và tọa độ ban đầu của other.
  public other(Image im,int x,int y){
  this.x=x;
  this.y=y;
  this.im=im;
  other=new Sprite(im,16,16);
  other.setFrame(f);
  }
  //hàm paint dùng vẽ nhân vật other lên Graphics g.
  void paint(Graphics g,map m){
  move(m);
  other.paint(g);
  }
  //hàm move nhân giá trị là biến map m, sử dụng để điều hướng nhân vật phụ.
  void move(map m){
      //so sánh mốc chuyển hướng cuối cùng để cho cứ 1.5s thì chuyển hướng.
  if(lc<m.curr()-1500){
      //với phép chia dư này, k chỉ nhận giá trị từ 1-4.
  k=Math.abs(rd.nextInt()%4)+1;
  lc=m.curr();}
  //so sánh với lần di chuyển cuối cùng, nếu cách 0.5s thì cho di chuyển.
  if(lm<m.curr()-200){
      f=f%2==0?(k-1)*2+1:(k-1)*2;
      other.setFrame(f);
      switch(k){
          case 1:
              y-=4;
      break;
          case 2:
              y+=4;
      break;
          case 3:
              x-=4;
      break;
          case 4:
              x+=4;
      break;}
      lm=m.curr();
  }
  //tọa độ của nhân vật phụ được hình thành từ tọa độ của bản đồ và tọa độ của nhân vật phụ.
  other.setPosition(x+m.x,y+m.y);
  }
}

Và ta sẽ sử dụng hình ảnh này để tạo nhân vật phụ :

Ta sẽ thêm vào trong map.java để sử dụng nhân vật phụ này : map.java
C:\Users\thanhliem\Documents\NetBeansProjects\RPG\src\map.java
import java.io.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;

public class map {
    //các thông số gồm có số cột, số hàng trong map, kích thước mỗi tile tw, th.
    //tọa độ map (x,y), phím bấm k, số lần di chuyển nhỏ.
    int mw,mh,tw,th,x,y,k,step=0;
    //chứa dữ liệu các tile trong map.
    byte[] map;
    //hình ảnh dùng làm tile
    Image tiles,other;
    //dùng class TiledLayer có sẵn trong GameCanvas để tạo map.
    TiledLayer tl;
    //mốc thời gian di chuyển lần cuối
    long lm=0;
    //thêm một biến other ot dùng làm nhân vật phụ.
        other ot;
    //hàm khởi tạo sẽ tải hình ảnh làm tile vào class map.
    public map(String im){
    try{
    this.tiles=Image.createImage("/"+im);
    }catch(Exception e){}}
    //hàm load dùng tải file nguồn và phân tích các thông số trong đó vào map.
    //bây giờ ta có thông số xác định tọa độ ban đầu nhân vật, tại cột c, dòng r và so sánh với độ dài rộng màn hình.
public void load(String fn,int c,int r,int w,int h){
    //file nguồn là dữ liệu với cấu trúc: [số cột]-[số hàng]-[tile width]-[tile height]-[dữ liệu map (số cột * số hàng)]
try{
InputStream is=getClass().getResourceAsStream("/"+fn);
mw=is.read();
mh=is.read();
tw=is.read();
th=is.read();
map=new byte[is.available()];
is.read(map);
//tạo TiledLayer tl từ những gì đã nhận được.
tl=new TiledLayer(mw,mh,tiles,tw,th);
//lát gạch vào TiledLayer tl.
for(int i=0;i<mh;i++){
for(int j=0;j<mw;j++){
tl.setCell(j, i, map[i*mw+j]);
}}
x=w/2-c*tw;
y=h/2-r*th;
//tạo file ảnh cho nhân vật phụ và khởi tạo ot ở vị trí cột 4 và hàng 6 trên map.
other=Image.createImage("/other.png");
    ot=new other(other,w/2-4*16,h/2-6*16);
}catch(Exception e){}} 
//hàm paint dùng vẽ tl lên Graphics g, và nhận giá trị phím bấm vào hàm move.
void paint(Graphics g,int k){
    move(k);
tl.paint(g);
//vẽ nhân vật phụ lên map.
ot.paint(g,this);}
//hàm move nhận giá trị phím bấm k và kiểm tra điều kiện để thay đổi tọa độ của map.
void move(int k){
    //do mỗi lần di chuyển ta cho chuyển 16 pixel, nên ta cần chia ra 4 lần di chuyễn nhỏ để hình ảnh mượt hơn.
    //nếu thấy không cần bước di chuyển nhỏ nào nữa thì cho di gán di chuyển tiếp
if(step==0){
this.k=k;
step=4;
}else{
    //nếu còn lượt di chuyển nhỏ step!=0, thì xét đến thời gian di chuyển lần cuối.
    if(lm<=curr()-30){
    step--;
    switch(this.k){
        case -1:
            y+=th/4;
    break;
        case -2:
            y-=th/4;
    break;
        case -3:
            x+=tw/4;
    break;
        case -4:
            x-=tw/4;
    break;}
    lm=curr();}
}
//gán vị trí cho tl.
tl.setPosition(x, y);
}
  long curr(){return System.currentTimeMillis();}
}

Rồi vậy là xong, kết quả đây :

source jar