我的第一个Java手机程序

原文:http://blog.sina.com.cn/s/blog_56dee71a010007n8.html

下午闲着没事,就看前段时间买的《Java手机游戏实例手册》,还到书上介绍的网站去下载了源代码。可是下载的源代码并不完整,例如第六章的源代码就只有第二节的那个程序,没有后面几节的程序;而且也没有后面讲解中用到的图片。于是只好自己在网上找图片,并且改写例子程序。本来我不太瞧得起这本书的,觉得它太简单了,买它只是想在无聊的时候打发下时间的,但是经过自己改写程序,才发现,要真正动手,才会注意到一些细节问题的;要做多了,才会熟悉。

虽然这个程序在高手看来是过于简单的,但是这个是我学习Java手机游戏编程的第一个自己写的程序,我也不用怕被人笑话,把这么简单的东西放到博客上来,因为学习总是有个过程的嘛。

功能很简单,仅仅是赵云骑着马不停地从屏幕左边走到右边。背景是用小图片拼接起来的,因为我把原图片处理得不好,所以上边一点还看得过去,下边就不对了。

程序由两个文件组成:MyCartoonMidlet.java和MainCanvas.java。

这是MyCartoonMidle.java的内容:

package mycartoon;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.lcdui.Display;

public class MyCartoonMidlet extends MIDlet {
    public MainCanvas m_canvas;

    public MyCartoonMidlet() {
        super();
    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        // TODO 自动生成方法存根
    }

    protected void pauseApp() {
    }

    protected void startApp() throws MIDletStateChangeException {
        m_canvas = new MainCanvas();
        if (null != m_canvas) {
            Display.getDisplay(this).setCurrent(m_canvas);
            while (true) {
                try {
                    Thread.sleep(100);
                    m_canvas.repaint();
                } catch (Exception e) {}
            }
        }
    }
}

这是MainCanvas.java的内容:

package mycartoon;

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

public class MainCanvas extends Canvas {
    public Sprite m_sprite;
    public TiledLayer m_bk;
    public LayerManager m_manager;

    MainCanvas() {
        Image image;
        try {
            /* 创建精灵并居中显示*/
            image = Image.createImage("/zhao_yun.png");

            m_sprite = new Sprite(image, 160, 120);
            m_sprite.setFrame(0);

            int x = 0;
            int y = getHeight() / 2 - 60;
            m_sprite.setPosition(x, y);

            /* 创建背景图层*/
            image = Image.createImage("/bk.jpg");
            m_bk = new TiledLayer(4, 8, image, 60, 40);
            m_bk.setPosition(0, 0);
            setCells();
        } catch (IOException e) {
            e.printStackTrace();
        }

        m_manager = new LayerManager();
        m_manager.append(m_sprite);
        m_manager.append(m_bk);
    }

    /* 设置背景图层的各单元格*/
    private void setCells() {
        int x, y;

        m_bk.setCell(0, 0, 2);
        m_bk.setCell(1, 0, 3);
        m_bk.setCell(2, 0, 4);
        m_bk.setCell(3, 0, 5);

        for (y = 1; y < 8; y++) {
            for (x = 0; x < 4; x++) {
                m_bk.setCell(x, y, 6);
            }
        }
    }

    protected void paint(Graphics g) {
        int n, x;

        n = m_sprite.getFrame();
        n = (n + 1) % 6;
        m_sprite.setFrame(n);

        x = m_sprite.getX() + 10;
        if (x > 240) {
            x = -160;
        }
        m_sprite.setPosition(x, m_sprite.getY());

        /*
                  m_bk.paint(g);
                  m_sprite.paint(g);
         */
        m_manager.paint(g, 0, 0);
    }
}

说明:

这是一个简单的Java Midlet 程序。Mid代表Mobile Information Device。Midlet这个名字大概跟Java Applet的命名类似。Applet程序都需要一个继承自Applet类的类,其中包含init(),destroy()等方法,Midlet程序也很类似地有一个继承自Midlet的类,其中含有startApp(),pauseApp(),destroyApp()等供系统回调的函数。这个还是比较容易理解的。

第23到26行:创建一个MainCanvas对象,并将它设置为当前显示对象。

第27到35行:每隔100毫秒进行一次刷新,实现动画效果。书上用的是while(true),作者实在是水平不咋的啊,难道不知道这样会不必要地使 CPU占用率很高的吗。说句题外话,这书是“‘十一五’全国计算机应用与软件技术专业领域技能型紧缺人才培养培训教材”,我看这书的这种等级刚好适合很多计算机专业刚毕业的学生。如果各种收费很高的IT培训就是用这个作为教材的话,吾窃以为,不值得花钱去参加培训,买本书自学好了。

第17到24行:创建精灵对象(骑马的赵云)并且设置其位置在画布中央。

J2ME提供了Sprite类表示游戏中的精灵对象。Sprite类可以利用一幅图片生成活动的精灵对象。当然,这幅图像就是把动画的各帧放在一起了, Sprite类可以较方便地管理各帧。为了准备这幅图片,我还费了点心思的。

(1) 先从网上找到了一个骑马的赵云的gif动画,第一个问题就是怎么把各帧提取出来,合并成一张图片。在网上搜索了一阵子,发现了一个实用的小工具:png超级伴侣pngmate。它可以提取gif动画的各帧,组合成一张图片,还可以方便地进行各种设置。可惜是个控制台程序。我打算有空的时候为它写一个简单的界面,把它包装起来。

(2) 用pngmate把gif动画转换成一张图片后,先把显示精灵部分的程序写好,运行起来后才发现,精灵的白色背景没有去掉,显示效果不好。在网上搜索了好一阵子,也没有找到合适的工具。最后才发现,原来我一直用的TotalCmd文件管理器就有这个功能,我是有点舍近求远了。

第27到30行:创建背景图层。m_bk = new TiledLayer(4,8,image,60,40);这句中前两个参数是图层的列数和行数,后两个参数是cell的宽度和高度,开始的时候我写好了程序,运行的效果却始终跟我想想的不一样。后来看了Java无线工具包文档才发现是参数的次序写错了。42行的setCells()函数设置各个cell 显示的tiled的编号。Java WTK提供的这个TiledLayer还真是比较好用,实用的。

第37到39行:创建图层管理器,将精灵对象和背景图层加入到图层管理器中。注意添加的次序,是从前往后依次添加的。如果说Z方向是沿屏幕向外的(向着人盯着屏幕看的眼睛,与视线方向相反),也就是沿Z轴负方向的次序依次添加。(这会儿发觉“视线”这个词好像跟“心理学”一样,虽然不正确,但是已经约定俗成了^_^)。

第69到73行:注释掉的部分是不使用图层管理器时候的绘制代码。这个绘制次序跟把对象(图层)添加到图层管理器的次序相反。