分享
 
 
 

MIDP1.0小游戏入门-五子棋1.1

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

文章来源:J2ME开发网

1.1版注:完善了游戏输赢的判断,由于考虑的比较匆忙,没有非常仔细地考量算法的优劣程度,目的主要对游戏的输赢进行一种实现,嵌入到程序中,给大家做为一个参考,由于其他内容和1.0版完全相同,我就把输赢实现这一节提到最前面,后面再补上所有的内容。

游戏输赢

最后要说的是判断游戏的输赢。我的思路是,每次在玩家着棋(按下5)时,判断棋局的输赢,然后用一个 Alert 显示哪一方赢了以及当前的比分,返回后开始新的一局。判断的逻辑是,在当前所下的棋子的0/180度、90/270度、45/225度、135/315度四个方向上分别往两头按照由近至远的顺序判断各5个棋子是否是当前下棋方的棋子,如果是则累加到一个变量上,如果在到达5之前出现“否”的情况,则中止在这一方向或这一角度的判断,变量归1(应为当前棋子肯定是当前下棋方下完的棋子)并进行下一个方向或角度。在判断是否是当前方时,用当前 isPlayer1 变量和棋子对象的 isPlayer1 变量进行比交。源代码如下:

int player1win,player2win;

protected synchronized void keyPressed(int keyCode) {

...

else if (action == Canvas.FIRE) {

if(chesses[selectedY][selectedX]==null){

chesses[selectedY][selectedX]=new Chesses(this.isPlayer1);

if(checkWin()){

String winner;

if(isPlayer1){

winner="白方胜利";

player1win++;

}

else{

winner="红方胜利";

player2win++;

}

try{

Thread.sleep(3000);

}catch(Exception e){

}

Alert winAlert=new Alert("",

winner+"\n白方 "+player1win+" : "+player2win+" 红方",

null,AlertType.INFO);

winAlert.setTimeout(Alert.FOREVER);

Display.getDisplay(gobang).setCurrent(winAlert,this);

init();

repaint();

}

this.isPlayer1=!this.isPlayer1;//切换下棋方

}

}

repaint();

}

private boolean checkWin(){

int num=1;

if(num<5){

num=1;

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX-i,selectedY)){

num++;

}

else break;

}

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX+i,selectedY)){

num++;

}

else break;

}

}

if(num<5){

num=1;

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX,selectedY-i)){

num++;

}

else break;

}

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX,selectedY+i))

num++;

else break;

}

}

if(num<5){

num=1;

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX-i,selectedY-i))

num++;

else break;

}

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX+i,selectedY+i))

num++;

else break;

}

}

if(num<5){

num=1;

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX+i,selectedY-i))

num++;

else break;

}

for(int i=1;i<=4;i++){

if(isPlayer1(selectedX-i,selectedY+i))

num++;

else break;

}

}

if(num>=5)

return true;

else

return false;

}

private boolean isPlayer1(int y,int x){

if(x<=15 && x>=0 && y<=15 && y>=0 && chesses[x][y]!=null){

if(chesses[x][y].isPlayer1==this.isPlayer1)

return true;

else

return false;

}

else return false;

}

介绍

这是我学习j2me入门后的第一个作品,当然这也是一个极其简单的作品(没有电脑AI,只能是两个人对战),现在我把当时的设计思路写成这篇文档,希望对想入门j2me的朋友在j2me的流程,按键响应,绘图等方面有所帮助,同时也希望大家指出错误和改进程序。

注意

代码列出解释的形式仿照《J2ME Game Programming》一书,按照程序功能思路给出相关代码,一个文件的代码会根据功能在不同的小节给出,文章结束了,代码也就完整了。这不同于通常书中的代码以文件为单位一次全部给出,我认为这样更有助于让大家了解一个程序从设计到最后完成的思路。

设计

数据结构:由于五子棋是一个二维棋类游戏,所有首先想到的是定义一个Chesses类来表示棋子,Chesses有一个boolean型的变量isPlayer1来区分该棋子是哪玩家下的,然后用一个Chess类型的二维数组来包含棋盘上的所有棋子。考虑到移动设备的资源有限,尽可能减少系统资源占用,我考虑不在数组建立后直接生成数组的每一个对象,而是把每一个棋子对象(Chesses)放在游戏的进行中生成,也就是说在游戏进行时,玩家每下一步棋,在数组相应位置生成该棋子的对象,这样可以避免还没有下的棋子在一开始就占用了系统内存

流程:游戏按照棋子的二维数组进行绘制棋子,玩家下棋后,程序修改数组相应位置,设置isPlayer1值,然后重新绘制(repaint),就更新了棋盘界面。由于游戏的功能简单,也为了使游戏的操作尽可能的简便,我不在游戏进入时设计菜单,而是直接开始对战,在对战界面,设置了重新开始和退出的按钮。即运行即玩,一键开始,一键重来,一键退出。

玩家切换:棋类游戏有一个问题需要注意,就是提示当前由哪方下棋,为了节省界面空间,简化游戏界面,我在棋盘外围加了一个3个像素宽的框,框的颜色就是当前下棋方的颜色,如图:

应用程序类:Gobang.java

接下来就开始完成游戏中的每一个类,首先就是一个MIDlet类。Gobang类继承自MIDlet类,用于连接设备的应用程序管理器(Application Manager),通过方法startApp,pauseApp,destroyApp来通知游戏的开始,暂停和销毁结束。源代码如下:

package com.occo.j2me.game.gobang;

import javax.microedition.lcdui.Display;

import javax.microedition.midlet.MIDlet;

public class Gobang extends MIDlet {

GobangCanvas gobang;//定义游戏界面的Canvas类GobangCanvas的对象gobang

public Gobang() {

super();

gobang=new GobangCanvas(this);//生成GobangCanvas类的对象gobang

}

protected void startApp(){

Display.getDisplay(this).setCurrent(gobang);

//在屏幕上绘出游戏见面gobang

}

protected void pauseApp(){

}

protected void destroyApp(boolean arg0){

}

}

游戏界面类:GobangCanvas.java

GobangCanvas类是游戏的核心类,继承自Canvas,此类将完成游戏的逻辑、绘图、控制、互动等所有功能,此类的框架代码如下:

package com.occo.j2me.game.gobang;

import javax.microedition.lcdui.Canvas;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Graphics;

public class GobangCanvas extends Canvas implements CommandListener{

protected Gobang gobang;

public GobangCanvas(){

}

public GobangCanvas(Gobang gobang){

this.gobang=gobang;

}

protected void paint(Graphics g) {

}

}

棋子类:Chesses.java

此类定义了一个棋子,棋盘上的每一个棋子都对应着一个Chesses的对象,整个棋盘是一个Chesses类型的二维数组,源代码如下:

package com.occo.j2me.game.gobang;

public class Chesses {

boolean isPlayer1;

public Chesses() {

}

public Chesses(boolean isPlayer1) {

this.isPlayer1=isPlayer1;

}

}

添加图形图像

到现在,我们已经完成了游戏的一个基本框架,接下来,我们就可以来绘制游戏的每一个部件了

首先是五子棋的一些初始设置,添加如下代码到GobangCanvas.java

...

int empty;//游戏界面到屏幕边缘的留空

int canvasW,canvasH;//画布的长和宽

int chessLength;//棋子的直径

int chessMapLength,chessMapGrid,chessGridLength;

//棋盘的边长,棋盘一边格子数,每格宽度

int chessMapX,chessMapY;//棋盘左上角x,y坐标

int selectedX,selectedY;//选择框在棋盘格局上的x,y位置

boolean isPlayer1;//是否是玩家1

Chesses[][] chesses;//棋子数组

boolean newGame;//是否是新的游戏

public GobangCanvas(Gobang gobang){

newGame=true;

empty=10;

canvasW=getWidth()-empty;canvasH=getHeight()-empty;

chessMapGrid=15;

chesses=new Chesses[chessMapGrid+1][chessMapGrid+1];

if(canvasW>canvasH){

chessMapLength=canvasH-canvasH%chessMapGrid;

chessMapX=(canvasW-chessMapLength)/2+empty/2;

chessMapY=(canvasH%chessMapGrid)/2+empty/2;

}

else{

chessMapLength=canvasW-canvasW%chessMapGrid;

chessMapX=(canvasW%chessMapGrid)/2+empty/2;

chessMapY=(canvasH-chessMapLength)/2+empty/2;

}

chessGridLength=chessMapLength/chessMapGrid;

chessLength=chessGridLength-1;

selectedX=selectedY=chessMapGrid/2;

isPlayer1=true;

}

最先要绘制的是棋盘,棋盘是正方形,但屏幕有矩形的,所以棋盘边长要按短边计,但短边未必是棋盘格子数的整数倍,因此

棋盘边长 = 短边 - 短边 % 格子数

因为棋盘要居中,所以在算左上角坐标时,记得也要把留空(empty)除以2,以下是画棋盘的代码:

protected void paintMap(Graphics g){

for(int i=0;i<chessMapGrid;i++){

for(int j=0;j<chessMapGrid;j++){

g.setColor(128,128,128);

g.drawRect(chessMapX+j*chessGridLength,

chessMapY+i*chessGridLength,

chessGridLength,chessGridLength);

}

}

}

然后是绘制选择框,注意:选择框的selectedX,selectedY并不是在画布上的x,y坐标,而是在棋子数组(chesses)中的位置,源代码如下:

protected void paintSelected(Graphics g){

g.setColor(0,0,255);

g.drawRect(chessMapX+selectedX*chessGridLength-chessGridLength/2,

chessMapY+selectedY*chessGridLength-chessGridLength/2,

chessGridLength,chessGridLength);

}

接着是按照棋子二维数组绘制已经下了的棋子。玩家每下一次棋,就修改数组,在重新绘图的时候就能绘出,源代码如下:

protected void paintChesses(Graphics g){

for(int i=0;i<=chessMapGrid;i++){

for(int j=0;j<=chessMapGrid;j++){

if(chesses[i][j]!=null){

if(chesses[i][j].isPlayer1)

g.setColor(255,255,255);

else

g.setColor(255,0,0);

g.fillArc(chessMapX+j*chessGridLength-chessLength/2,

chessMapY+i*chessGridLength-chessLength/2,

chessLength,chessLength,0,360);

}

}

}

}

最后是绘制玩家提示框,并且把所有部件的绘制汇总在paint()方法中,注意绘制的顺序,显而易见,应该先绘制提示框-棋盘-选择框-棋子:

protected void paintPlayer(Graphics g,boolean isPlayer1){

if(isPlayer1)

g.setColor(255,255,255);

else

g.setColor(255,0,0);

g.drawRect(1,1,getWidth()-2,getHeight()-2);

g.drawRect(2,2,getWidth()-4,getHeight()-4);

g.drawRect(3,3,getWidth()-6,getHeight()-6);

}

protected void paint(Graphics g) {

g.setColor(0x00000000);

g.fillRect(0, 0, getWidth(), getHeight());

paintPlayer(g,isPlayer1);

paintMap(g);

paintSelected(g);

paintChesses(g);

}

响应玩家操作

接下来应该添加命令按钮和游戏操作控制。我们在游戏中需要有两个按钮,重新开始和退出,此外还必须接收玩家控制选择框的操作上下左右和着棋,添加代码到GobangCanvas.java:

Command exitCmd;

Command restartCmd;

public GobangCanvas(Gobang gobang){

...

restartCmd = new Command("重新开始", Command.SCREEN, 0);

exitCmd = new Command("退出", Command.EXIT, 0);

addCommand(restartCmd);

addCommand(exitCmd);

setCommandListener(this);

}

private void init(){

if(newGame){

chesses=new Chesses[chessMapGrid+1][chessMapGrid+1];

isPlayer1=true;

selectedX=selectedY=chessMapGrid/2;

}

}

public void commandAction(Command c, Displayable d) {

if (c == exitCmd) {

gobang.destroyApp(false);

gobang.notifyDestroyed();

}else if(c==restartCmd){

init();//初始化棋盘,把棋盘清空,重新开始游戏

repaint();

}

}

protected synchronized void keyPressed(int keyCode) {

int action = getGameAction(keyCode);

if (action == Canvas.LEFT ) {

selectedX=(--selectedX+chessMapGrid+1)%(chessMapGrid+1);

}

else if (action == Canvas.RIGHT) {

selectedX=(++selectedX)%(chessMapGrid+1);

}

else if (action == Canvas.UP) {

selectedY=(--selectedY+chessMapGrid+1)%(chessMapGrid+1);

}

else if (action == Canvas.DOWN) {

selectedY=(++selectedY)%(chessMapGrid+1);

}

else if (action == Canvas.FIRE) {

if(chesses[selectedY][selectedX]==null){

chesses[selectedY][selectedX]=new Chesses(this.isPlayer1);

//checkWin();

this.isPlayer1=!this.isPlayer1;//切换下棋方

}

}

repaint();

}

private void checkWin(){

}

至此,游戏的所有绘图部分全都给出,有一点需要指出,双缓冲显示技术,由于现在有的手机已直接内置了双缓冲,这里我们就不在详细说明,有兴趣可以查阅相关文档。

总结

整个游戏已经全部完成,大家一定会觉得很简单吧,这个游戏其实只使用了j2me-midp1.0种很少的内容,但已经是一个完整的游戏了,希望对一些朋友有所帮助。当然我们完全可以进行一些扩展,比如加上片头动画,加上声音,加上电脑AI,加上蓝牙对战功能(已经完成,下次专门写一篇文档),这样游戏就慢慢的完善,并且具有商业价值,愿大家学J顺利,多多交流(MSN:yinowl@163.com QQ:47599318 E-mail:yinowl@163.com)。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有