J2me游戏开发实例讲解(华容道)

J2ME游戏开发实例讲解 文章来源:J2ME开发网一、序言  昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正想借这个机会小试牛刀。选用J2ME的原因还有一个就是目前Java开发大行其到,无线增殖业务迅猛发展,J2ME的应用日渐活跃起来,也希望我的这篇文章能 阅读详情
j2me游戏开发实例讲解
(加入日期:2003-10-10 点击数:1541)
【对此文发表评论】?【编程爱好者论坛】 【保存文章至硬盘】 ?【打印文章】

Borland开发讨论区 微软开发讨论区 C/C++讨论区 新手入门专区


J2ME再现华容道
一.序言
  昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正想借这个机会小试牛刀.选用J2ME作为开发语言还有一个原因就是目前Java开发大行其到,无限增殖业务迅猛发展,J2ME的应用日渐活跃起来,也希望我的这篇文章能够为J2ME知识的普及和开发团队的壮大推波助澜.由于长期受ISO规范的影响,这次小试牛刀我也打算遵照软件工程的要求,并采取瀑布式的开发模式来规划项目,也希望借此机会向各位没有机会参与正式项目开发的读者介绍一下软件开发的流程.
????这里我们先定义项目组的人员体制(其实只有我一个人):技术调研、需求分析、概要设计、详细设计、编码、测试均有笔者一人担任;美工这里我找了个捷径,盗用网上现成的图片,然后用ACDSee把它由BMP转换成PNG格式(我出于讲座的目的,未做商业应用,应该不算侵权吧);至于发布工作,由于缺少OTA服务器,此项工作不做(但是我会介绍这步如何做)。
????接下来,我们规划一下项目实现的时间表,以我个人经验,设想如下:技术调研用2天(这部分解决项目的可行性和重大技术问题,时间会长一些),需求分析用半天(毕竟有现成的东东可以参照,只要理清思路就行了,况且还有很多以前用过的设计模式和写好的代码),概要设计再用半天(有了需求,概要只不够是照方抓药),详细设计要用2天(这一步要把所有的问题想清楚,还要尽可能的准确描述出来),编码用2天(其实1天就够了,技术已经不是问题,多计划出一天来应付突发事件),测试用2天(测试应该至少占全部项目的四分之一,不过这个项目只是一个Demo,也太简单了),发布也要用上半天(尽管我们不去实际发布它,但是还要花点时间搞清楚应该如何做),最后就是项目总结和开庆功会(时间待定)。
二.利其器
   "公欲善其事,必先利其器",做项目之前第一步是前期调研.我们要做的华容道这个东东随处可见,我们要调研的是两个方面:
  1.游戏的内容:游戏本身很简单,就是有几个格子,曹操占据其中一个较大的格子,然后被几个格子包围,这些格子形状不一定相同,但是挡住了曹操移动的方向.游戏者需要挪动这些格子最终把曹操移动到一个指定的位置才算是过关.更具体的分析我们放在后面需求分析和概要设计中讨论.
  2.技术储备:谈到技术,这里简单介绍一下J2ME.Java有三个版本,分别是J2ME(微型版).J2SE(标准版).J2EE(企业版).J2ME是一个标准,采用3层结构设计.最低层是配置层(Configuration)也就是设备层,其上是简表层(Profile),再上是应用层(Application).MIDP就是移动信息设备简表,目前主流手机支持MIDP1.0,最新的是MIDP2.0,它比前一个版本增加了对游戏的支持,在javax.microedition.lcdui.game包中提供了一些类来处理游戏中的技术,比如我们后面会用到的Sprite类,它是用来翻转图片的.权衡再三,笔者决定使用MIDP2.0来做开发.首先需要安装一个J2ME的模拟器,我们就用Sun公司的WTK2.0,我觉得Sun的东西最权威.当然你也可以使用Nokia.Siemens或是Motolora等其他模拟器,但是他们的JDK不尽相同,写出来的程序移植是比较麻烦的.Sun公司的WTK2.0可以到http://here/下载,当然要想成功下载的前提是你要先注册成为Sun的会员(其实这样对你是有好处的).当下来之后就是按照提示一步一步的安装.安装好了之后,我们用一个"Hello World"程序开始你的J2ME之旅.我们启动WTK2.0工具集中的KToolBar,然后点击New Project按钮,在弹出的输入框中输入Project Name为HelloWorld,MIDlet Class Name为Hello,然后点击Create Project,开始生成项目,工具会弹出MIDP配置简表,这里接受生成的默认值(以后还可以修改)点击OK,工具提示我们把写好的Java源程序放到[WTK_HOME]/apps/HelloWorld/src目录之下.我们编辑如下代码,并保存在上述目录之下,文件名为Hello.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Hello extends MIDlet
{
private Display display;
public Hello(){
display =Display.getDisplay(this);
}
public void startApp(){
TextBox t = new TextBox("Hello","Hello",256,0);
display.setCurrent(t);
}
public void pauseApp(){
}
public void destroyApp(boolean unconditional){
}
}
保存好了之后,点击Build按钮,工具会为你编译程序,如无意外再点击Run按钮,会弹出一个手机界面,剩下的就不用我教了吧(用鼠标对手机按键一顿狂点).呵呵,你的第一个J2ME程序已经OK了.什么?你还一点都没懂呢(真是厉害,不懂都能写出J2ME程序来,果然是高手).我这里主要是介绍WTK2.0工具的使用,程序并不是目的,不懂的话后面还会有详细的解说,这里只是带你上路.什么?你不懂Java!那也没有关系,后面我再讲得细一点.
  跳过J2ME,我们先来讲点游戏的理论.具体到华容道这个游戏,主要有三个方面,贴图.游戏操作.逻辑判断.这里讲讲贴图,其他两方面放在概要设计和详细设计里讲.所谓的贴图,其实就是画图,就是在要显示图形的位置上输出一副图片,(要是牵扯到动画就要麻烦一些,可以使用TimerTask.Thread或Rannable之类的技术),这副图片可以是事先准备好的也可以是临时处理的.在J2ME中有一个Image类,专门用于管理图片,它有createImage()方法,可以直接读取图片文件(J2ME只支持PNG格式的图片),也可以截取已有的图片的一部分(这样我们可以把很多图片放在一起,然后一张一张的截下来,好处是节省存储空间和文件读取时间,对于手机这两者都是性能的瓶颈).J2ME还有一个Graphics类,专门用于绘图,它有drawImage()方法,可以把一副图片在指定的位置上显示出来,它还有drawRect()方法和setColor()方法,这两个方法在后面我们进行游戏操作时就会用到,这里先交代一下.有了图片和绘图的方法,还需要知道把图画到谁身上,J2ME提供了一个Canvas类,字面意思就是画布,它有一个paint()方法用于刷新页面,还有一个repaint()方法用于调用paint()方法.听着有些糊涂是吧,不要紧,我来结合具体程序讲解一下.为了今后编程的方便,我们创建两个类Images和Draw,Images用于保存一些常量值和图片,Draw主要是用于画图,这两个类的源代码如下。
Images类的源代码如下:
package huarongroad;

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

public class Images {//保存常量
//绘图位置常量
public static final int UNIT = 32;//方块的单位长度
public static final int LEFT = 10;//画图的左边界顶点
public static final int TOP = 9;//画图的上边界顶点
//地图位置常量
public static final int WIDTH = 4;//地图的宽度
public static final int HEIGHT = 5;//地图的高度
//地图标记常量
public static final byte CAOCAO = (byte) 'a'; file://曹操的地图标记
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'; file://组合图形左边标记
public static final byte DUP = (byte) '2';??file://组合图形上边标记
public static final byte DLEFTUP = (byte) '3'; file://组合图形左上标记
???? //图片常量
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 {
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);

return true;
}catch (Exception ex) {
return false;
}
}
}
Draw类的源代码如下:
package huarongroad;

import javax.microedition.lcdui.*;

public class 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;
}
}
}
其中Images类存的是绘图位置常量(也就是在画图时每个格子的长度和相对坐标原点位置要进行的调整)、地图位置常量(地图的长、宽),地图标记常量(人物对应的记号),地图组合标记常量(后面会细说),图片常量(存放人物的图片);Draw类主要负责在制定的位置画出人物图片。下面我来说说Images类中的地图标记常量和地图组合标记常量。为了能够灵活的安排各个关面的布局,我们决定把游戏布局的信息存储在外部文件中,然后程序启动后把它读进来。这样我们制定了一套存储图片的代码,这就是地图标记常量,如上面Images类中定义的Caocao(曹操)用a字符来表示,当程序读到a字符时就能将它转化成曹操对应的图片,并在读到a字符的位置上进行显示。但是从实际观察中我们发现所有的图片并不是统一大小的,有的占4个格子,有的占2个格子,还有的占1个格子,而且即便同是占两个格子的图片还有横、竖之分。有鉴于此,我们引入了地图组合标记常量,就是说在遇到占有多个格子的时候,值1(也就是Images.LEFT)表示它的左边是一个真正的地图标记,值2(也就是Images.UP)表示它的上边是一个真正的地图标记,值1(也就是Images.LEFTUP)表示它的左上边是一个真正的地图标记。地图组合标记常量其实就是用来占位置的,与实际显示无关,当后面我们将到移动时还会再来分析组合标记的使用。
Draw类主要是用来在画布上画出图形,它有两个paint方法,这是很常见的函数重载。但是程序中实际上只用到了4个参数的paint方法,它直接获得要画图片的相对坐标位置信息,然后调用5个参数的paint方法。5个参数的paint方法将相对坐标位置信息转换成绝对位置,并实际调用Graphics.drawImage()方法,将Images中的图片画了出来。这种实现方法的好处是灵活和便于扩展,但你需要画图的位置并不能够对应到格子中的相对坐标位置时,你就可以直接调用5个参数的paint方法,而不必再去修改这各类;但你添加新的图片时,只要在Images中增加对应的常量,然后向Draw中5个参数的paint方法添加一条处理就可以了。
????写到这里,两天的时间刚好用完。
三、需求分析
????这部分叫做需求分析,听起来挺吓人的,其实就是搞清楚我们要做什么,做成什么样,那些不做。下面我引领着大家共同来完成这一步骤。首先,我们要做一个华容道的游戏,华容道的故事这里不再赘述了,但其中的人物在这里限定一下,如上面Images类里的定义,我们这个版本只提供曹操(Caocao)、关羽(Guanyu)、张飞(Zhangfei)、赵云(Zhaoyun)、黄忠(Huangzhong)、马超(Machao)和卒(Zu)。我们这里也限定一下游戏的操作方法:首先要通过方向键选择一个要移动的区域(就是一张图片),被选择的区域用黑色方框框住;选好后按Fire键(就是确定键)将这块区域选中,被选中的区域用绿色方框框住;然后选择要移动到的区域,此时用红色方框框住被选择的区域;选好要移动到的区域之后按Fire键将要移动的区域(图片)移到要移动到的区域,并去掉绿色和红色的方框。这里需要强调的概念有选择的区域、选中的区域、要移动的区域和要移动到的区域,这四个概念请读者注意区分,当然也应当把这一部分记入数据字典之中。为了使文章的重点突出(介绍如何制作一个J2ME的收集游戏),我们这里限定一些与本主题无关的内容暂不去实现:过关之后的动画(实现时要用到TimerTask或Thread类,后续的系列文章中我会详细介绍动画方面的知识)、关面之间的切换(其实很简单,当完成任务之后重新再做一边)、暂停和保存等操作(这部分的内容介绍的资料很多,我也写不出什么新的东东来,难免抄袭,故此免掉)。
????需求分析基本完成,离下午还有一段时间,马上动手用ACDSee把从网上找来的BMP文件,调整其大小为271*177(我的这个图片是两个部分合在一起,所以比手机实际屏幕大了),另存为PNG格式。半天时间刚刚好,不但搞清楚了要做的东东,还把要用的图片准备好了。
四、概要设计
????概要设计是从需求分析过渡到详细设计的桥梁和纽带,这一部分中我们确定项目的实现方法和模块的划分。我们决定将整个项目分成五个部分,分别是前面介绍的Images、Draw,还有Map和Displayable1和MIDlet1。Images和Draw类功能简单、结构固定,因此很多项目我们都使用这两各类,这里直接拿来改改就能用了,前面已经介绍过这里不再赘述。Map类是用来从外部文件读入地图,然后保存在一个数组之中,这部分的内容是我们在本阶段讨论的重点。Displayable1是一个继承了Canvas类的画布,它用来处理程序的主要控制逻辑和一部分控制逻辑所需的辅助函数,主要函数应该包括用来绘图的paint()函数、用来控制操作的keyPressed()函数、用来控制选择区域的setRange()函数、用来控制选择要移动到区域的setMoveRange()函数、用来移动选中区域的Move()函数和判断是否完成任务的win()函数,更具体的分析,我们放到详细设计中去细化。MIDlet1实际上就是一个控制整个J2ME应用的控制程序,其实也没有什么可特别的,它和我们前面介绍的"Hello World"程序大同小异,这里就不展开来说了,后面会贴出它的全部代码。
????Map类主要应该有一个Grid[][]的二维数组,用来存放华容道的地图,还应该有一个read_map()函数用来从外部文件读取地图内容填充Grid数据结构,再就是要有一个draw_map()函数用来把Grid数据结构中的地图内容转换成图片显示出来(当然要调用Draw类的paint方法)。说到读取外部文件,笔者知道有两种方法:一种是传统的定义一个InputStream对象,然后用getClass().getResourceAsStream()方法取得输入流,然后再从输入流中取得外部文件的内容,例如
InputStream is = getClass().getResourceAsStream("/filename");
if (is != null) {
????byte a = (byte) is.read();
}
这里请注意文件名中的根路径是相对于便以后的class文件放置的位置,而不是源文件(java)。第二种方法是使用onnector.openInputStream方法,然后打开的协议是Resource,但是这种方法笔者反复尝试都没能调通,报告的错误是缺少Resource协议,估计第二种方法用到J2ME的某些扩展类包,此处不再深究。由于以前已经做过一些类似华容道这样的地图,这里直接给出Map类的代码,后面就不再详细解释Map类了,以便于我们可以集中精力处理Displayable1中的逻辑。Map类的代码如下:
package huarongroad;

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

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

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

public Map() {//构造函数,负责初始化地图数据的存储结构
this.Grid = new byte[Images.HEIGHT][Images.WIDTH];
//用二维数组存放地图数据,注意第一维是竖直坐标,第二维是水平坐标
}

public int[] read_map(int i) {
????????file://从外部文件加载地图数据,并存放在存储结构中,返回值是光标点的位置
//参数是加载地图文件的等级
????????int[] a = new int[2];//光标点的位置,0是水平位置,1是竖直位置
try {
InputStream is = getClass().getResourceAsStream(
"/huarongroad/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;
}
}
}
????对于像华容道这样的小型地图可以直接用手工来绘制地图的内容,比如:
fa1c
2232
bd1e
2gg2
gihg
但是,如果遇到像坦克大战或超级玛莉那样的地图,就必须另外开发一个地图编辑器了(我会在后续的文章中介绍用vb来开发一个地图编辑器)。
看看时间,刚刚好有过了半天。休息,休息一下,明天再见。
五、详细设计
????详细设计是程序开发过程中至关重要的一个环节,好在我们在前面的各个阶段中已经搭建好了项目所需的一些工具,现在这个阶段中我们只需集中精力设计好Displayable1中的逻辑。(两天的时间当然不只干这点活,还要把其他几个类的设计修改一下)
????Displayable1这个类负责处理程序的控制逻辑。首先,它需要有表示当前关面的变量level、表示当前光标位置的变量loc、表示要移动区域的变量SelectArea、表示要移动到的区域的变量MoveArea、表示是否已有区域被选中而准备移动的变量Selected和Map类的实例MyMap。然后,我们根据用户按不同的键来处理不同的消息,我们要实现keyPressed()函数,在函数中我们处理按键的上下左右和选中(Fire),这里的处理需要我展开来讲一讲,后面我很快会把这一部分详细展开。接下来,是实现paint()函数,我们打算在这一部分中反复的重画背景、地图和选择区域,这个函数必须处理好区域被选中之后的画笔颜色的切换,具体讲就是在没有选中任何区域时要用黑色画笔,当选重要移动的区域时使用绿色画笔,当选择要移动到的区域时改用红色画笔(当然附加一张流程图是必不可少的)。再下面要实现的setRange()函数和setMoveRange()函数,这两个函数用来设置要移动的区域和要移动到的区域,我的思路就是利用前面在Images类中介绍过的地图组合标记常量,当移动到地图组合标记常量时,根据该点地图中的值做逆向变换找到相应的地图标记常量,然后设置相应的loc、SelectArea和MoveArea,其中setMoveRange()函数还用到了一个辅助函数isInRange(),isInRange()函数是用来判断给定的点是否在已选中的要移动的区域之内,如果isInRange()的返回值是假并且该点处的值不是空白就表明要移动到的区域侵犯了其他以被占用的区域。有了setRange()和setMoveRange()函数,Move()函数就水到渠成了,Move()函数将要移动的区域移动到要移动到的区域,在移动过程中分为三步进行:第一.复制要移动的区域,第二.将复制出的要移动区域复制到要移动到的区域(这两步分开进行的目的是防止在复制过程中覆盖掉要移动的区域),第三.用isInRange2()判断给定的点是否在要移动到的区域内,将不在要移动到的区域内的点设置成空白.
????下面我们详细的分析一下keyPressed()函数的实现方法:首先,keyPressed()函数要处理按键的上下左右和选中(Fire),在处理时需要用Canvas类的getGameAction函数来将按键的键值转换成游戏的方向,这样可以提高游戏的兼容性(因为不同的J2ME实现,其方向键的键值不一定是相同的).接下来,分别处理四个方向和选中.当按下向上时,先判断是否已经选定了要移动的区域(即this.selected是否为真),如果没有选中要移动区域则让光标向上移动一格,然后调用setRange()函数设置选择要移动的区域,再调用repaint()函数刷新屏幕,否则如果已经选中了要移动的区域,就让光标向上移动一格,然后调用setMoveRange()函数判断是否能够向上移动已选中的区域,如果能移动就调用repaint()函数刷新屏幕,如果不能移动就让光标向下退回到原来的位置.当按下向下时,先判断是否已经选定了要移动的区域,如果没有选中要移动的区域则判断当前所处的区域是否为两个格高,如果是两个格高则向下移动两格,如果是一个格高则向下移动一格,接着再调用setRange()函数设置选择要移动的区域,而后调用repaint()函数刷新屏幕,否则如果已经选中了要移动的区域,就让光标向下移动一格,然后调用setMoveRange()函数判断是否能够向下移动已选中的区域,如果能移动就调用repaint()函数刷新屏幕,如果不能移动就让光标向上退回到原来的位置.按下向左时情况完全类似向上的情况,按下向右时情况完全类似向下的情况,因此这里不再赘述,详细情况请参见程序的源代码.当按下选中键时,先判断是否已经选中了要移动的区域,如果已经选中了要移动的区域就调用Move()函数完成由要移动的区域到要移动到的区域的移动过程,接着调用repaint()函数刷新屏幕,然后将已选择标记置成false,继续调用win()函数判断是否完成了任务,否则如果还没有选定要移动的区域则再判断当前选中区域是否为空白,如果不是空白就将选中标记置成true,然后刷新屏幕.这里介绍一个技巧,在开发程序遇到复杂的逻辑的时候,可以构造一格打印函数来将所关心的数据结构打印出来以利调试,这里我们就构造一个PrintGrid()函数,这个函数纯粹是为了调试之用,效果这得不错.至此我们完成了编码前的全部工作.
????看看时间还早,我在这里简单介绍一下MIDlet1类,这个类和前面介绍的"Hello World"中的内容大同小异,先看一下程序代码:



其中startApp().pauseApp()和destroyApp()三个函数时MIDlet类的三个抽象方法必须要被重载一边,他们控制着一个J2ME程序的开始.暂停和结束.而其中的Displayable1 就是我们前面提到的Displayable1的一个实例,用来负责显示程序的用户界面和控制程序逻辑.这个类一般都是写成这样.
????两天的时间稍稍还有些富裕,今天可以早放工.
六.编码
????整个项目共有五个类,有四个类的代码前面已经介绍过了,而且是在其他项目中使用过的相对成熟的代码.现在只需全力去实现Displayable1类.Displayable1类的代码如下:
package huarongroad;

import javax.microedition.lcdui.*;

public class Displayable1 extends Canvas implements CommandListener {

????private int[] loc = new int[2]; file://光标的当前位置,0是水平位置,1是竖直位置
????private int[] SelectArea = new int[4];//被选定的区域,即要移动的区域
????private int[] MoveArea = new int[4];//要移动到的区域
????private Map MyMap = new Map();//地图类
????private boolean selected;//是否已经选中要移动区域的标志
????private int level;//但前的关面
????public Displayable1() {//构造函数
try {
????jbInit();//JBuilder定义的初始化函数
}catch (Exception e) {
????e.printStackTrace();
}
????}
????private void Init_game(){
//初始化游戏,读取地图,设置选择区域,清空要移动到的区域
this.loc = MyMap.read_map(this.level);//读取地图文件,并返回光标的初始位置
//0为水平位置,1为竖直位置
this.SelectArea[0] = this.loc[0];//初始化选中的区域
????????this.SelectArea[1] = this.loc[1];
????????this.SelectArea[2] = 1;
????????this.SelectArea[3] = 1;
????????this.MoveArea[0] = -1;//初始化要移动到的区域
????????this.MoveArea[1] = -1;
????????this.MoveArea[2] = 0;
????????this.MoveArea[3] = 0;
}
????private void jbInit() throws Exception {//JBuilder定义的初始化函数
????????file://初始化实例变量
????????this.selected = false;//设置没有被选中的要移动区域
this.level = 1;
????????Images.init();//初始化图片常量
Init_game();//初始化游戏,读取地图,设置选择区域,清空要移动到的区域
setCommandListener(this);//添加命令监听,这是Displayable的实例方法
addCommand(new Command("Exit", Command.EXIT, 1));//添加“退出”按钮
????}

????public void commandAction(Command command, Displayable displayable) {
//命令处理函数
if (command.getCommandType() == Command.EXIT) {//处理“退出”
????MIDlet1.quitApp();
}
????}

????protected void paint(Graphics g) {
//画图函数,用于绘制用户画面,即显示图片,勾画选中区域和要移动到的区域
try {
????g.drawImage(Images.image_Frame, 0, 0,
Graphics.TOP | Graphics.LEFT);//画背景
????????????MyMap.draw_map(g);//按照地图内容画图
????if ( this.selected )
g.setColor(0,255,0);//如果被选中,改用绿色画出被选中的区域
????g.drawRect(this.SelectArea[0] * Images.UNIT + Images.LEFT,
???????????????this.SelectArea[1] * Images.UNIT + Images.TOP,
???????this.SelectArea[2] * Images.UNIT,
???????this.SelectArea[3] * Images.UNIT);//画出选择区域,
?????????????????????????????????????????????????file://如果被选中,就用绿色
?????????????????????????????????????????????????????????file://否则,使用黑色
????????????g.setColor(255,255,255);//恢复画笔颜色
????if (this.selected) {//已经选中了要移动的区域
g.setColor(255, 0, 255);//改用红色
g.drawRect(this.MoveArea[0] * Images.UNIT + Images.LEFT,
???this.MoveArea[1] * Images.UNIT + Images.TOP,
???this.MoveArea[2] * Images.UNIT,
???this.MoveArea[3] * Images.UNIT);//画出要移动到的区域
??? ????????g.setColor(255, 255, 255);//恢复画笔颜色
????}
}catch (Exception ex) {
}
System.out.println(Runtime.getRuntime().freeMemory());
System.out.println(Runtime.getRuntime().totalMemory());
????}

????private void setRange() {
//设置移动后能够选中的区域
//调整当前光标位置到地图的主位置,即记录人物信息的位置
if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DLEFT) {
????this.loc[0] -= 1;//向左调
}else if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DUP) {
????this.loc[1] -= 1;//向上调
}else if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DLEFTUP) {
????this.loc[0] -= 1;//向左调
????this.loc[1] -= 1;//向上调
}
this.SelectArea[0] = this.loc[0];//设置光标的水平位置
this.SelectArea[1] = this.loc[1];//设置光标的竖直位置
//设置光标的宽度
if (this.loc[0] + 1 < Images.WIDTH) {
????this.SelectArea[2] = this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] != (byte) '1' ?
???1 : 2;
}else {
????this.SelectArea[2] = 1;
}
//设置光标的高度
if (this.loc[1] + 1 < Images.HEIGHT) {
????this.SelectArea[3] = this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] != (byte) '2' ?
?????? ???1 : 2;
}else {
????this.SelectArea[3] = 1;
}
????}

????private boolean setMoveRange() {
//设置要移动到的区域,能够移动返回true,否则返回false
for (int i = 0; i < this.SelectArea[2]; i++) {
???for (int j = 0; j < this.SelectArea[3]; j++) {
???????if (this.loc[1] + j >= Images.HEIGHT ||
???this.loc[0] + i >= Images.WIDTH ||
???(!isInRange(this.loc[0] + i, this.loc[1] + j) &&
????this.MyMap.Grid[this.loc[1] + j][this.loc[0] + i] !=
????Images.BLANK)) {
??????return false;
}
???}
}
this.MoveArea[0] = this.loc[0];
this.MoveArea[1] = this.loc[1];
this.MoveArea[2] = this.SelectArea[2];
this.MoveArea[3] = this.SelectArea[3];
return true;
????}

????private boolean isInRange(int x, int y) {
//判断给定的(x,y)点是否在选定区域之内,x是水平坐标,y是竖直坐标
if (x >= this.SelectArea[0] &&
????x < this.SelectArea[0] + this.SelectArea[2] &&
????y >= this.SelectArea[1] &&
????y < this.SelectArea[1] + this.SelectArea[3]) {
return true;
}else {
return false;
}
????}

????private boolean isInRange2(int x, int y) {
//判断给定的(x,y)点是否在要移动到的区域之内,x是水平坐标,y是竖直坐标
if (x >= this.MoveArea[0] &&
????x < this.MoveArea[0] + this.MoveArea[2] &&
????y >= this.MoveArea[1] &&
???? ????y < this.MoveArea[1] + this.MoveArea[3]) {
return true;
}else {
return false;
}
????}

????protected void keyPressed(int keyCode) {
//处理按下键盘的事件,这是Canvas的实例方法
switch (getGameAction(keyCode)) {//将按键的值转化成方向常量
??? ????case Canvas.UP://向上
if (!this.selected) {//还没有选定要移动的区域
??? ????if (this.loc[1] - 1 >= 0) {//向上还有移动空间
this.loc[1]--;//向上移动一下
setRange();//设置光标移动的区域,该函数能将光标移动到地图主位置
repaint();//重新绘图
????}
}else {//已经选定了要移动的区域
????if (this.loc[1] - 1 >= 0) {//向上还有移动空间
this.loc[1]--;//向上移动一下
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域
????repaint();//重新绘图
}else {//不能移动
????this.loc[1]++;//退回来
}
????}
}
break;
????case Canvas.DOWN://向下
if (!this.selected) {//还没有选定要移动的区域
????if (this.loc[1] + 1 < Images.HEIGHT) {//向下还有移动空间
if (this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] ==
????Images.DUP){//该图片有两个格高
this.loc[1]++;//向下移动一下
if (this.loc[1] + 1 < Images.HEIGHT) {//向下还有
??????????????????????????????file://移动空间
????this.loc[1]++;//向下移动一下
????setRange();//设置光标移动的区域,
???????file://该函数能将光标移动到地图主位置
????repaint();//重新绘图
}else {//向下没有移动空间
????this.loc[1]--;//退回来
}
}else {//该图片只有一个格高
this.loc[1]++;//向下移动一下
setRange();//设置光标移动的区域,
???????????file://该函数能将光标移动到地图主位置
repaint();//重新绘图
}
????}else {
???? ????}
}else {//已经选定了要移动的区域
????if (this.loc[1] + 1 < Images.HEIGHT) {//向下还有移动空间
this.loc[1]++;//向下移动一下
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域
????repaint();//重新绘图
}else {//不能移动
????this.loc[1]--;//退回来
}
????}
}
break;
????case Canvas.LEFT://向左
if (!this.selected) {//还没有选定要移动的区域
????????????if (this.loc[0] - 1 >= 0) {//向左还有移动空间
this.loc[0]--;//向左移动一下
setRange();//设置光标移动的区域,该函数能将光标移动到地图主位置
repaint();//重新绘图
????}
}else {//已经选定了要移动的区域
????if (this.loc[0] - 1 >= 0) {//向左还有移动空间
this.loc[0]--;//向左移动一下
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域
????repaint();//重新绘图
}else {//不能移动
????this.loc[0]++;//退回来
}
????}
}
break;
????case Canvas.RIGHT://向右
if (!this.selected) {//还没有选定要移动的区域
??? ????if (this.loc[0] + 1 < Images.WIDTH) {//向右还有移动空间
if (this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] ==
??? ????Images.DLEFT) {//该图片有两个格宽
this.loc[0]++;//向右移动一下
if (this.loc[0] + 1 < Images.WIDTH) {//向右还有
??????????????????????????????file://移动空间
??? ????this.loc[0]++;//向右移动一下
????setRange();//设置光标移动的区域,
???????file://该函数能将光标移动到地图主位置
????repaint();//重新绘图
}else {//向右没有移动空间
????this.loc[0]--;//退回来
}
}else {//该图片只有一个格宽
this.loc[0]++;//向右移动一下
setRange();//设置光标移动的区域,
???????????file://该函数能将光标移动到地图主位置
repaint();//重新绘图
}
????}else {
??? ????}
}else {//已经选定了要移动的区域
????if (this.loc[0] + 1 < Images.WIDTH) {//向右还有移动空间
this.loc[0]++;//向右移动一下
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域
????repaint();//重新绘图
}else {//不能移动
????this.loc[0]--;//退回来
}
????}
}
break;
????case Canvas.FIRE:
if (this.selected) {//已经选定了要移动的区域
??? ????Move();//将要移动的区域移动到刚选中的区域
????repaint();//重新绘图
????this.selected = false;//清除已选定要移动区域的标志
????????????????????if ( win()) {
System.out.println("win");
????}
}else {//还没有选定要移动的区域
????if (this.MyMap.Grid[this.loc[1]][this.loc[0]] ==
Images.BLANK) {//要移到的位置是一个空白
????}else {//要移到的位置不是空白
this.selected = true;//设置已选定要移动区域的标志
????}
????????????????????repaint();//重新绘图
????????????????}
break;
}
????}

????private boolean win(){
????????file://判断是否已经救出了曹操
if ( this.MyMap.Grid[Images.HEIGHT - 2 ][Images.WIDTH - 3 ] == Images.CAOCAO )
????????????return true;
????????else
????????????return false;
????}

????private void PrintGrid(String a) {
????????file://打印当前地图的内容,用于调试
System.out.println(a);
for (int i = 0; i < Images.HEIGHT; i++) {
????for (int j = 0; j < Images.WIDTH; j++) {
System.out.print( (char)this.MyMap.Grid[i][j]);
????}
????System.out.println("");
}
????}

????private void Move() {
????????file://将要移动的区域移动到刚选中的区域
if (this.MoveArea[0] == -1 || this.MoveArea[1] == -1 ||
?? ????this.SelectArea[0] == -1 || this.SelectArea[1] == -1) {//没有选中区域
}else {//已经选中了要移动的区域和要移动到的区域
????byte[][] temp = new byte[this.SelectArea[3]][this.SelectArea[2]];
??? ????file://复制要移动的区域,因为这块区域可能会被覆盖掉
????for (int i = 0; i < this.SelectArea[2]; i++) {
for (int j = 0; j < this.SelectArea[3]; j++) {
? ????temp[j][i] =
this.MyMap.Grid[this.SelectArea[1] +j]
[this.SelectArea[0] + i];
}
????}
????file://PrintGrid("1"); // 调试信息
????file://将要移动的区域移动到刚选中的区域(即要移动到的区域)
????for (int i = 0; i < this.SelectArea[2]; i++) {
for (int j = 0; j < this.SelectArea[3]; j++) {
????this.MyMap.Grid[this.MoveArea[1] + j]
[this.MoveArea[0] + i] = temp[j][i];
}
????}
????file://PrintGrid("2");// 调试信息
????file://将要移动的区域中无用内容置成空白
????for (int i = 0; i < this.SelectArea[3]; i++) {
? for (int j = 0; j < this.SelectArea[2]; j++) {
????if (!isInRange2(this.SelectArea[0] + j,
????????????this.SelectArea[1] + i)) {//该点是不在要移动到
??????????????????????????????????????file://的区域之内,需置空
????????????????????????this.MyMap.Grid[this.SelectArea[1] + i]
[this.SelectArea[0] + j] = Images.BLANK;
????}else {
??? ????}
}
????}
????file://PrintGrid("3");// 调试信息
????this.SelectArea[0] = this.MoveArea[0];//重置选中位置的水平坐标
????this.SelectArea[1] = this.MoveArea[1];//重置选中位置的竖直坐标
????????????this.MoveArea[0] = -1;//清空要移动到的位置
????????????this.MoveArea[1] = -1;//清空要移动到的位置
????this.MoveArea[2] = 0;//清空要移动到的位置
????????????this.MoveArea[3] = 0;//清空要移动到的位置
}
????}
}
????代码的相关分析,在详细设计阶段已经讲过,代码中有比较相近的注释,请读者自行研读分析.将全部的代码写好,用wtk2.0自带的Ktoolbar工具建立一个工程,接下来把去不源文件放到正确位置下,然后点击build,再点run,就完成了程序的编写.当然如果有错误还要修改和调试.
????经过两天的痛苦煎熬,程序终于有了眉目,编码阶段初战告捷.
七、测试
?????作为一个真正的产品要经过单体测试、结合测试和系统测试。由于项目本身简单,而且大部分代码已经是相对成熟的,我们跳过单体测试;又由于笔者的实际环境所限,无法搞到Java手机,无法架设OTA服务器,因此我们也只能放弃系统测试。那么就让我们开始结合测试吧。测试之前要先出一个测试式样书,也就是测试的计划。我们将它简化一下,只测试如下几种情况:第一、对各种形状的区域的选择和移动;第二、临近边界区域的选择和移动;第三、同一区域的反复选择和反复移动;第四、非法选择和非法移动。有了测试的目标,接下来的工作就是用wtk2.0自带的Run MIDP Application工具进行测试。打开这个工具,加载huarongRoad的jad文件,程序就会自动运行,选择launch上MIDlet1这个程序,华容道游戏就会跃然屏幕之上,接下来的工作就是左三点.右三点,拇指扭扭,来做测试。测试过程中发现任何的问题,立刻发一个bug票给自己,然后就又是痛苦的调试和修正bug,如此如此。
????两日过后,程序日渐成熟起来,笔者也日渐消瘦下去,还好熬了过来。
八.发布
????谈到发布,其实是个关键,再好的产品不能很好的发布出去也只是个产品而已,变不成商品也就得不到回报.由于笔者的条件所限,这里只能是纸上谈兵,不过还是希望能够使读者对这一过程有所了解(网上的资料也很多)。
????J2ME的程序发布一般都是通过OTA(Over The Air),你只需要一台有公网IP的主机和一个普通的web Server就可以了(尽管要求很低,但笔者还是没有),这里我们以apache为例介绍一下OTA服务的配置,首先是安装好了apache服务器,然后在conf目录下找到mime.types文件,在该文件中加入如下两行
application/java-archive??????????????????????????jar
text/vnd.sun.j2me.app-descriptor??????????????????jad
然后重起apache服务器就可以了。接下来的工作就是修改jad文件中MIDlet-Jar-URL:后面的参数,将它改为URL的绝对路径,即http://***/huarongroad.jar(其中***是你的域名或IP地址)。在下面就是用java手机下载jad文件,它会自动部署相应的jar文件并加载它。剩下的工作就和在模拟器上操作是一样的了。
九、项目总结
????至此,我们已经完成了一个J2ME游戏的全部开发过程,程序中涉及到了调研、分析、设计、编码、测试和发布等方面的问题,其实在实际的工作中还有很多更为具体的问题,毕竟技术只在软件开发过程中占据很有限的一部分,这里限于篇幅的限制无法一一具体展开。今后,笔者计划再写一篇使用J2ME开发手机屏保的文章,借此机会向读者展示J2ME动画技术;然后再写一篇J2ME网络应用的文章,做一个类似开心辞典那样的知识问答游戏,以便向读者展示J2ME的网络技术;待这两方面的技术交待清楚之后,我将引领读者制作一个稍大一些的游戏。
????通过本文,笔者尽力向大家展示J2ME游戏开发的全貌,但限于水平和经验的问题,文中不当之处还望广大读者不吝指教。我的联系方式MSN:dong_peng_eng@eyou.com.
基于Android系统的华容道游戏的开发 这款华容道游戏设计是在Android Studio软件环境下,使用Java语言编程实现的可以在Android系统上运行的华容道游戏。 立即下载

相关推荐

3种六轴IMU姿态解算算法对比:互补滤波、Mahony与卡尔曼滤波性能实测

本文深入对比了三种六轴IMU姿态解算算法:互补滤波、Mahony与卡尔曼滤波的性能表现。通过实测数据分析了各算法在精度、计算复杂度和适用场景上的差异,为无人机、机器人等运动控制系统提供了选型建议。重点探讨了六轴陀螺仪与加速度传感器的数据融合技术,帮助开发者根据项目需求选择最优解算方案。

weixin_29054245的博客 337

Java小白实现数字华容道

本人是一名Java小白(有其他语言的基础),最近在跟着网上的资源学习,今天做了一个初步的数字华容道游戏,觉得很有意思,做完也很有成就感,记录一下开发过程啦 哈哈哈。

2303_79532367的博客 2526

机器视觉:9点标定的原理与实现

标定就是将机器视觉处理得到的像素坐标转换成实际项目中使用到的毫米坐标。简单说即使看看实际单位距离内有几个像素,如下图所示,10mm的距离内有222个像素,那像素坐标和实际的毫米坐标就有个比例关系了。

m0_49968063的博客 1万+

JAVA开发的华容道游戏

 import java.awt.*;import java.applet.*;import java.awt.event.*;class People extends Button implements FocusListener  //代表华容道人物的类。{  Rectangle rect=null;   int left_x,left_y;//按扭的左上角坐标.   int widt

Canding lin的专栏 3140

基于SHIS凝视型短波红外高光谱相机(900-1700nm)区分不同宝石的差异性和可分性(宝石分选)

SHIS系列凝视型高光谱相机基于液晶可调谐滤波器(LCTF),通过电控LCTF进行连续细分窄波段分光,对每个波段进行高分辨率的二维面阵成像,进行图像处理后,获得完整的高光谱高清图像序列。具有低功耗、小型化、使用方便和易于扩展的优势。其工作波段覆盖400nm至1000nm,能够捕捉宝石在不同波长下的光谱特征,为宝石鉴定提供了新的工具。

wayho的博客 709

《Long-CLIP: Unlocking the Long-Text Capability of CLIP》

总的来说,Long-CLIP是一个改进的视觉-语言预训练模型,它通过知识保留的位置上插值和主要成分匹配策略,有效解决了原始CLIP模型在处理长文本输入时的限制。这一模型不仅在长文本图像检索任务中表现出色,而且在零样本图像分类任务中保持了与原始CLIP相当的性能,同时还能以即插即用的方式增强图像生成任务中的长文本处理能力。

Angelina_Jolie的博客 1379

MarkDown 流程图使用

基本st=>start: 开始 e=>end: 结束 op1=>operation: 获取鼠标点击位置 sub1=>subroutine: 继续点击 cond=>condition: 两个点及以上 io=>inputoutput: 拟合直线st->op1->cond cond(yes)->io->e cond(no)->sub1(right)->op1Created with Raphaël 2.1

华容道的博客 493

基于集成运放心电放大器设计(原创)

原创作者:einyboy or alert 最近在看美剧《危机边缘》关于边缘科学的案件,科技控可以看一下,但建议不在吃东西的时候看。想像力是创造的思想源头,动手能力则是创造的车床。 自认为是没有想像力的那类,只能发挥动手能力了。 心电测量简介 图1 心电极接线图 如图1心电测试一般使用3个心电极进行测量,各个电极的说明如下: RA: Right Arm缩写,右手电极 LA: L...

huandaohack的专栏 6924

基于java的华容道小游戏

一、华容道游戏简介华容道,古老的中国游戏,以其变化多端、百玩不厌的特点与魔方、独立钻石棋一起被国外智力专家并称为“智力游戏界的三个不可思议”。它与七巧板、九连环等中国传统益智玩具还有个代名词叫作“中国的难题”。华容道游戏取自著名的三国故事,曹操在赤壁大战中被刘备和孙权的“苦肉计”、“火烧连营”打败,被迫退逃到华容道,又遇上诸葛亮的伏兵,关羽为了报答曹操对他...

qq_38474871的博客 854

MR2数据集实战:5步搞定多模态谣言检测模型训练(附完整代码)

本文详细介绍了如何使用MR2多模态数据集构建高精度谣言检测系统,涵盖环境配置、数据准备、多模态特征工程、模型架构设计、训练调优及部署优化等5个关键步骤。通过双线性融合技术处理文本、图像和网页三模态数据,解决特征对齐和模态冲突等工程难题,并提供完整代码实现和性能优化方案。

weixin_42657024的博客 239

DSPy 入门: 再见提示,你好编程

DSPy("Declarative Self-improving Language Programs (in Python)",发音为 "dee-es-pie")[1] 是斯坦福大学 NLP 研究人员开发的 "基础模型编程 "框架。它强调编程而非提示,并将构建基于 LM 的管道从操作提示转向编程。因此,它旨在解决构建基于 LM 应用程序时的脆弱性问题。DSPy 还将程序的信息流与每一步的参数(提示和 LM 权重)分离开来,为构建基于 LM 的应用程序提供了更系统的方法。

具身机器人和通用AI 3486

j2me游戏开发实例讲解

J2ME再现华容道一.序言  昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正想借这个机会小试牛刀.选用J2ME作为开发语言还有一个原因就是目前Java开发大行其到,无限增殖业务迅猛发展,J2ME的应用日渐活跃起来,也希望我

dongpengpeng的专栏 1324

微信小程序 - 最新图片组拖拽排序更换位置功能,类似微信朋友圈九宫格图片的拖动排序效果,微信小程序实现九宫格图片拖拽调换位置,小程序详细实现图像拖曳拖动切换位置排序功能(详细示例代码,一键复制开箱即用

微信小程序,wechat,小程序,图片排序,图像排序,九宫格拖拽排序,拖曳,拖动,拖拽,仿微信朋友圈,朋友圈宫格图片排序,微信朋友圈图片拖动排序效果,实现九宫格图片拖拽,图像拖拽排序组件插件,小程序实现拖拽图片切换图片位置,图片拖动互换位置,图像拖拽切换位置,九宫格图片组合拖动排序,交换九宫格图片的位置,小程序上传图片后,如何实现拖拽排序功能,小程序怎么实现图片拖拽排序,微信小程序图片上传,九宫格拖拽组件,选择图片并拖动_微信小程序图片拖拽,微信小程序长按拖拽排序,图像的拖拽移动,多图拖拽排序功能,实现九宫

✅ 精选推荐文章 4454

[转载]J2ME游戏开发实例讲解

J2ME游戏开发实例讲解一、序言  昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对...

congji3817的博客 278

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

J2ME游戏开发实例讲解(转)

一、序言  昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正...

cuankuangzhong6373的博客 205

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

用NetBeans开发J2ME游戏实例讲解(一)

作者:林刚 文章来源:http://blogs.sun.com/lirincy 最近在网上看到一篇关于华容道的J2ME的开发实例,花了一天时间,在NetBeans上实现了,并且进行了改进和增强,现将经验共享出来,部分代码属原作者所有,在附录中。 1. 必须先安装 NetBeans IDE 4.0 和 NetBeans Mobility Pack 4.0,然后才能开始进行 J2ME MIDP 开发。

mobilechannel的专栏 1346

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

[转载]用NetBeans平台开发J2ME游戏实例讲解2

用NetBeans平台开发J2ME游戏实例讲解2(3).建立Draw类用来显示图形:    public class Draw {    /** Creates a new instance of Draw */    publ...

congji3817的博客 149

J2Me华容道游戏的实现(转载)

一、序言  昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正想借这个机会小试牛刀。选用J2ME的原因还有一个就是目前Java开发大行其到,无线增殖业务迅猛发展,J2ME的应用日渐活跃起来,也希望我的这篇文章能够为J2ME知识的普

fen_fen的专栏 2060

J2ME游戏开发实例讲解(1)

J2ME游戏开发实例讲解(1)一、序言   昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正想借这个机会小试牛刀。选用J2ME的原因还有一个就是目前Java开发大行其到,无线增殖业务迅猛发展,J2ME的应用日渐活跃起来,也希

々Java々 Fan ∷∷∷天道酬勤∷∷∷∷ 731

用NetBeans开发J2ME游戏实例讲解(二)

作者:林刚 文章来源:http://blogs.sun.com/lirincy3).建立Draw类用来显示图形:public class Draw {      /** Creates a new instance of Draw */    public Draw(Canvas canvas) {    }      public static boolean paint(Graphics g,

mobilechannel的专栏 857

NetBeans开发J2ME游戏实例讲解

最近在网上看到一篇关于华容道的J2ME的开发实例,花了一天时间,在NetBeans上实现了,并且进行了改进和增强,现将经验共享出来,部分代码属原作者所有,在附录中。 1. 必须先安装 NetBeans IDE 4.0 和 NetBeans Mobility Pack 4.0,然后才能开始进行 J2ME MIDP 开发。有关下载和安装完整环境的说明,请参见 J2ME MIDP 开发下载页面htt

派的无理性 1743
上一篇: 在asp.net中动态生成web控件
下一篇: SMTP、POP3、NNTP、FTP、HTTP 的服务器指令(简表)
shearlock
博客等级 码龄24年 2粉丝 16原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值