1、4.系统总体设计与实现4.1 总体设计分析总体设计是软件开发过程中的另一个重要阶段,在这一阶段中将根据需求分析中提出的逻辑模型,科学合理地进行物理模型的设计。这个阶段的主要目标是将反映用户信息需求的逻辑方案转换成物理方案,并为下一阶段提供必要的技术资料。4。1。1 总体设计原则 (1)整体性:软件是作为统一整体而存在的.因此,在总体设计中要从整个软件的角度进行考虑。(2)灵活性:为保持软件长久的生命力,要求该手机游戏软件具有很强的环境适应性.为此,游戏软件应具有较好的开放性和结构的可变性。(3)可靠性:可靠性是指软件抵御外界干扰的能力及受外界干扰时的恢复能力。(4)经济性:经济性是指在满足游戏
2、软件需求的前提下,尽可能地减小游戏软件的开销。4。1。2 软件模块总体设计软件中各模块之间的关系通常利用层次图来表示。它是一种一系列多层次的用树形结构的矩形框描绘数据的层次结构框图。一个单独的矩形框作为树形结构的顶层,各个数据的子集由下面的各层矩形框代表,最底层的各个矩形框代表组成这个数据的实际数据元素(不能再分割的元素),它代表完整的数据结构。这模式非常适合于需求分析阶段的需要,层次方框图对数据结构描绘随着结构精细化也越来越详细。反复细化沿着图中每条路径,从对顶层信息的分类开始,直到确定了数据结构的全部细节为止。 图4-1 游戏功能结构本研究中将游戏软件分为三大模块,如图41所示,包括:游戏
3、选项、游戏设置和帮助.按照在调研中搜集的资料对每个模块的功能进行编排制作。依据上述功能的分析,本研究中,将游戏软件在三大模块的基础上又对每一大模块又分为几个子模块: 游戏选项包括六个模块:开始游戏、重新游戏、悔棋、认输、背景音乐和退出游戏。 游戏设置包括三个模块:先后手设置、棋盘底纹颜色设置和棋盘大小设置。 帮助包括两个模块:游戏帮助和关于.4.2 游戏设计4。2.1 游戏前的准备本游戏在开发之前需要做一些前期准备工作,尤其是对于精通五子棋游戏的Java 游戏开发者来说。通常情况下,一款运用起来比较熟练地 J2ME 开发工具是必不可少的。本游戏使用的是J2ME的简化开发工具 Sun Java
4、(TM) Wireless Toolkit 2.5.2 for CLDC,他需先将Java虚拟机安装调试好之后才能使用。WTK 2。5。2 不带有文本编辑功能,所以需要另寻搭配使用。本游戏采用 Ultra Edit 进行编辑。本游戏需要几张后缀名为。png格式的卡通图,除了一张用作五子棋游戏的 Logo 外,其余的都将在游戏中使用.4。2。2 游戏界面和事件驱动设计游戏的界面设计采取传统游戏界面风格,如图4-2所示。游戏设计中采用传统界面游戏风格,首先启动游戏,然后进入游戏开始界面,界面中放置“设置”、“开局”、“帮助”、“关于四个选项供玩家选择。其中“设置”选项主要是对游戏的相关功能进行设置
5、,如游戏难度设置.另外还有“悔棋、“重玩”等项目的设置.除此之外还包括查看游戏帮助、游戏介绍等.图42 游戏界面设计所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么操作(即调用什么函数)。当然事件不仅限于用户的操作。我们知道,事件是事件驱动的核心自然是.从事件角度说,一个事件收集器、一个事件发送器和一个事件处理器组成了事件驱动程序的基本结构。事件收集器专门负责收集包括来自硬件的(如时钟事件等)、来自用户的(如键盘、鼠标事件等)及来自软件的(如应用程序本身、操作系统等)的所有事件。将收集器收集到的事件分发到目标对象中则由事件发送器负责完成。具体的事件响应工作则由事件处理器完
6、成,它需要运用虚函数机制(函数名取为类似于 Handle Msg 的一个名字),它往往要到实现阶段才完全确定。事件处理器对于框架的使用者来说是他们唯一能够看到的. 棋类游戏通常具备两个重要特性,首先是对战双方轮流落子,其次是落子间隔通常是不确定的,尤其是对战后期,可能每一步棋都要经过深思熟虑,无论是人还是计算机,都无法对时间间隔有事先的预期.基于以上两个特性,本游戏摒弃了大多数游戏采用的线程或定时器驱动游戏的方法,而采用了事件驱动的方法,即玩家的键盘或触摸笔触发游戏的下一个动作。事件驱动大大减少了不必要的工作量,只有玩家发出消息时,计算机才启动运算,而在玩家思考期间,计算机不做任何运算和重绘操
7、作。4。2。3 游戏的类设计五子棋游戏属于二维棋类游戏,因此可以定义一个 Chesses 类来表示棋子,用一个 Chess 类型的二维数组来包含棋盘上的所有棋子,对于该棋子玩家的区分使用Chesses 的 boolean 型的变量 is Player1 来区分。可以考虑直接生成数组的每一个对象而不是在数组建立后,而是把每一个棋子对象(Chesses)放在游戏的进行中生成,这主要是考虑到移动设备的资源有限,尽可能减少系统资源占用。这样在游戏进行时,可以避免还没有下的棋子在一开始就占用了系统内存,玩家每下一步棋,在数组相应位置生成该棋子的对象.对于游戏中的每一类的设计,首先就是一个 MIDlet
8、类,Gobang 类继承自MIDlet 类,通过方法 start App,pause App,destroy App 来通知游戏的开始,暂停和销毁结束,用于连接设备的应用程序管理器(Application Manager)。 本游戏共由7个类组成,它们各自的功能如下: (1) Gobang MIDlet类 负责程序的启动和屏幕之间的切换;(2) Gobang Canvas 类玩家的对战平台,他继承于 Canvas 类;(3) Setting 类 用于创建游戏的各项设置参数表单; (4) Gobang Logic 类游戏的逻辑类,负责胜负判断和计算机落子; (5) Dot 类棋子类,包含了棋子的
9、位置信息;(6) Help 类游戏的帮助类,包含五子棋的一些常识信息和五子棋教学内容;(7) About类游戏的关于类,包含游戏的版本、版权等信息.各个类之间的关系如图4-3所示:图43游戏类设计4.2。4 游戏的流程设计对于棋盘界面的更新,游戏进行绘制棋子时是按照棋子的二维数组来完成的,玩家下棋后,设置is Player1 值,程序修改数组相应位置,然后重新绘制(repaint)。为了使游戏的操作尽可能的简便,本文设计上不在游戏进入时设计菜单,玩家可以直接开始对战,而是在开始游戏的过程中设置重新开始和退出的按钮。即一键开始,运行即玩,重来或退出都使用一键操作. 游戏流程的设计依据主要是游戏的
10、界面设计和游戏的类的设计. 游戏启动时,Gobang MIDlet 对象先显示游戏的主屏幕,在屏幕下方一侧是出软键(软键指描述抽象客户端设备如何显示),另一侧是用软件构成的菜单,菜单元素主要有“开局”、“游戏设置”、“游戏帮助”、“关于”选项。当玩家选择“游戏设置”软键时,则显示游戏参数设置表单;当玩家选择“开局”软键时,则显示游戏对战主界面;当玩家选择“游戏帮助”软键时,则显示游戏帮助表单;当玩家选择“关于”软键时,则显示游戏关于表单。 玩家进入游戏参数设置表单,当玩家按下“确定软键时,则确认当前游戏参数,返回游戏主屏幕;当玩家按下“取消”软键时,则放弃此次对游戏的修改,直接返回游戏主屏幕.
11、 玩家进入游戏对战画布,对战中画布有两个软键,当玩家按下“返回主菜单”软键时,则退出游戏到达游戏主菜单;当玩家按下“悔棋软键时,则进行悔棋操作;当游戏结束时,“悔棋”软键被换成了“重玩”软键。 玩家进入游戏介绍表单,当玩家按下“确定”软键时,返回游戏主屏幕。4。2。5 游戏算法的设计1、五子棋的获胜组合有哪些获胜组合是在一场五子棋的游戏中计算机必须要知道的,因此,获胜组合的总数必须要求得。在本文中我们假定当前的棋盘为1515:(1)每一列的获胜组合是11,共15列,计算水平方向的获胜组合数,所以水平方向的获胜组合数为:11*15=165.(2)每一行的获胜组合是11,共15列,则可计算垂直方向
12、的获胜组合总数,垂直方向的获胜组合数为:11*15=165。(3)同理,可计算正对角线方向的获胜组合总数,正对角线上的获胜组合总数为11+(10+9+8+7+6+5+4+3+2+1)2=121。(4)计算反对角线上的获胜组合总数。计算反对角线方向的获胜组合总数可计算为11+(10+9+8+7+6+5+4+3+2+1)2=121。这样可计算得所有的获胜组合数为:165+165+121+121=572。2、设计获胜棋型通过上面的计算,一个1515的屋子棋盘在此已经计算出了会有572中获胜方式,因此,我们就可以利用数组建立一些常规棋型,棋型的主要作用是:(1)判断是否有任何一方获胜;(2)根据当前格
13、局判断最可能的落子方式.然而在现实中,高手留给我们的经验就是把握前奏,如“冲四、“活三,除了“连五以外,这些也是同向胜利的捷径.3、攻击与防守获胜棋型的算法是中性的,不区分计算机和玩家,这就涉及到攻击和防守何者优先的问题。而许多高手都认为五子棋的根本是“防守”,“攻击”是灵魂。进攻是取胜的手段,是防守的延续和发展.许多经验和研究表明,一个棋手只要掌握了全面的、基本的防守原理和技巧,就能和比自己棋力高一个等级的进攻型选手对抗,起码能立于不败之地.对手进过越偏激,则防守的效果越好.没有进攻的防守就像只开花不结果,没有实际意义,顽强的防守是反攻的前奏,没有进攻的延续,防守也失去了价值。而这缺一不可。
14、根据以上原理,计算机在接受最佳的攻击位置之前,还要计算当前玩家的最佳攻击位置。如果玩家存在最佳攻击位置,那么计算机就将下一步的棋子摆在玩家的最佳攻击位置上以阻止玩家的进攻,否则计算机便将棋子下在自己的最佳攻击位置上进行攻击.4、用到的典型算法(1)坐标变换算法游戏的实质其实是对所下棋子的位置进行操作和判断,因此将己方、对方以及棋盘上空点的位置坐标存储在相应的List中。我对所下棋子的坐标进行了处理,因为我所采用的棋盘为1515,所以棋子横坐标为0到14的整数,纵坐标也为0到14的整数.因此,每次在棋盘上下子之后,计算机在存储该点的坐标时,便要对坐标进行加工。假设左上角点为firstPoint,
15、它的实际坐标为(x1,y1),而我是将它作为(0,0)存储的,其它的坐标,其它点都是以该点为标准进行变换的,假设棋盘上每个格子的宽度为w,某实际点为(x2,y2),变换后的坐标为(x,y),x=(x2-x1)/w,y=(y2y1)/w.(2)胜负判断算法胜负判断的规则很简单,就是判断游戏双方的棋子在同一条水平线、同一条竖线或是同一条斜线上谁先出现5个连续的棋子,谁先达到这样的目标,谁就获得胜利.在本设计中,是在每次下完一个子后进行判断,看己方是否达到了胜利的标准,若胜利游戏便结束;否则,游戏继续。(3)人工智能算法人工智能算法的主体思想分为以下三个步骤:第一步:根据双方的当前的形势循环地假设性
16、的分别给自己和对方下一子(在某个范围内下子),并判断此棋子能带来的形势上的变化,如能不能冲4,能不能形成我方或敌方双3等。第二步:根据上一步结果,组合每一步棋子所带来的所有结果(如某一步棋子可能形成我方1个活3,1个冲4(我叫它半活4)等),包括敌方和我方的。第三步:根据用户给的规则对上一步结果进行排序,并选子(有进攻形、防守形规则).5、典型类的具体设计(1)应用程序类Gobang 类用于连接设备的应用程序管理器(Application Manager),Gobang类继承自 MIDlet 类,通过 Gobang 类的方法 start App,pause App,destroy App 来通
17、知游戏的开始,暂停和销毁结束.源代码如下: package com。occo。j2me。game.gobang; import javax.microedition。lcdui。Display; import javax.microedition.midlet.MIDlet;public class Gobang extends MIDlet /定义游戏界面的 Canvas 类 Gobang Canvas 的对象 Gobang public Gobang Canvas gobang; Gobang() super(); gobang=new Gobang Canvas(this);/生成 Go
18、bang Canvas 类的对象 gobang protected void start App() Display。get Display(this).set Current(gobang); protected void pause App() protected void destroy App(boolean arg0) /在屏幕上绘出游戏见面 gobang(2)游戏界面类Gobang Canvas 类继承自 Canvas,游戏的核心类是 Gobang Canvas 类,此类将完成游戏的绘图、互动、控制、逻辑、等所有功能,此类的框架代码如下:Package com.occo.j2me。
19、game.gobang; import javax。microedition。lcdui。Displayable; import javax.microedition.lcdui。Command; import javax.microedition。lcdui。Canvas; import javax.microedition.lcdui。Command Listener; public Gobang Canvas(Gobang gobang) this.gobang=gobang; protected void paint(Graphics g) import javax.microedit
20、ion。lcdui。Graphics;public class Gobang Canvas extends Canvas implements Command Listenerprotected Gobang gobang; public Gobang Canvas() (3)棋子类整个棋盘是一个 Chesses 类型的二维数组,棋盘上的每一个棋子都对应着一个Chesses 的对象,此类定义了一个棋子,源代码如下:package com.occo。j2me。game。gobang; public Chesses() public class Chesses boolean is Player1
21、; public Chesses(boolean is Player1) this。is Player1=is Player1; 4.3 游戏实现4.3。1 主类的实现YpkWuZiQiActivity类是五子棋游戏的主类,同时也是五子棋游戏的入口,它继承自Activity类。进入程序后,首先调用init()方法,init()方法通过调用setContentView(R.layout.welcomeview)显示登录该游戏的第一个界面。welcomeview。xml是一个布局文件,里面存储了界面信息。该界面中有四个Button,分别为welButton1、welButton12、welButt
22、on3、 welButton4,点击每个Button都会触发一个事件,其中点击welButton1和welButton2还会给它的成员变量FIGHTINGMODE赋值,因为在人人对战和人机对战是写在同一个ChessBoard类中的,所以需要通过FIGHTINGMODE的值来区分是人人对战还是人机对战。点击welButton1时,FIGHTINGMODE=1,然后会调用initTwo()方法,该方法通过调用setContentView(R.layout.chess)方法,来显示对战的界面。chess.xml文件存储了对战界面的信息.在chess。xml文件中调用了ChessBoard类,该类中主
23、要定义了棋盘的信息,下文会对该类做具体的介绍的。在对战界面中也有四个Button,分别是b1、b2、b3、b4。首先来介绍一下b2,该Button的功能是返回主页,调用init()方法就可以实现。b3的功能是重新开始,这个也只需要调用initTwo()方法.b3的功能是退出,调用了系统方法:System。exit(1)。下面重点介绍一下b1,该Button的功能是悔棋。该Button设定的点击事件详细内容如下:b1。setOnClickListener(new OnClickListener()public void onClick(View v)ChessBoard chess = (Che
24、ssBoard)findViewById(R.id。chess);Point temp = null;if(chess。whoRun = 1) if(chess.firstPlayer。getMyPoints()。size()=1 &chess。secondPlayer !=null)temp=chess。secondPlayer。getMyPoints()。get(chess。secondPlayer。getMyPoints()。size()-1);chess。secondPlayer.getMyPoints().remove(temp);chess。freePoints.add(temp)
25、;temp=chess。firstPlayer。getMyPoints().get(chess。firstPlayer.getMyPoints().size()-1);chess.firstPlayer。getMyPoints().remove(temp);chess。freePoints.add(temp);chess。freshCanvas(); if(chess。whoRun = 2)if(chess。firstPlayer.getMyPoints().size()=1 chess。secondPlayer !=null)temp=chess。firstPlayer.getMyPoint
26、s()。get(chess。firstPlayer.getMyPoints()。size()1);chess.firstPlayer。getMyPoints()。remove(temp);chess.freePoints。add(temp);temp=chess.secondPlayer。getMyPoints().get(chess。secondPlayer.getMyPoints()。size()-1);chess。secondPlayer。getMyPoints()。remove(temp);chess。freePoints.add(temp);chess。freshCanvas();
27、)首先获取ChessBoard对象,该对象继承自View,详细的定义了棋盘信息,主要负责显示棋盘的内容.接下来判断一下触发悔棋事件的是哪一个玩家,再判断是否符合悔棋的条件,这个条件很简单,就是棋盘上至少要有两个棋子。之后便进行悔棋操作,分别将两个玩家最后下的棋子取出,程序实现就是将两个ArrayList的最后一个元素remove出来,再分别放到记录棋盘中没有棋子的点的集合中,最后更新一下画布,主要是调用ChessBoard的invalidate()方法。通过以上步骤之后,呈现在我们面前的便是悔完棋的画面了。点击welButton2时,FIGHTINGMODE=2,之后的步骤便会点击welBut
28、ton1是相同的了,不同的是,由于对战模式的改变,从人人对战变成了人机对战。 点击welButton3时,通过initThree()方法调用setContentView(R。chess)方法实现网络对战。详细的对战实现细节将会在下文一一介绍。在这个界面中只保留了两个Button:b2和b4。这两个Button所实现的功能和上面的b2和b4是相同的。最后,welButton4比较简单。它所实现的功能为退出应用程序,调用System.exit(1)方法。4。3。2 游戏设置类的实现游戏设置表单用来对游戏参数进行设置,包括棋盘大小、先手选择、智能级别。表单中使用了 Gauge 和 Choice Gr
29、oup 两种高级用户界面组件.1、棋盘尺寸选择标准的五子棋棋盘为 1515,但为了满足不同玩家的需求,这里提供了大小为10*10 到 2020 的棋盘,用户可以通过 Gauge 组件改变。棋盘的最小值为 10,而Gauge 组件的最小值为 0,所以当前的 Gauge 值需要角上 10 才是当前棋盘大小。创建 Gauge 组件的代码如下:form = new Form(” 游戏设置”); / 创建参数设置表单并添加标签gauge Size = new Gauge(棋盘规格: ” + board Size + ” X + board Size, true, 10, board Size - 10)
30、; /棋盘规格 form。append(gauge Size);图4-4 棋盘尺寸的设计在Gauge交互模式下可以为Gauge对象所在的表单对象绑定一个Item State Listener 事件监听器,并在监听器上捕捉 Gauge 对象的事件,当 Gauge 的值发生变化时就会触发事件。这里将根据 Gauge 的当前值改变标签,显示当前的棋盘大小。其代码如下: public void item State Changed(Item item) if(item = gauge Size) /当 Gauge 组件发生变化时 int bs = gauge Size.get Value() + 10
31、; /获取当前的 Gauge 值并计算棋盘大小(加10) gauge Size。set Label(”棋盘规格: ” + bs + X ” + bs); /改变 Gauge 组件的标签 2、难度选择游戏的难易程度根据计算机的智能级别来控制,创建及添加选项的方法和复选框一样,所不同的是在创建 Choice Group 对象时,类型设置为 1(单选)。对于单选框,set Selected Index 只能用来指定某个选项被选中,因此,布尔值 selected 的值必然为 true,否则便没有意义。游戏共有 3 个难度级别,分别是:拜师学艺、棋行天下、谁与争锋(此游戏中并未作出区分),初始情况下为拜
32、师学艺,该选项的索引值为 0。创建难度选择单选框的代码如下: level = 1; /默认情况下的难度级别 choicelevel = new Choice Group(电脑智能级别:”, 1); /创建难度级别选项组choicelevel。append(”拜师学艺”, null); /难度 1 choicelevel.append(”棋行天下”, null); /难度 2 choicelevel。append(”谁与争锋”, null); /难度 3 choicelevel.set Selected Index(level1 , true); /设置默认情况为难度 1,索引值为0 form。
33、append(choicelevel); /将选项组添加到主表单中游戏设置选项表单还有两个 Command 对象,分别用于玩家却热和取消,所以表单需要监听软键事件和组件事件: public class Setting implements Command Listener, Item State Listener3、棋手选择选择先手和难度等级用 Choice Group 组件来实现。Choice Group 组件用来构造选择框,其构造函数如下:Choice Group(String label, int choice Type) 选择先手的选项框为选择组件,属性为复选框,标签名为空。创建好选择
34、组件后,逐条添加选项元素。添加选项的方法如下: int append(String string Part, Image image Part) 该方法追加一个选项元素到选择组中,追加的选项为选择组中的最后一个元素,选择组的大小加 1。对于多选类型的 Choice Group,还可以设置个别选项的选择状态。设置初始选择状态的方法如下: void set Selected Index(int element Num, Boolean selected) 这里创建一个只有一个选项元素的多选框用于玩家设置是否计算机先行,在默认情况下为true,创建完成多选框后将其添加到主表单中,代码如下: Comp
35、uter First = true; /在默认情况下为计算机先行choice First = new Choice Group(null, 2); /创建复选框choice First。append(”电脑先手”, null); /添加选项元素choice First。set Selected Index(0, Computer First); /设置多选框的默认状态form。append(choice First); /将多选框添加到主表单中4.3。3 棋子类的实现1、棋子的行列位置此五子棋游戏是一个二维棋类游戏,所以定了了一个 Dot 类来表示棋子。由于移动设备的局限性,所以程序不在下每一
36、步棋时生成一个对象,而是在游戏进行时,玩家或者计算机没下一步棋,在数组相应位置生成该棋子的对象,而将已经下过的棋子保存到数组中随时检索,这样可以避免过多棋子对象占用系统内存.Dot 类的 UML 图如图 45 所示:图4-5棋子行列设计Dot 类主要有两个变量 row 和 col,分别表示行和列:public int row; /行 public int col; /列2、检查越位棋子的位置并非是任意的,玩家和计算机每走一步棋之前都要线检查该位置的合法性,即棋子是否在棋盘上,否则判为无效落子.检查是否越界的代码如下: public boolean is In Board(int board S
37、ize) /判断棋子是否越界(超出棋盘)return row = 0 & row board Size col = 0 col board Size; 3、修改棋子位置在创建好 Dot 对象后,Dot 类提供了两种方法更改棋子位置,包括设置行列位置和从已有棋子中复制参数。public void set Row Col(int r, int c) /设置棋子位置 row = r; col = c; public void copy From(Dot d) /复制已有的棋子 row = d。row; col = d。col; 4.3.4 对战逻辑类的实现1、建立数据结构本程序以数组保存当前盘面的情
38、况,每个位置可能有三种状态:空、玩家的落子、计算机的落子,分别用 0、1、2 来表示.代码如下: public static int PLAYER_NONE = 0; /该位置为空 public static int PLAYER_COMPUTER = 1; /该位置有电脑的落子public static int PLAYER_HUMAN = 2; /该位置有玩家的落子棋盘在初始情况下为空,即棋子上没有任何棋子,在Gobang Logic类的构造函数中对棋盘进行初始化: table = new intboard Sizeboard Size;/创建棋盘数组for(int r = 0; r =
39、3) Dot d = new Dot();/创建棋子对象d.copy From(Dot)steps.pop());/从堆栈弹出的棋子中复制行列位置坐标 tabled。rowd。col = 0;/将该位置设置为空 game Canvas.repaint At(d.row, d。col); /在棋盘上重新绘制该位置 d。copy From((Dot)steps。pop());/从堆栈弹出的棋子中复制行列位置坐标 tabled.rowd。col = 0; /将该位置设置为空 game Canvas。repaint At(d.row, d。col); /在棋盘上重新绘制该位置 d。copy From((Dot)steps。peek());/获取栈顶对象,作为最后一步棋存储last Dot。copy From(d); game Canvas.repaint At(d.row, d。col); /重新绘制最后一步(添加引导框)return true; /悔棋成功 else return false; /悔棋失败 4。4 本章小结本章主要内容是游戏的实现,包括主类的实现,如构造函数、事件处理等,游戏帮助和介绍表单类的实现,游戏设置类的实现,如棋盘、选手、难度等,旗子类的实现,如棋子行列位置、检查越界等,对战逻辑类的实现,如落子和悔棋、逻辑运算等的实现.