用NetBeans开发J2ME游戏实例讲解

       
最近在网上看到一篇关于华容道的
J2ME的开发实例,花了一天时间,在NetBeans上实现了,并且进行了改进和增强,现将经验共享出来,部分代码属原作者所有,在附录中。


 
1. 必须先安装 NetBeans
IDE 4.0

NetBeans Mobility Pack 4.0
,然后才能开始进行 J2ME MIDP 开发。有关下载和安装完整环境的说明,请参见 J2ME MIDP 开发下载页面http://www.netbeans.org/kb/articles/mobility_zh_CN.html  2. 创建 MIDP 应用程序 创建新的 J2ME MIDP 项目  

2. 创建新的移动应用程序:

(1).选择文件”>“新建项目”(Ctrl-Shift-N)。在类别下选择移动。在项目下选择移动应用程序,然后单击下一步    

(2). 项目名称下输入 HuaRongDao。将项目主目录更改为您系统上的任何目录。从现在起,我们将该目录称为 $PROJECTHOME    

(3). 不要选中创建 HelloMIDlet”复选框。单击下一步   CLDC1.0 MIDP1.0,  

(4). J2ME Wireless Toolkit 作为选定的目标平台。    

(5). 单击完成IDE 将创建 $PROJECTHOME./HuaRongDao 项目文件夹。该项目文件夹包含所有的源和项目元数据,如项目 Ant 脚本。此时将在项目窗口中打开
HuaRongDao
项目。                 

(6). 现在,我们来添加一个MIDlet, 右键单击项目,选新建MIDlet,名字为HuaRongDao,不要写package.点确定生成, 然后在生成后的 代码里加入CommandListener支持,代码框架如下:

/*  *
HuaRongDaoMidlet.java  *
    
* Created on
2005
71, 下午8:18 
*/

import javax.microedition.midlet.*;

import
javax.microedition.lcdui.*;

/** 

* 

* @author  lin 

* @version 

*/


 
public
class HuaRongDaoMidlet extends MIDlet implements CommandListener{

     public void startApp()
{    

     }         

      public void pauseApp() {

     }         

     public void
destroyApp(boolean unconditional) {    

     }

    public void
commandAction(Command c, Displayable d) {    

    }     


3. 开始编码

(1)加入退出按钮,这里,我们用TextBox这种高级UI来做例子:

public class HuaRongDaoMidlet extends MIDlet
implements CommandListener{

    private Display
display;

    private final static
Command CMD_EXIT = new Command("
退出", Command.EXIT,
1);

    public HuaRongDaoMidlet(){

        display =
Display.getDisplay(this);

    }

    public void startApp()
{

        TextBox t = new
TextBox("TextBox
的第一个参数","TextBox的第二个参数",256,0);

        t.addCommand(CMD_EXIT);

        t.setCommandListener(this);

        display.setCurrent(mainList);

    }

    …..

    public void
commandAction(Command c, Displayable d) {

        if (c == CMD_EXIT)
{

            destroyApp(false);

            notifyDestroyed();

        }

    }

    }

注意:A.关于j2meapi函数,可以在WTKdocs目录当中查到。

   B.我们使用的是MIDP1.0的函数,2.0支持游戏函数,但是大部分原先的手机都不支持。

   C.TextBox是可输入框,有标题,缺省内容和内容长度等参数。


 
(2)创建一个处理图片的类Images, 处理图片的方式在2.0当中有了很大的改进,可以直接从一张图片中按照坐标取一部分,但是1.0

还没有这个功能,所以我们使用Image数组来实现。

首先,我们先来显示一个图片,来熟悉一下有关image的操作。首先,加入一个Image和包含它的ImageItem,因为Image本身不能显示,

必须包在ImageItem中,然后创建一个Form,把ImageItem加到Form中,最后在屏幕上显示这个Form

    public void startApp()
{

        Image a;

        ImageItem i;

        Form props = new
Form("
测试页");

        try

        {

            a =
Image.createImage("/Duke.png");

            i = new
ImageItem("java
吉祥物",a,ImageItem.LAYOUT_DEFAULT,"图片无法显示");

            props.append(i);

        }

        catch (IOException
e)

        {

            a =
null;

        }

        props.addCommand(CMD_EXIT);

        props.setCommandListener(this);

        display.setCurrent(props);

    }

编译运行一下,发现没有图片,说明或者是指定的图片位置不对或者是系统没有找到,其中,createImage()中的文件路径是关于项目

根目录/res/的,没有错,因此是系统没有找到res目录。 File|"HuaRongDao"property,选择Libraries and
Resources
,把res的完

全路径加进去,再编译就可以了。

好了,测试成功了,现在可以开始编写Images类,如下:

import
javax.microedition.lcdui.*;

import
javax.microedition.midlet.*;


 
/**

 *

 * @author lin

 */


 

public class Images {//保存常量

    //绘图位置常量

    public static final int
UNIT = 20;//
方块的单位长度

    public static final int
LEFT = 20;//
画图的左边界顶点

    public static
final int TOP = 22;//
画图的上边界顶点

    //地图位置常量

    public static final int
WIDTH = 4;//
地图的宽度

    public static final int
HEIGHT = 5;//
地图的高度

    //地图标记常量

    public static final byte
CAOCAO = (byte) ‘a’; //
曹操的地图标记

    public static final byte
MACHAO = (byte) ‘b’;//
马超的地图标记

    public static final byte
HUANGZHONG = (byte) ‘c’;//
黄忠的地图标记

    public static final byte
GUANYU = (byte) ‘d’;//
关羽的地图标记

    public static final byte
ZHANGFEI = (byte) ‘e’;//
张飞的地图标记

    public static final byte
ZHAOYUN = (byte) ‘f’;//
赵云的地图标记

    public static final byte ZU
= (byte) ‘g’;//
卒的地图标记

    public static final byte
BLANK = (byte) ‘h’;//
空白的地图标记

    public static final byte
CURSOR = (byte) ‘i’;//
光标的地图标记

    //地图组合标记常量

    public static final byte
DLEFT = (byte) ‘1’; //
组合图形左边标记

    public static final byte
DUP = (byte) ‘2’; //
组合图形上边标记

    public static final byte
DLEFTUP = (byte) ‘3’; //
组合图形左上标记

    //图片常量

    //public static Image
image_base;//
基本图片

    public static Image
image_Zhaoyun;//
赵云的图片

    public static Image
image_Caocao;//
曹操的图片

    public static Image
image_Huangzhong;//
黄忠的图片

    public static Image
image_Machao;//
马超的图片

    public static Image
image_Guanyu;//
关羽的图片

    public static Image
image_Zhangfei;//
张飞的图片

    public static Image
image_Zu;//
卒的图片

    public static Image
image_Blank;//
空白的图片

    public static Image
image_Frame;//
游戏框架的图片


 

    public Images()
{//
构造函数

    }


 
    public static boolean init() {//初始化游戏中用到的图片

        try {

/*          以下的实现都是基于MIDP2.0的,我们在程序中采用的是基于MIDP1.0的实现

            image_base =
Image.createImage("/huarongroad/BITBACK.png");

            image_Frame =
Image.createImage(image_base, 126, 0, 145, 177,Sprite.TRANS_NONE);

            //Sprite类是用来翻转图片的,是MIDP2.0新新增加的支持游戏的特性

            image_Zhaoyun =
Image.createImage(image_base, 0, 0, UNIT, 2 *
UNIT,Sprite.TRANS_NONE);

            image_Caocao =
Image.createImage(image_base, UNIT, 0, 2 * UNIT,2 * UNIT,
Sprite.TRANS_NONE);

            image_Huangzhong =
Image.createImage(image_base, 3 * UNIT, 0, UNIT,2 *
UNIT,Sprite.TRANS_NONE);

            image_Machao =
Image.createImage(image_base, 0, 2 * UNIT, UNIT,2 *
UNIT,Sprite.TRANS_NONE);

            image_Guanyu =
Image.createImage(image_base, UNIT, 2 * UNIT,2 * UNIT,
UNIT,Sprite.TRANS_NONE);

            image_Zhangfei =
Image.createImage(image_base, 3 * UNIT, 2 * UNIT,UNIT, 2 *
UNIT,Sprite.TRANS_NONE);

            image_Zu =
Image.createImage(image_base, 0, 4 * UNIT, UNIT,
UNIT,Sprite.TRANS_NONE);

            image_Blank =
Image.createImage(image_base, 1 * UNIT, 4 *
UNIT,UNIT,UNIT,Sprite.TRANS_NONE);

*/

            image_Frame =
Image.createImage("/frame.png");

            image_Zhaoyun =
Image.createImage("/zhaoyun.png");

            image_Caocao =
Image.createImage("/caocao.png");

            image_Huangzhong =
Image.createImage("/huangzhong.png");

            image_Machao = Image.createImage("/machao.png");

            image_Guanyu =
Image.createImage("/guanyu.png");

            image_Zhangfei =
Image.createImage("/zhangfei.png");

            image_Zu =
Image.createImage("/zu.png");

            image_Blank =
Image.createImage("/blank.png");

            return
true;

        }catch (Exception ex)
{

            return
false;

        }

    }

}

这里提一下图形格式,一般来说,MIDP1.0只支持png格式,而且对有些png还无法读取,因此在图片做好了以后一定要测试一下,测试方法可以是用NetBeans的调试功能,在运行旁边有一个Debug
按钮,就是进入调试,可以在创建图形的语句处下端点,然后看能否成功。

(3).建立Draw类用来显示图形:

public class Draw {

      /** Creates a new instance of Draw */

    public Draw(Canvas canvas) {

    }

      public static boolean paint(Graphics g, byte img, int x, int y) {

        //在地图的x,y点绘制img指定的图片

        try {

            paint(g, img, x, y, Images.UNIT);//把地图x,y点转化成画布的绝对坐标,绘图

            return true;

        }

        catch (Exception ex) {

            return false;

        }

    }

     public static boolean paint(Graphics g, byte img, int x, int y, int
unit) {

        try {

            switch (img) {

                case Images.CAOCAO://画曹操

                    //变成绝对坐标,并做调整

                    g.drawImage(Images.image_Caocao, Images.LEFT + x *
unit,

                    Images.TOP + y * unit,Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.GUANYU://画关羽

                    g.drawImage(Images.image_Guanyu, Images.LEFT + x *
unit,

                    Images.TOP + y * unit,Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.HUANGZHONG://画黄忠

                    g.drawImage(Images.image_Huangzhong, Images.LEFT + x *
unit,

                    Images.TOP + y * unit,Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.MACHAO://画马超

                    g.drawImage(Images.image_Machao, Images.LEFT + x *
unit,

                    Images.TOP + y * unit, Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.ZHANGFEI://画张飞

                    g.drawImage(Images.image_Zhangfei, Images.LEFT + x *
unit,

                    Images.TOP + y * unit,Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.ZHAOYUN://画赵云

                    g.drawImage(Images.image_Zhaoyun, Images.LEFT + x *
unit,

                    Images.TOP + y * unit,

                    Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.ZU://画卒

                    g.drawImage(Images.image_Zu, Images.LEFT + x * unit,

                    Images.TOP + y * unit, Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.BLANK://画空白

                    g.drawImage(Images.image_Blank, Images.LEFT + x *
unit,

                    Images.TOP + y * unit, Graphics.TOP | Graphics.LEFT);

                    break;

                case Images.CURSOR://画光标

                    g.drawRect(Images.LEFT + x * unit,

                    Images.TOP + y * unit,Images.UNIT,Images.UNIT);

                    break;

            }

            return true;

        }catch (Exception ex) {

            return false;

        }

    }

}

 (4)建立Map类来读取布局信息:

package HuaRongDao;

import java.io.InputStream;

import javax.microedition.lcdui.*;

 /**

 *

 * @author lin

 */

public class Map {

    //处理游戏的地图,负责从外部文件加载地图数据,存放地图数据,并按照地图数据绘制地图

     public byte Grid[][];//存放地图数据

     public Map() {//构造函数,负责初始化地图数据的存储结构

        this.Grid = new byte[Images.HEIGHT][Images.WIDTH];

        //用二维数组存放地图数据,注意第一维是竖直坐标,第二维是水平坐标

    }

     public int[] read_map(int i) {

        //从外部文件加载地图数据,并存放在存储结构中,返回值是光标点的位置

        //参数是加载地图文件的等级

        int[] a = new int[2];//光标点的位置,0是水平位置,1是竖直位置

        try {

            InputStream is =
getClass().getResourceAsStream("/levels/level".concat(String.valueOf(i)));

            if (is != null) {

                for (int k = 0; k < Images.HEIGHT; k++) {

                    for (int j = 0; j < Images.WIDTH; j++) {

                        this.Grid[k][j] = (byte) is.read();

                        if ( this.Grid[k][j] == Images.CURSOR ) {

                            //判断出光标所在位置

                            a[0] = j;//光标水平位置

                            a[1] = k;//光标竖直位置

                            this.Grid[k][j] = Images.BLANK;//将光标位置设成空白背景

                        }

                    }

                    is.read();//读取回车(13),忽略掉

                    is.read();//读取换行(10),忽略掉

                }

                is.close();

            }else {

            //读取文件失败

            a[0] = -1;

            a[1] = -1;

        }

        }catch (Exception ex) {

            //打开文件失败

            a[0] = -1;

            a[1] = -1;

        }

        return a;

    }

     public boolean draw_map(Graphics g) {

        //调用Draw类的静态方法,绘制地图

        try {

            for (int i = 0; i < Images.HEIGHT; i++) {

                for (int j = 0; j < Images.WIDTH; j++) {

                    Draw.paint(g, this.Grid[i][j], j, i);//绘制地图

                }

            }

            return true;

        }catch (Exception ex) {

            return false;

        }

    }

}

注意这里的读文件操作的文件位置同样是相对于res文件夹的。