一个J2ME?D游戏技术DEMO
源码计划了很久了Q准备作为J2ME的Game APIs的例子脓出来Q无奈一直不得空Q直到最q才单地整理了一下,把它׃n出来?br />
严格地讲Q这个不能算作一个完整的游戏Q没有自q创意Q只是简单地模仿了一个Flash的小游戏Q当时是Z自己l习用的QGame Play也很单,但包含了J2ME的Game包中所有的东西Q作Z个Demo来讲Q内容还是很充实的?br />
在介l之前,先简单地提一下Gaming API中的几个c,包括SpriteQTiledLayer,LayerManager,Media.Player{?br />SpriteQ偶看到过有些人把它翻译成_Q我h一下,׃译了,q个cȝ来表达游戏中的一个活动的角色Q包括玩家控制的Player角色和非玩家控制的角ԌNPCQ?br />TiledLayerQ这个是用来表达背景的类Q其实从l图的角度来看,Sprite和TiledLayer没有本质差别Q只是将要画在屏q上的一q图像而已Q因此在Game包中它们都是Layercȝ子类。ƈ且都能够从一q图像方便地构造?br />LayerManagerQ这个是用来理所有图像对象的c,通过把Sprite和TiledLayer加入其中QJ2ME讑֤q道如何来l制它们了?br />GameCanvasQ最后要提到的是dQ这是所有可视对象最l要表演的舞台。其实跟以前的Canvas没有本质的不同,同样是提供了一个Graphics接口来供把一些内容画上去而已Q但增加了对玩家输入的处理,能够通过按键状态来直接d玩家按键操作Q相Ҏ加简便了?br />q利用到了Media包中的Player和ToneControl来播N乐,没有音乐和声音的游戏是不可能出现的,呵呵?br />
先简单地介绍一下这个游戏,玩家只能控制角色Q一个端着|兜的小人)水^地移动,接住自然下落的小球就得分Q积分到一定程度后Q小球下落速度加快,直到最高速ؓ止;如果没接到小球,也有相应的惩|,最l游戏会Game Over?br />
Z避免q于单调Q玩家角色不是简单地q移Q而是利用了Sprite的简单动画来让角色看上L些动作。其实很方便的,只是在移动位|时更换一下图像就行了QSprite提供有几个方法NextFrame()QPrevFrame()用来切换?br />声音部分更单了Q只是重复地播放一D预先写q去的音乐,来自Sun的WTK中的一个例子?br />不过既然提到了它Q就q是先简单地说一下吧Q免得后面介l其他部分时有些疑问?br />
J2ME中的声音部分非常单,当然效果也不太好Q所需的基本元素只有如下几个,一个内定w分的BytepdQ一个是播放器PlayerQ还有一个是控制部分的ToneControl。代码示例如下:
tonePlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
tonePlayer.setLoopCount(-1);
tonePlayer.realize();
ToneControl tc = (ToneControl)tonePlayer.getControl("javax.microedition.media.control.ToneControl");
tc.setSequence(mySequence);
当然q需要些初始化工作和异常处理Q然后就可以通过tonePlayer.start()/close()Ҏ来控制声音的播放和停止了?br />
具体请参见源码中CanvasGetBall.java中的createTonePlayer()Ҏ?br />
锣鼓响了半天Q主角也该出Z。这个游戏里的主角只有一个接球小人,不过角色q有一个跟它演Ҏ戏的NPCQ就是那只从天而降的小球了。这两个c都是Sprite的子c,球因ؓ有些自己的动作,同时实现了Runnable接口Q能自主zd。不q也很简单,NPC嘛,一般来说都是相对弱ZQ不然也没法玩了Q谁的反应速度跟得上机器啊Q再说了QNPC知道的信息也要多?)?br />
先来看看主角吧,BallPlayercd是我们的主角Q其实非常简单,在所有的6个类中,除了记分用的Score外就数它最了。提供一q图像把它实例化后,只能通过左右Ud来控制了Q额外的几个Ҏ都是跟记分有关系的,先略q不提?br />先来看看它的构造方法:
public BallPlayer(Image img, int fw, int fh) {
super(img, fw, fh);
this.step = fw / 2;
}
主要工作都由它的父类Sprite做了Q给Zq图像,q个囑փ是用PNG格式提供的,大家可能留意C是一q单一的图像,而是有点象动画中的几个关键帧,不错Q的如此,构造方法中的后两个参数是告诉Sprite如何分割q幅囑փ的。这里整个Player共有6个关键Q比较粗p,呵呵Q自己动手截屏做?)
Field step是用来控制主角的Ud步伐的,Z快一点,取了它n宽的一半?br />
接下来我们看看如何移动它Q就是通过q样两个Ҏ来左Ud右移?br />
public void left() {
prevFrame();
if( (getX() - step) >= -12 ) {
move( -1 * step, 0);
}
}
public void right () {
nextFrame();
if((getX() + step) < canvas.getWidth()) {
move(step, 0);
}
}
留意一下,q里只管相对位移Q主角的开始位|通过setPosition来设定,在运动过E中最好就不要直接讄位置了,增大计算量,要不qh动作不自然了?br />
接下来简单说一下配角-Q球。球的构造跟主角cMQ只是ؓ了节省构造销毁对象带来的开销Q这个对象是一直存在的Q也是让它掉下M又自pv来,q根据记分来定下落速度Q简单地用线E实现的Q没怎么仔细设计Q大家看看代码就清楚了?br />
再来看看CanvasGetBallq个c,它从GameCanvasl承Qƈ实现了CommandListener和Runnable两个接口Q是整个游戏中最复杂的一个类了,主要工作有如下几个部分,实例化主角,配角对象Q还有背景对象,音乐{,q在适当的时候画些对象,在顶部画Z状态信息,q根据玩家操作开始和暂停游戏Qƈ昄相应画面。ƈ通过q行撞来判定玩家是否接到了小球?br />方法如下:
private boolean notMiss( ) {
// return player.collidesWith(ball,false);
int ballCX = ball.getX() + ball.getWidth()/2;
int ballCY = ball.getY() + ball.getHeight()/2;
int playerCX = player.getX() + player.getWidth()/2;
int playerCY = player.getY() + player.getHeight()/2;
return ((Math.abs(playerCX - ballCX)< ball.getWidth()/2) &&
(Math.abs(ballCY - playerCY) < 5));
}
被注释掉的一行是直接用Sprite的碰撞检,下面的部分是自己计算两幅囑փ有没有重叠,效果差不多。其中collidesWith()的第二个参数是告诉内部方法是否要用像素别的,通常{案是千万不要,q很慢的Q而且没有必要q么_?br />
Z说明整个游戏的控刉辑Q我们先来看看MIDletGetBallq个c,跟通常的MIDlet略有不同Q因为我把主U程攑֜了CanvasGetBall中,MIDletGetBall只是单地控制ȝE就行了?br />
public void startMainThread() {
Display.getDisplay(this).setCurrent(displayable);
if(mainThread != null) {
mainThread = null;
Runtime.getRuntime().gc();
}
mainThread = new Thread(displayable);
mainThread.start();
}
其中W一行就是设|当前显C页面;也就是显CCanvasGetBall?br />
回到CanvasGetBallQ整个游戏分几个阶段Q相应有不同的画面和命o接口Q详l说明如下:
1. {待开始,对应在方法ready():
public void ready() {
cover.setTitle(TIPS[2]);
cover.addCommand(playCommand);
Display.getDisplay(MIDletGetBall.instance).setCurrent(cover);
}
Zl制方便Q这里单独用了个GameCanvas来绘制提CZ息和响应命oQƈҎ玩家操作在CanvasCover和CanvasGetBall两个画面之间来回切换?br />
2. 游戏画面Q包括启动和l束两个ҎQ?br /> public void start() {
if(!playing) {
strTip = TIPS[0];
playing = true;
MIDletGetBall.instance.startMainThread();
removeCommand(playCommand);
removeCommand(resumeCommand);
addCommand(pauseCommand);
ball.start();
try {
if(tonePlayer != null) {
tonePlayer.start();
}
}
catch (MediaException ex) {
tonePlayer.close();
tonePlayer = null;
}
}
}
public void stop () {
if(playing) {
ball.stop();
strTip = TIPS[1];
try {
Thread.sleep(300);
}
catch (InterruptedException ex) {
}
playing = false;
removeCommand(pauseCommand);
addCommand(resumeCommand);
try {
tonePlayer.stop();
}
catch (MediaException ex1) {
tonePlayer.close();
tonePlayer = null;
}
}
}
q对应设|相应的命o来让玩家能够l箋下去Q构成一个简单的闭控制环\?br />
3.游戏l束Q对应方法gameover()
public void gameover() {
this.stop();
cover.setTitle(TIPS[3]);
cover.removeCommand(playCommand);
cover.addCommand(restartCommand);
Display.getDisplay(MIDletGetBall.instance).setCurrent(cover);
}
说到q里Q基本上也就把它讲完了,具体内容误l研I源码,其实没必要看太多书,深入地研I一个问题ƈҎ自己的理解来改进或者是修正它,实践才是最好的老师Q希望大家能够有所收获?br />
ȝ一下,q个游戏存在的问题有如下几个Q?br />1. 没有好的Game PlayQ画面很差;
2. 可玩性不强,控制比较单调Q?br />3. 游戏声音q于单调Q?br />4. q行速度有些慢?br />
但作Z个技术DemoQ它늛了Game包中的所有内容,q提供了一个利用线E方式实现简单游戏的ҎQ很单,但不适合真实的游戏,比较Ҏ?br />背景处理很差Q可以通过一个Map来分割组合处理背景小片,能让游戏场景变得生动些,可以实现cM于卷轴游戏的效果Q自p试吧Q?br />
附:源代码和工程Q在JBuilderX下编译,同时需要WTK2.0或以上版本。好久没有用Jbuilder了,Ch?),现在主要开发工hEclipse和EclipseME,感觉非常爽,免费的也有好货?br />