J2ME-MIDP1.0游戏完整实现-双人扫雷1.0(一)(转)

2024-02-21 08:18

本文主要是介绍J2ME-MIDP1.0游戏完整实现-双人扫雷1.0(一)(转),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:yinowl
2005年2

前言
我的上一篇也是第一篇文章《J2ME-MIDP1.0小游戏入门-五子棋》贴出以后,有好多的朋友发邮件、加QQ、加MSN和我聊有关J2ME的内容,我很开心也很感慨,开心并不是因为自己文章写得如何如何而有很多人联系我,而是有这么多的朋友在学J2ME,我原来以为现在已经没有人再会联系文章的作者,看来我错了;感慨是因为,我知道我的第一篇文章其实很差的,从大家的反应,我能感觉出网上的原创资料的缺乏,官方或者书籍虽然权威且,但总感觉学的时候和实际的情况有点距离,我在看这些资料的时候,也经常会想,实际游戏开发公司里为怎么处理、会怎么写这个代码、程序的结构流程会怎么样等等。原创文章的最大有点就是它包含了作者的经验,实际开发中的经验。介于大家对我的支持并且网上原创文章有限,我非常乐意继续写一些自己的经验和技术,充实我们的中文资源。所以我接着写了这篇文档,文档内容即程序的开发过程都是真实的,不是为写文章而写文章,完全是近一段时间里先完成的程序,现在再把它写成教学文章。愿大家学J顺利,多多交流(MSN:yinowl@163.com QQ:47599318 E-mail:yinowl@163.com)

注意
平台:这个游戏我是在 Nokia 的平台上设计的,也就是说使用了 FullCanvas 类以及针对Nokia-60系列(7650)的屏幕设计的,稍作修改就能运行在其他型号的手机上(我已经制作了 Siemens-C65Nokia-7210 ,以及所有支持标准midp1.0/2.0、屏幕128 x 128的手机的几个移植版本,有需要可以和我联系)
代码:同我的前一篇文章《J2ME-MIDP1.0小游戏入门-五子棋》一样,代码列出解释的形式仿照《J2ME Game Programming》一书,按照程序功能思路给出相关代码,一个文件的代码会根据功能在不同的小节给出,文章结束了,代码也就完整了。这不同于通常书中的代码以文件为单位一次全部给出,我认为这样更有助于让大家了解一个程序从设计到最后完成的思路。

游戏介绍
扫雷这个游戏大家一定再熟悉不过了,但这个双人扫雷游戏的扫雷新玩法大家可能就没见过了,其实这就是MSN软件中的一个网络联机小游戏,大家每天在使用MSN,但都很少注意或玩MSN中的游戏吧,可以说我就是把MSN上的联机双人扫雷移植到的手机上,一模一样。如果你现在不方便上网或者没有人和你联机看一下这个游戏的界面和玩法,没关系,我现在就来介绍一下,游戏区(雷区)中一共有 16 x 16 共256个格子,其中有52颗雷,操作除了上下左右键(当然电脑上是用鼠标点的)只需要一个挖雷键,你要做的是挖出雷,而不是用另一个键去标示雷。两个玩家,一方开始挖雷,如果挖到(点到)雷,则加一分,没挖到,就和经典的扫雷一样,显示这个位置周围一圈有几个雷,如果一个雷都没有,和经典的扫雷一样,会把和这一格相连的所有周围没有雷的格子和再外面一圈格子打开,然后另一位玩家挖雷,谁先挖到半数以上(大于26颗)的雷谁就获胜,其实很简单吧,游戏的界面如下:

    

    

游戏逻辑设计
数据结构:这个游戏属于二维棋类游戏,所以我们自然而然想到设计一个 Bomb 类表示每一颗雷位,用一个Bomb类型的二维数组表示整个雷区的所有雷位,每一个雷位(即每一个Bomb实例)包含一个表示该雷位是否是雷的boolean类型的变量,一个表示该雷位是否已被挖的boolean类型的变量,一个表示是否是被玩家一挖的boolean类型的变量(否表示是被玩家二挖),一个表示该雷位周围八个雷位共有几颗雷的int类型变量。这样整个雷区的状态就完全被描述出来了,然后就根据这个二维表来绘制游戏界面
游戏流程:整个游戏只使用唯一一个 FullCanvas 类,用唯一一个表示状态的变量来控制整个游戏的状态,整个游戏也只使用本身的唯一线程
玩家切换:用一个boolean类型的变量来表示当前执行的玩家,用游戏的外框颜色及右下角的旗子颜色来表示出来

接下来就开始详细介绍并列出游戏程序的各部分代码,文章的结束,整个游戏也就完成了

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

package com.imy.yinowl.miningscroll;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
public class MiningMIDlet extends MIDlet {
    MiningCanvas miningCanvas;
//定义游戏界面的FullCanvas类MiningCanvas的对象miningCanvas
    public MiningMIDlet() {
display=Display.getDisplay(this);
miningCanvas=new MiningCanvas(this);
//生成MiningCanvas类的对象miningCanvas
}
    protected void startApp(){
MiningMIDlet.display.setCurrent(miningCanvas);
//在屏幕上绘出游戏见面miningCanvas
}
    protected void pauseApp(){
}
    protected void destroyApp(boolean arg0){
notifyDestroyed();
}
}

地雷类:Bomb.java
Bomb 类定义了二维雷区内每一个雷位的所有信息,这样用一个Bomb类型的二维表就能完全描述整个雷区的状态。我在Bomb类中,用了三个boolean型变量和一个int型变量来描述一个雷位:boolean型变量isBomb:描述这个位置是否是雷;boolean型变量hasFound:描述这个位置是否已被挖掘;boolean型变量isPlayer1:描述如果这个位置是雷,而且已经被挖掘,那么是否是玩家一挖掘,如果值是false,表示是被玩家二挖掘,只有在isBomb和hasFound都为true时,这个变量的值才有意义;int型变量bombaround:描述此位置周围八个位置共有几颗雷,这个变量除了用来告诉玩家周围雷数还可以用来打开成片的非雷区域。通过这四个变量配合不同的图片就可以把整个雷区的不同状态的图形界面呈现在玩家面前。Bomb类的源代码如下:

package com.imy.yinowl.miningscroll;
public class Bomb {
    int bombaround;
boolean isPlayer1;
boolean hasFound;
boolean isBomb;
    public Bomb(){
bombaround=0;
isBomb=false;
hasFound=false;
isPlayer1=true;
}
}

界面逻辑类框架:MiningCanvas.java
一般游戏会出现很多的界面,例如游戏的LOGO(也称作闪屏或启动界面)、开始菜单(主菜单)、设置界面、游戏界面、帮助界面、关于界面、游戏时帮助菜单等等,我的这个游戏不需要设置所以没有设置界面,所有的这些界面我们都整合在这一继承自Nokia-API中的FullCanvas类中,我们要尽可能的较少类的数目以减少资源的开销,具体是如何整合的,我们会再稍候详细介绍。FullCanvas不支持Command及CommandListener,所以我们不能再使用原先的命令方法;我们会需要通过线程控制启动界面的停留时间,所以会用到游戏自己的主线程。
整个游戏我们通过一个int型变量gamestate配合switch结构来控制游戏中所处的界面状态,也就是当前所显示界面,在switch结构中根据gamestate不同的值,绘制不同的界面,在相应的时间改变gamestate的值,然后repaint,也就改变了接下来玩家所看见的界面,也同样用这个方法在keyPressed方法中控制玩家按键的相应
框架源代码如下:

package com.imy.yinowl.miningscroll;
import java.io.IOException;
import java.util.Random;
import java.util.Vector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import com.nokia.mid.ui.FullCanvas; public class MiningCanvas extends FullCanvas implements Runnable{ MiningMIDlet miningMIDlet; int gamestate; static final int GAMESTATE_SPLASH=0; static final int GAMESTATE_MENU=1; static final int GAMESTATE_GAMEING=2; static final int GAMESTATE_HELP=3; static final int GAMESTATE_SETTING=4; static final int GAMESTATE_ABOUT=5; static final int GAMESTATE_GAMEMENU=6; static final int GAMESTATE_COUNT=7; public MiningCanvas(MiningMIDlet miningMIDlet){ super(); this.miningMIDlet=miningMIDlet; gamestate=0;//游戏加载时默认界面为启动界面 } protected void paint(Graphics g) { g.setColor(0x00FFFFFF); g.fillRect(0,0,canvasW,canvasH); switch(gamestate){ case GAMESTATE_SPLASH: paintSplashScreen(g);//绘制游戏启动界面 break; case GAMESTATE_MENU: paintMenuScreen(g);//绘制游戏主菜单 break; case GAMESTATE_HELP: paintHelpScreen(g);//绘制帮助界面 break; case GAMESTATE_GAMEING: paintGameScreen(g);//绘制游戏界面 break; case GAMESTATE_GAMEMENU: paintGameMenuScreen(g);//绘制游戏时菜单界面 break; default: paintMenuScreen(g);//绘制游戏主菜单 break; } public void run() { } protected synchronized void keyPressed(int keyCode) { int action = getGameAction(keyCode); switch(gamestate){ } } }

接下来就开始设计绘制游戏的每一个界面了

启动画面
一般启动画面是一个静态的或动态的图片(静态居多),显示了游戏的LOGO和游戏的发行或制作商的名称,停留时间为三秒,而且不能通过按键跳过,如果游戏需要进行一些费时的初始化,可以在这三秒中同时进行。这停留的三秒通过控制本线程来实现。图片:源代码如下:

在MiningCanvas.java中添加如下代码
Thread thread;
Image splashImage;
int splashDelayTime;
public MiningCanvas(MiningMIDlet miningMIDlet){
...
splashDelayTime=3000;
try{
splashImage=Image.createImage("/occo.png");
}catch(IOException e){}
thread=new Thread(this);
thread.start();
}
private void paintSplashScreen(Graphics g){
g.setColor(0x00000000);
g.drawImage(splashImage,getWidth()/2,getHeight()/2-5,Graphics.HCENTER|Graphics.VCENTER);
}
public void run() {
try{
Thread.sleep(splashDelayTime);
}catch(InterruptedException e){}
gamestate=GAMESTATE_MENU;//在启动动画停留3秒后,改变游戏状态变量值,跳转到主菜单状态
repaint();
}

游戏主菜单界面
主菜单通过不同大小的两种字体来显示选中和未选中的菜单项,用一个int型变量表示当前选中的菜单项,用一个String型的一维数组储存菜单项的内容,在keyPresseed方法中根据菜单选项变量menuIdx来相应的改变游戏的状态,从而使游戏跳转到相应的状态。需添加的源代码如下:

在MiningCanvas.java中添加如下代码
static final Font lowFont  = Font.getFont (Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);  
static final Font highFont = Font.getFont (Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM);  
//两种字体,分别是未选中和选中状态的字体
static final int lowColor  = 0x000000FF;//未选中状态的颜色
static final int highColor = 0x00FF0000;//选中状态的颜色
static final int highBGColor = 0x00CCCCCC;//选中状态的背景颜色 static final int MAIN_NEW_GAME = 0;
static final int MAIN_SETTINGS = 1;
static final int MAIN_HELP = 2;
static final int MAIN_ABOUT = 3;
static final int MAIN_EXIT = 4;
static final int MAIN_MENU_ITEM_COUNT = 5;
static String[] mainMenu = new String[MAIN_MENU_ITEM_COUNT]; static int canvasW;//屏幕宽
static int canvasH;//屏幕高
static int startHeight;//菜单列表的起始高度
static int spacing;//菜单项间宽度
static int menuIdx;//当前选中的菜单项 public MiningCanvas(MiningMIDlet miningMIDlet){ ... menuIdx = 0; canvasW=getWidth();canvasH=getHeight(); spacing = highFont.getHeight()/2; startHeight = (canvasH-(lowFont.getHeight()*mainMenu.length)-(mainMenu.length-1)*spacing)/2; mainMenu[0] = "New Game"; mainMenu[1] = "Settings"; mainMenu[2] = "Help"; mainMenu[3] = "About"; mainMenu[4] = "Exit"; } private void paintMenuScreen(Graphics g){ for(int i=0;i < mainMenu.length;i++){ if(i==menuIdx){ g.setColor(highBGColor); g.fillRect(0,startHeight+i*(lowFont.getHeight()+spacing)-(highFont.getHeight() -lowFont.getHeight())/2,canvasW,highFont.getHeight()); g.setFont(highFont); g.setColor(highColor); g.drawString(mainMenu[i],(canvasW-highFont.stringWidth(mainMenu[i]))/2, startHeight+i*(lowFont.getHeight()+spacing)-(highFont.getHeight()-lowFont.getHeight())/2, Graphics.TOP|Graphics.LEFT); } else { g.setFont(lowFont); g.setColor(lowColor); g.drawString(mainMenu[i],(canvasW - lowFont.stringWidth(mainMenu[i])) / 2, startHeight + i*(lowFont.getHeight() + spacing),Graphics.TOP|Graphics.LEFT); } } } 在keyPressed方法中的switch结构中添加 case GAMESTATE_MENU: { if (getGameAction(keyCode) == FullCanvas.UP && menuIdx - 1 >= 0) { menuIdx--; } else if (getGameAction(keyCode) == FullCanvas.DOWN && menuIdx + 1 < mainMenu.length) { menuIdx++; } else if (getGameAction(keyCode) == FullCanvas.FIRE) { switch(menuIdx) {//选中后,按照选项值改变游戏状态值,跳转到相应的状态 case MAIN_NEW_GAME: gamestate=GAMESTATE_GAMEING; break; case MAIN_SETTINGS: gamestate=GAMESTATE_SETTING; break; case MAIN_HELP: gamestate=GAMESTATE_HELP; break; case MAIN_ABOUT: gamestate=GAMESTATE_ABOUT; break; case MAIN_EXIT: miningMIDlet.destroyApp(false); break; } } break; } }

这篇关于J2ME-MIDP1.0游戏完整实现-双人扫雷1.0(一)(转)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/731118

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

Windwos +vs 2022 编译openssl 1.0.2 库

一 前言 先说 结论,编译64位报错,查了一圈没找到解决方案,最后换了32位的。 使用qt访问web接口,因为是https,没有openssl库会报错 QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());if (reply){if (reply->error() == QNetworkReply::NoError

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

基于Springboot + vue 的抗疫物质管理系统的设计与实现

目录 📚 前言 📑摘要 📑系统流程 📚 系统架构设计 📚 数据库设计 📚 系统功能的具体实现    💬 系统登录注册 系统登录 登录界面   用户添加  💬 抗疫列表展示模块     区域信息管理 添加物资详情 抗疫物资列表展示 抗疫物资申请 抗疫物资审核 ✒️ 源码实现 💖 源码获取 😁 联系方式 📚 前言 📑博客主页:

探索蓝牙协议的奥秘:用ESP32实现高质量蓝牙音频传输

蓝牙(Bluetooth)是一种短距离无线通信技术,广泛应用于各种电子设备之间的数据传输。自1994年由爱立信公司首次提出以来,蓝牙技术已经经历了多个版本的更新和改进。本文将详细介绍蓝牙协议,并通过一个具体的项目——使用ESP32实现蓝牙音频传输,来展示蓝牙协议的实际应用及其优点。 蓝牙协议概述 蓝牙协议栈 蓝牙协议栈是蓝牙技术的核心,定义了蓝牙设备之间如何进行通信。蓝牙协议

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python