1 绪论
1.1休闲类游戏开发简述
休闲类游戏(Casual Game)可以包含任何游戏类型的游戏性要素,通常休闲游戏的游戏规则相对简单,无需特别的技巧,也不需要较长的时间就能完成游戏。休闲游戏经常出现在不同的平台上,包括家用游戏机、掌上游戏机和个人电脑上等等。休闲类游戏通常也适合于各种不同性别、地域和文化的玩家群体。 本文所涉及的游戏项目,是一款棋类休闲游戏,基于 Windows 平台,无需较长的时间就能完成一局的游戏,一局游戏一般在 5 到 20 分钟左右就能完成。根据该局游戏玩家的游戏策略等因素,一局游戏的完成时间会有一定差别[1]。
在游戏开发团队中,团队成员主要有制片人等管理人员、程序员、美术(本文中的美术一词代表的是美术制作人员,在一些游戏公司中用 美术 这个词,而不用 美工 这个词)和策划(游戏设计师)。通常还需要音乐制作人员,但在多数公司中,主要的音乐制作任务都会交由独立音乐制作人或者专门的音乐制作公司完成。 早期传统的游戏程序是大多都是单线程执行的,这是由于游戏软件本身的内在逻辑,以及需要较高的实时交互性。如下一页的图所示,说明了通常一个游戏软件的运行流程。 在程序初始化阶段,进行内存分配,从硬盘读取资源,建立相关的辅助结构等。接下来进入游戏主循环不停的循环运行直到接收到退出游戏的指令或者消息。在主循环中,首先接收玩家的输入,然后根据游戏内部规则改变游戏的状态,然后进行图形图像处理,输出一帧游戏画面,接下来根据帧速率和游戏速度的要求,等待一段时间后继续执行主循环或者直接开始下一个循环[2]。
1.2中国象棋发展现状
中国象棋发展至今已有二千三、四百年的历史了,早在战国时期就有正式记载,堪称中国的国粹。据统计,中国有2亿人会下中国象棋,这相当于平均6至7个人中就有1人会下,可见它有着广泛的群众基础。方寸之间包含天地玄机,人生哲理,使人越是细玩越觉其味悠然深远。简单的棋规,便捷的载体,使得他成为雅俗共赏的竞技项目。然而,如今中国象棋的发展前景却令人堪忧。导致这种现象的主要因素有:第一,就东亚地区,围棋的影响力胜于中国象棋。第二,中国象棋虽然在国内很受欢迎,但在世界舞台上名声却不如国际象棋那么响亮。之所以会存在这两个因素,都是因为在国际赛事上中国象棋的参赛选手大都是华人,而非华裔的选手又大都是业余选手,这无疑使比赛失去了竞争力,而围棋有韩国和日本的参与,国际象棋有全世界国家的加入。第三,进入了21世纪后,供人们娱乐活动的选择越来越多,中国象棋在年轻人的心中已开始渐渐失去魅力。
各种中国象棋软件的开发与应用,尤其是在网络方面的推广,使得中国象棋得到了很好的宣传,全面展示了中国象棋的风采、优点与特色,同时也让外国人认识到中国象棋这一我国的“国粹”。 中国象棋打谱系统正是为这一可行性而开发设计的,玩家可以通过它很好的研究棋谱,学习中国象棋技术,提高水平。想必在未来的几年随着中国象棋在各个领域的推广,必定会带来巨大的影响和经济效益[3]。
1.3论文组织结构
第一章是绪论,主要介绍了休闲类游戏,中国象棋发展现状以及论文的组织结构。
第二章是相关技术简介,主要介绍了本次设计所用到的知识,如Java面向对象程序设计技术和鼠标事件模型处理。
第三章是需求分析及可行性研究,主要介绍了需求分析中的系统功能需求和系统性能需求;经济性、技术性和社会性可行性分析。
第四章是系统总体设计,主要介绍了系统设计的主要内容、软件结构图和游戏运行流程图。
第五章是系统详细设计,主要介绍函数的算法实现和流程图,是本论文的重点部分。
第六章是系统实现和测试,主要是介绍系统的主要实现界面和系统的测试方法和结果。
第七章是总结和展望,总结了所取得的成果和存在的不足之处,展望未来应改进的地方。
2 相关技术简介
2.1面向对象程序设计方法
Java面向对象程序设计的基本思想是通过构造和组织对象来求解问题的。对象是很好理解的,任何一种物体都可以被认为是对象,例如,汽车就是一种对象。对象具有两个最基本的属性:具有一定的物质基础和一定的功能,这两种基本属性在Java语言中表现为拥有一定的存储单元并具备一定的功能。理解了Java的这种面向对象程序设计的基本思想之后,在以后的编程过程中,就应当个构造人们容易理解的更接近于真实物理世界物体的对象。
Java面向对象程序设计的过程非常简单。这里介绍一种自顶向下的程序设计方法,首先构造一个类来处理需要解决的问题,然后根据已经拥有的类(例如以前求解其它问题时实现的类),分析和构造新的类,设法将问题分解下去,而最开始构造的类通过组织这些新构造的类、已有的类及由他们生成的实例来求解指定的问题。这个过程可以继续递归下去,用在新构造的类上,直到最后解决了指定的问题,例如Java的应用程序和小应用程序都必须有一个类作为入口求解问题。
在计算机语言中,面向对象的类一般具有三个基本特性:封装性、继承性和多态性,这三种特性进一步简化了Java求解模型,提高了程序的复用性,节省了程序的代码量,从而提高了软件的生产率[4]。
2.2Java技术
图形用户界面不仅可以提供各种数据的基本图形的直观表示形式,而且可以建立友好的交互方式,从而使计算机软件可以设计得十分简单易用。从Java语言诞生到现在,Java语言已经提供了两代图形用户界面。第一代图形用户界面AWT提供了基本的图形用户界面,它强烈地依赖于具体的计算机操作系统,而且缺少基本的剪贴板和打印支持功能。现在第二代图形用户界面Swing对AWT进行了扩展,Swing不仅在功能上比AWT强大,而且在很大程度上克服了AWT的上述缺点,它所设计的图形用户界面与具体的计算机操作相关性很小,而且可以定制指定的操作系统风格的图形用户界面[5]。
GUI(图形用户界面)组件构成了图形用户界面的基础。在Java程序设计中,要求按照一定的布局方式将组件安排在容器中,然后通过事件处理的方式实现人机交互,而容器本身也是组件,这样在容器中也可以含有容器,从而可以通过这种嵌套方式方便地组合各种组件。
事件处理模型是Java语言提供的一种人机交互模型,它使得用户能够通过鼠标、键盘或其他输入设备来控制程序的执行流程,从而达到人机交互的目的。对鼠标、键盘或其他输入设备的各种操作一般也称为事件。Java语言对这些事件的处理模型是采用面向对象的方法,即通过对象的形式把各种事件进行封装和处理。这种事件处理模型的三个基本要素是事件源、事件对象以及事件监听器。事件源是各种组件,是接受各种事件的对象。在各种事件源上运用鼠标、键盘或其他输入设备进行各种操作,就会有事件发生。每种操作一般都对应着事件,Java语言通过事件对象来包装这些事件。对事件进行处理是通过事件监听器实现的。
因为鼠标事件也是一种事件,所以对鼠标事件进行处理要遵循事件处理模型。鼠标事件的处理也是建立在事件源的基础之上,以事件对象本身,最后通过事件监听器进行处理。
类java.awt.event.MouseEvent包装常用的鼠标事件,例如,按下鼠标键和放开鼠标键等。类MouseEvent的实例对象记录了鼠标的当前位置和状态发生变化的鼠标键等。对鼠标事件进行处理最关键的是实现事件监听器接口。这些相关接口包括MouseListener,MouseMotionListener,MouseWheelListener,MouseInputListener。前面三个是包java.awt.event中的接口,最后一个接口MouseInputlistener来自包javax.swing.event。
接口Java.awt.event.MouseListener主要用来处理按下鼠标键、放开鼠标键、单击鼠标键、鼠标进入组件内和鼠标离开组件区域等事件。
对鼠标事件进行处理,就是要设计类,实现上面的鼠标事件监听器接口,然后在事件源中注册处理鼠标事件监听器的对象,以便对鼠标事件进行处理[6]。
3 需求分析与可行性研究
3.1需求分析
3.1.1系统功能需求
象棋盘由九道直线和十道横线交叉组成,棋盘上共有九十个交叉点,象棋子就摆放和活动在这些交叉点上。棋盘中间没有划通直线的地方,叫做“河界”;划有交叉线的地方,叫做“九宫”。九道直线,红棋方面从右到左用中文数字一至九来代表,黑棋方面从右到左用阿拉伯数字1至9来表示。
棋子共有三十二个,分为红、黑两组,每组十六个,各分七种,其名称和数目如下:
红棋子:帅一人,车、马、炮、相、士各两个,兵五个。
黑棋子:将一个,车、马、炮、象、士各两个,卒五个。
在对局时,由执红棋的一方先走,双方轮流各走一着,直至分出胜负或走成和棋为止。轮到走棋的一方,将某个棋子从一个交叉点到另一个空着的交叉点,或者吃掉对方的棋子而占领叉点,都算走了一着。双方各走了一着,称为一个回合。
帅(将)每一着只许走一步,前进、后退、横走都可以,但不能走出“九宫”。帅和将不准在同一直线上直接对面,如一方已先占据,另一方必须回避。
士每一着只许沿“九宫”斜线走一步,可进可退。
相(象)不能越过“河界”,每一着斜走两步,可进可退,即俗称相(象)走田字。当田字中心有别的棋子时,俗称塞相(象)眼,则不行走过去。
马每着走一直(或一横)一斜,可进可退,即俗称“马走日字”。如果在要去方向紧靠一直(或一横)的地方,有别的棋子挡住,俗称“蹩马腿”,就不能走过去。
车每一着可以直进、直退、横走,不限步数。
炮在不吃子的时候,走法同车一样。
兵(卒)在没有过“河界”前,每着只许向前直走一步;过“河界”后,每着可以向前走一步,也可以横走一步,但不能后退。
走一着棋时,如果己方棋子能够走到的位置有对方棋子存在,就可把对棋子吃掉而占领那个位置。只有炮吃了必须隔一个棋子(无论是哪一方的)跳吃,即俗称“炮打隔子”。除帅(将)外,其它棋子都可以听任对方吃,或主动送吃。
一方的棋子攻击对方的帅(将),并在下一着要把它吃掉,称为“将军”,或简称“将”。被“将军”的一方必须立即“应将”,即用自卫的着法去化解被“将”的状态。如果被“将军”而无法“应将”,就算被“将死”。轮到走棋的一方,帅(将)虽没被对方“将军”,却被禁在一个位置上无路可走,同时己方其它棋子也都不能走动,就算被“困毙”[7]。
系统实现:象棋棋谱界面,记录棋步,走棋与吃棋,胜负判断,设计中国象棋主菜单,下拉分菜单为制作棋谱,保存棋谱,演示棋谱,退出棋谱。
(1)制作棋谱:实现棋谱的显示,可以实战对弈功能,下棋规则的监督。
(2)保存棋谱:实现棋谱的保存,并生成一个棋谱文件,以便演示棋谱时使用。
(3)演示棋谱:实现对弈过程的演示,可以自动演示也可手动演示,并有时间的控制。
3.1.2系统性能需求
为了保证系统能够长期、安全、稳定、可靠、高效的运行,中国象棋游戏系统应该满足以下的性能需求:
(1)系统处理的准确性和及时性
系统处理的准确性和及时性是系统的必要性能。在系统设计和开发过程中,要充分考虑系统当前和将来可能承受的工作量,使系统的处理能力和响应时间能够满足用户对运行速度的需求。
(2)系统的开放性和系统的可扩充性
中国象棋游戏系统在开发过程中,应该充分考虑以后的可扩充性。例如,网络对战。这些都要求系统提供足够的手段进行功能的调整和扩充,而要实现这一点,应通过系统的开放性来完成,既系统应是一个开放系统,只要符合一定的规范,可以简单的加入和减少系统的模块,配置系统的硬件。通过软件的修补、替换完成系统的升级和更新换代。
(3)系统的易用性和易维护性
中国象棋游戏系统是直接面对用户的,要求系统能够提供良好的用户接口,易用的人机交互界面。要实现这一点,就要求系统应该尽量使用用户熟悉的术语和中文信息的界面;针对用户可能出现的使用问题,要提供足够的在线帮助,缩短用户对系统熟悉的过程。
(4)系统的标准性
系统在设计开发使用过程中都要涉及到很多计算机硬件、软件。所有这些都符合主流国际、国家和行业标准,例如在开发中使用的操作系统、网络系统、开发工具都必须符合通用标准,如ISO9002标准所要求的质量规范等;同时,在自主开发本系统时,要进行良好的设计工作,制订行之有效的软件工程规范,保证代码的易读性、可操作性和可移植性。
(5)系统的先进性
目前计算系统的技术发展相当快,做为中国象棋游戏,应该保证系统在未来几年仍旧是先进的,在系统的生命周期尽量做到系统的先进,充分完成娱乐的要求而不至于落后。这一方面通过系统的开放性和可扩充性,不断改善系统的功能完成;另一方面,在系统设计和开发的过程中,应在考虑成本的基础上尽量采用当前主流并先进且有良好发展前途的产品。
(6)系统的响应速度
中国象棋游戏在使用时的响应速度为秒级,达到实时的要求,保证对弈的双方不会因为速度问题而影响效果[8]。
3.2可行性分析
可行性研究所采用的方法和步骤:通过研究分析中国象棋游戏所具备的能力及实现的方法、确定主体结构。利用现阶段我所能达到的能力,以最简洁、最容易的办法,边开发边测试边修改,实现一个初级游戏软件。
管理可行性:该游戏软件为首次开发,用 Java软件编写后在Applet管理器中运行简单明了,所有功能均根据基本需求所做,便于管理,所以在这方面是可以实现的。
经济可行性:由于本游戏的主要背景是毕业设计,不注重直接的经济效益和其后的发展方向,只在注重自身水平和能力的提高,对自身的经济要求也不高,只要有一台能运行Java软件的电脑便可,所以不用考虑到经济问题。
技术可行性:可用与本游戏的编程语言有VB,Java,Delphi等,考虑到用于编写程序的困难度,和对语言的了解程度,选择Java作为编程语言。需要对变量定义、变量初始化、界面初始化、游戏初始化、然后进入游戏、处理游戏过程中的各种操作。
社会可行性:本游戏的开发作为毕业设计以巩固先前所学的知识,以个人为单位,仅供个人平常娱乐所用,无须考虑有可能造成的社会影响,不用考虑到法律、版权等的社会因素,所以在这方面是完全可行的。
3.3小结
本部分主要介绍了系统的需求分析和可行性分析。在需求分析中确定的系统的功能需求和性能需求,为中国象棋游戏的进一步开发奠定了基础;在可行性分析中,通过对游戏的管理可行性、经济可行性、技术可行性和社会可行性的研究,确定本游戏软件的技术成熟、完备,各方面均无重大问题,因此本游戏软件可开始着手编写。
4 总体设计
4.1设计内容
4.1.1设计内容
对阵双方各执一方战场,中间有空白河界相隔,俗称“楚河汉界”,在己方各有一个交点互相连接的田字格,俗称 “九宫格”、“王宫”,其余盘面都有纵横交错的线划分。
我们使用一个9 ×10 的二维数组表示棋盘,每一个元素代表棋盘上的一个交点,定义一个ChessPoint point [ ] [ ]二维数组,纵列从 1 到 9,横列从A到J,这样就表示了象棋棋盘的9 × 10的方阵,为每个棋子提供了相应的位置。象棋的棋子共 32 个,分为红黑两组,各16个,由对弈双方各执一组,兵种是一样的,分为 7种,红方:帅、仕、相、车、马、炮、兵。黑方:将、士、象、车、马、炮、卒。其中帅与将、仕与士、相与象、兵与卒的作用完全相同,仅仅是为了区分红棋和黑棋。中国象棋最特殊的就是棋子的行棋规则路线,本游戏最重要的也是对这些规则的实现方法。
系统实现:象棋棋谱界面,记录棋步,走棋与吃棋,胜负判断,设计中国象棋主菜单,下拉分菜单为:制作棋谱,保存棋谱,演示棋谱(见图4.1)。
(1)制作棋谱:实现棋谱的显示,可以实战对弈功能,下棋规则的监督。
(2)保存棋谱:实现棋谱的保存,并生成一个棋谱文件,以便演示棋谱时使用。
(3)演示棋谱:实现对弈过程的演示,可以自动演示也可手动演示,并有时间的控制。
图4.1 系统界面
4.1.2系统组成
中国象棋游戏共有8个Java源文件。
(1)Chess.java
该java文件生成的类负责创建本程序的主窗口。该类含有main方法,程序从该类开始执行。
(2)ChessBoard.java
该文件生成的类负责创建“对弈棋盘”对象,用户可以用鼠标拖到棋盘上的棋子,当松开鼠标时则将棋子放置到棋点。该棋盘不允许用户将棋子拖放到非棋点上,棋盘的Rule对象负责判断走棋是否遵守了中国象棋的规则,例如“马”走日、“象”走田、“小卒”一去不回头等等。如果用户的走法不符合规则,棋子将被放回原处,重新走。棋盘的MakeChessManual对象负责记录棋谱,当用户选择“保存棋谱”菜单项时,可以将当前棋谱保存成文件。另外,该棋谱还能进行悔棋,悔棋将不被记录到棋谱。
(3)ChessPoint.java
该文件生成的类负责创建棋盘的棋点对象。棋点对象可以判断该棋点上是否有棋子、指定当前棋点上的棋子、获取当前棋点上的棋子、移动当前棋点上的棋子。
(4)ChessPiece.java
该文件生成的类负责创建棋子对象,并绘制棋子的外观。棋子对象可以获取本身的颜色、大小、名字,也可以设置和获取本身的类别,即红棋或黑棋。
(5)MakeChessManual.java
该文件生成的类负责创建记录棋谱的对象,该对象可以将棋谱记录到一个链表中,并在一个文本区内显示棋谱。当对弈者悔棋时,该对象将修改棋谱,并更新文本区的信息。
(6)Rule.java
该文件生成的类负责创建“走棋规则”对象,该对象负责判断用户的走棋是否遵守了中国象棋规则。
(7)Demon.java
该文件生成的类负责创建“演示棋谱”对象,该对象可以演示已保存的棋谱。
(8)MoveStep.java
该类创建的对象负责记录一个棋步。
4.2系统结构图
中国象棋游戏共有8个java文件。重要类之间的相互调用关系如图4.2:
图4.2 软件结构图
4.3游戏流程图
游戏运行流程图如图4.3所示:
图4.3 游戏流程图
4.4小结
该部分主要是系统的总体设计,包括系统的设计内容、软件体系结构设计和游戏流程图三个部分。系统的设计内容部分详细介绍了棋盘的组成,系统的主要内容和主界面,系统包含的主要文件;软件体系结构设计介绍了各类之间的调用关系;游戏流程图部分介绍了系统的运行过程。总体设计这部分为系统的详细设计做好准备,使我们对系统有个大致的了解,方便我们以后设计。
5 系统详细设计
5.1主类设计
该java文件生成的类负责创建本程序的主窗口。该类含有main方法,程序从该类开始执行。
表5.1 成员变量
成员变量描述 | 变量类型 | 名称 |
菜单条 | JMenuBar | bar |
菜单 | JMenuItem | fileMenu |
菜单项 | JMnuBar | 制作棋谱,保存棋谱,演示棋谱 |
对弈的棋盘 | ChessBoard | board |
演示棋谱的棋盘 | Demon | demon |
棋谱记录者 | MakeChessManual | record |
保存棋谱的链表 | LinkedList | 棋谱 |
表5.2 方法
名称 | 功能 | 备注 |
Chess | 创建程序主窗口 | 构造方法 |
actionPerformed | 处理ActionEvent事件 | 接口方法 |
main | 程序开始运行 |
函数actionPerformed用于处理ActionEvent事件,设置菜单项JMenuItem 制作棋谱,保存棋谱,演示棋谱;if(e.getSouce()= =制作棋谱)是将制作棋谱事件传递给棋盘,
split.setDividerSize(5);
split.setDividerLocation(460);
con.add(split,BorderLayout.CENTER)这些语句是用来使制作棋谱事件得到处理。
if(e.getSouce()= =保存棋谱)是将保存棋谱事件传递给棋盘,设置outOne和outTwo分别为文件和对象输出流,outTwo.writeObject(record.获取棋谱())语句实现保存棋谱事件的处理。
if(e.getSouce()= =演示棋谱))是将演示棋谱事件传递给棋盘,
(ListedList)intTwo.readObjedt();con.add(demon,BorderLayout.CENTER);
con.add(demon,BorderLayout.CENTER);
con.validate();
label .setForeground(Color.red);
label.setHorizontalAlignment(SwingConstantants.CENTER);
con.add(label,BorderLayout.CENTER);这些语句用来打开已经保存的棋谱,并演示棋谱。
函数主要流程图如图5.1所示:
图5.1 函数actionPerformed流程图
5.2对弈棋盘类设计
表5.3 成员变量
描述 | 变量类型 | 名称 |
棋点数组 | ChessPoint[][] | point |
棋点的水平和垂直距离 | int | unitWidth,unitHeight |
棋盘的行数和列数 | int | x轴长,y轴长 |
棋子颜色 | String | 红方颜色,黑方颜色 |
棋子对象 | ChessPiece | 红车1……黑炮2 |
走棋规则对象 | Rule | rule |
负责记录棋谱的对象 | MakeChessManual | record |
表5.4方法
名称 | 功能 | 备注 |
ChessBoard | 创建对弈棋盘 | 构造方法 |
paintComponent | 绘制棋盘 | |
mousePressed | 返回棋子 | 接口方法 |
mouseMoved | 无 | 接口方法 |
mouseDragged | 拖动棋子移动 | 接口方法 |
mouseReleased | 将棋子放到棋点 | 接口方法 |
mouseEntered | 无 | 接口方法 |
mouseExited | 无 | 接口方法 |
mouseClicked | 无 | 接口方法 |
设置ChessPoint point[][]为棋点;整型变量UnitWidth,UnitHeight为棋盘单位格的宽和高;x轴长,y轴长为棋盘的行数和列数;x,y记录鼠标的位置;startX,startY记录棋子的初始坐标;startI,startJ记录棋子的初始棋点。
(1)函数ChessBoard是构造函数,用于创建对弈棋盘,point[i][j]=new ChessPoint(i*unitWidth,j*unitHeight,false)语句用于确定每个棋点,即棋盘的左上角的点是point[1][1],第一行的点分别是point[1][1],point[2][1],point[3][1]……point[x轴长][1],右下角的点是point[x轴长][y轴长]。
红车1=new ChessPiece(“车”,Color.red,bc,w-4,h-4,this);
……
红兵5=new ChessPiece(“兵”,Color.red,bc,w-4,h-4,this);
黑车1=new ChessPiece(“车”,Color.blue,bc,w-4,h-4,this);
……
黑卒5=new ChessPiece(“卒”,Color.blue,bc,w-4,h-4,this);
Point[1][10].setPiece(红车1,this);
……
……
Point[9][4].setPiece(黑卒5,this);
函数主要流程图如图5.2所示:
图5.2 函数ChessBoard流程图
(2)函数paintComponent用于绘制棋盘,
画出棋盘的横线
g.drawLine(point[1][j].x,point[1][j].y,point[x轴长][j].x,point[x轴长][j].y);
画出棋盘的竖线
g.drawLine(point[i][1].x,point[i][1].y,point[i][y轴长-5].x,point[i
][y轴长-5].y);
g.drawLine(point[i][y轴长-4].x,point[i][y轴长-4].y,point[i][y轴长].x,point[i][y轴长].y);
画出将帅区
g.drawLine(point[4][1].x,point[4][1].y,point[6][3].x,point[6][3].y);
g.drawLine(point[6][1].x,point[6][1].y,point[4][3].x,point[4][3].y);
g.drawLine(point[4][8].x,point[4][8].y,point[6][y轴长].x,point[
6][y轴长].y);
g.drawLine(pont[4][y轴长].x,point[4][y轴长].y,point[6][8].x,
point[6][8].y);
画出棋盘两边的提示坐标
g.drawingString(“ ”+i,i*unitWidth,unitHeight/2);
g.drawingString(“ ”+i*unitWidth/4,j*unitHeight);
函数主要流程图如图5.3所示:
图5.3 函数paintComponent流程图
(3)函数mousePressed处理按下鼠标事件,用于返回棋子,if(e.getSource()= =this)是如果在棋盘上按下鼠标,棋子不移动,piece=(ChessPiece)e.getSource()获取用户选中的棋子对象。startI=i;stattJ=j检查棋子所在的棋点。
函数主要流程图如图5.4所示:
图5.4 函数mousePressed流程图
(4)函数mouseDragged在棋子上拖动鼠标时,将导致棋子上发生鼠标拖动事件,if(e.getSource() instanceof ChessPiece)是如果在棋子上拖动鼠标,piece=(ChessPiece)e.getSource()是获取用户拖动的棋子对象;e=SwingUtilities.convertMouseEvent(Piece,e,this)是将鼠标拖动事件传递给棋盘,导致棋盘上发生鼠标拖动事件,e是传递给mouseDragged方法的参数。
函数主要流程图如图5.5所示:
图5.5 函数mouseDragged流程图
(5)函数mouseReleased处理放开鼠标事件,用于将棋子放到棋点上,if(e.getSource() instanceof ChessPiece)是如果在棋子上释放鼠标,piece=(ChessPiece)e.getSource())是获取用户释放的棋子对象;e=SwingUtilities.convertMouseEvent(Piece,e,this)是将鼠标释放事件传递给棋盘,导致棋盘上发生鼠标释放事件,e是传递给mouseReleased方法的参数。
函数主要流程图如图5.6和图5.7所示:
图5.6 函数mouseReleased流程图
图5.7 事件响应流程图
5.3棋子类设计
该文件生成的类负责创建棋子对象,并绘制棋子的外观。棋子对象可以获取本身的颜色、大小、名字,也可以设置和获取本身的类别,即红棋或黑棋。
表5.5 成员变量
描述 | 变量类型 | 名称 |
棋子的名称 | String | name |
棋子的背景色 | Color | backColor |
棋子的前景色 | Color | foreColor |
颜色的类别 | String | 颜色类别 |
表5.6 方法
名称 | 功能 | 备注 |
ChessPiece | 创建棋子对象 | 构造方法 |
paint | 绘制棋子的外观 | |
getWidth | 获取棋子的宽度 | |
getHeight | 获取棋子的高度 | |
getName | 获取棋子的名字 | |
获取棋子颜色 | 获取棋子的颜色 | |
set棋子类别 | 设置棋子的类别 | |
棋子类别 | 获取棋子的类别 |
5.4棋点类设计
该文件生成的类负责创建棋盘的棋点对象。棋点对象可以判断该棋点上是否有棋子、指定当前棋点上的棋子、获取当前棋点上的棋子、移动当前棋点上的棋子。
表5.7 成员变量
描述 | 变量类型 | 名称 |
棋点的位置坐标 | int | x,y |
棋点上是否有棋子 | Booean | 有棋子 |
表5.8 方法
名称 | 功能 | 备注 |
ChessPoint | 创建棋点对象 | 构造方法 |
isPiece | 判断棋点上是否有棋子 | |
set有棋子 | 设置棋点上是否有棋子 | |
setPiece | 在棋点上放置棋子 | |
getPiece | 获取棋点上的棋子 | |
reMovePiece | 移掉棋点上的棋子 |
5.5走棋规则类设计
该文件的类负责判断用户的走着是否遵守了中国象棋规则。
表5.9 成员变量
描述 | 变量类型 | 名称 |
走着棋子的起始棋点 | int | startI,startJ |
走着棋子的终止棋点 | int | endI,endJ |
表5.10 方法
名称 | 功能 | 备注 |
Rule | 创建走棋规则对象 | 构造方法 |
movePieceRule | 判定走棋规则 |
函数movePieceRule用于制定走棋的规则,流程图如图5.8所示:
图5.8 函数movePieceRule流程图
(1)车:
对每个棋子行棋时的初始和终止位置设置为start I,start J,endI,end J,用这4个值表示出棋子的初始及终止位置的坐标点。设置整型变量minI,maxI,minJ和maxJ分别表示初始坐标和终止坐标之间的横向和纵向的最小和最大间隔的值,
minI=Math.min(startI,endI),maxI=Math.max(startI,endI),minJ=Math.min(startJ,endJ),maxJ=Math.max(startJ,endJ)。
车的走棋规则是按照直线行走,且起点和终点之间无其他棋子。if(startI= =endI)是沿纵向行走,if(point[startI][j].isPiece())表示起点和终点之间的棋点上有棋子,可否走棋=false表示不能走棋;if(j=maxJ)表示起点和终点间无棋子,可否走棋=true表示可以走棋。
if(startJ= =endJ)是沿横向行走,if(point[i][startJ].isPiece())表示起点和终点之间的棋点上有棋子,可否走棋=false表示不能走棋;if(j=maxI)表示起点和终点间无棋子,可否走棋=true表示可以走棋。
其主要流程图如图5.9所示:
图5.9 车行走流程图
(2)马:
首先我们对每个棋子行棋时的初始和终止位置设置为start I ,start J ,endI ,end J,用这4个值表示出棋子的初始及终止位置的坐标点。
接着 ,我们再定义两个整型变量 xAxle 和yAxle,它们分别表示初始与终止坐标值之间差值的绝对值,xAxle = Math. abs ( start I - endI) ,yAxle =Math. abs (start J - end J )。
马的走棋规则是走“日”字形,且无“别腿”。if(xAxle= =2&&yAxle= =1)是横向走两个棋点,纵向走一个棋点;if(endI>startI)是向前跳,if(point[startI+1][startJ].isPiece())是在发生“别腿”的棋点处有棋子,可否走棋=false语句表示不可走棋;if(endI<startI)是向后跳,if(point[startI-1][startJ].isPiece())是“别腿”棋点处有棋子,可否走棋=false语句表示不可走棋。
if(xAxle= =1&&yAxle= =2)是纵向走两个棋点,横向走一个棋点;if(endJ>startJ)是向前跳,if(point[startI][startJ+1].isPiece())是“别腿”棋点处有棋子,可否走棋=false语句表示不可走棋;if(endJ<startJ)是向后跳,if(point[startI][startJ-1].isPiece())是“别腿”棋点处有棋子,可否走棋=false语句表示不可走棋。
其主要流程图如图5.10所示:
图5.10 马行走流程图
(3)象:
首先我们对每个棋子行棋时的初始和终止位置设置为start I ,start J ,endI ,end J这样就可以用这4个值表示出棋子的初始及终止位置的坐标点。
接着 ,我们再定义两个整型变量 xAxle 和yAxle,它们分别表示初始与终止坐标值之间差值的绝对值,xAxle = Math. abs ( start I - endI) ,yAxle =Math. abs (start J - end J );同时再设置两个整型变量centerI和centerJ分别表示起点和终点坐标的中点,centerI=(startI+endI)/2,centerJ=(startJ+endJ)/2。
象的走棋规则是象走“田”字形,且象眼处无棋子。if(xAxle= =2&&yAxle= =2&&endJ<5)是横向走两个棋点,纵向走两个棋点,即“田”,不可以过河;if(point[centerI][centerJ].isPiece())是象眼棋点上有棋子,可否走棋=false表示不可走棋。
其主要流程图如图5.11所示:
图5.11 象行走流程图
(4)炮:
对每个棋子行棋时的初始和终止位置设置为 start I ,start J ,endI ,end J,这样就可以用这4个值表示出棋子的初始及终止位置的坐标点。
设置整型变量number记录棋子起点和终点之间的棋子数目。
if(startI= =endI)是炮沿纵向行走,number++语句用于计算起点和终点之间的棋子的数目。
if(number>1)是起点和终点间的棋子多于一个,可否走棋=false表示不可走棋。
if(number= =1)和if(number= =0)为起点和终点间的棋子有一个和一个没有的情况,可否走棋=true表示可以走棋。
if(startJ= =endJ)是炮沿横向行走,number++语句用于计算起点和终点之间的棋子的数目。
if(number>1)是起点和终点间的棋子多于一个,可否走棋=false表示不可走棋。
if(number= =1)和if(number= =0)为起点和终点间的棋子有一个和一个没有的情况,可否走棋=true表示可以走棋。
其主要流程图如图5.12所示:
图5.12 炮行走流程图
(5)兵:
兵的行棋规则分为两种情况:一种是棋子没过河界的时候;一种是棋子过河界的时候。在棋子没过河界时,兵只能向前挪动一次,一次一格,不能后退;当棋子越过河界以后,兵可以向前、 向左、 向右移动,一次一格,仍然不能后退。
首先我们对每个棋子行棋时的初始和终止位置设置为start I ,start J ,endI ,end J这样就可以用这4个值表示出棋子的初始及终止位置的坐标点。
接着 ,我们再定义两个整型变量 xAxle 和yAxle,它们分别表示初始与终止坐标值之间差值的绝对值,xAxle = Math. abs ( start I - endI) ,yAxle =Math. abs (start J - end J )。
兵的行棋思路如图5.13所示:
图5.13 行棋图
每步只能向前移动一点。过河以后,它便增加了向左右移动的能力,但不允许向后移动。当if (end J < = 5) 没有越过河界,则兵只能向前走一格,行至( x2 , y1)点,我们通过if (start J - end J = = 1&&xAxle = = 0)这样一条语句来限定,start J - end J = = 1表示纵坐标始末位置之间差为1,即限制它只能向前走一步 ,且不能向后走;xAxle = = 0表示横坐标始末位置之间差的绝对值为0,即限制它不能左右偏移移动。当if (end J > = 6) ,即越过河界的情况,这样兵就增加了向左右移动的能力,不仅可以移动到( x2 , y1) ,还可以移动到( x1 , y2)和( x3 , y2)两点,原有的向前移动能力仍然用 if ( ( start J - end J = =1) &&( xAxle = = 0) )语句来给予限定,原理同上;另一种是增加的左右移动能力,用if ( (end J - start J= = 0) &&( xAxle = = 1) )语句来限定,end J - start J= = 0表示纵坐标始末位置之间差为0,即兵没有向前行,也没有向后退;xAxle = = 1 则表示了横坐标始末位置之间差的绝对值为 1 ,即棋子可以左右移动一格。
其主要流程图如图5.14:
图5.14 兵行走流程图
(6)士:
对每个棋子行棋时的初始和终止位置设置为start I ,start J ,endI ,end J,这样就可以用这4个值表示出棋子的初始及终止位置的坐标点。
接着 ,我们再定义两个整型变量 xAxle 和yAxle,它们分别表示初始与终止坐标值之间差值的绝对值,xAxle =Math.abs ( start I-endI),yAxle =Math.abs (start J - end J )。if(endI<=6&&endI>=4&&xAxle= =1&&yAxle= =1)中endI<=6&&endI>=4表示棋子在九宫中行走,xAxle= =1&&yAxle= =1表示棋子只能斜走。
其主要流程图如图5.15所示:
图5.15 士行走流程图
(7)将或帅:
对每个棋子行棋时的初始和终止位置设置为start I ,start J ,endI ,end J,这样就可以用这4个值表示出棋子的初始及终止位置的坐标点。
接着,我们再定义两个整型变量 xAxle和yAxle,它们分别表示初始与终止坐标值之间差值的绝对值,xAxle =Math.abs ( start I-endI),yAxle =Math.abs (start J - end J )。if(endI<=6&&endI>=4)中endI<=6&&endI>=4表示棋子在九宫中行走,if((xAxle= =1&&yAxle= =0)||(xAxle= =0&&yAxle= =1))表示棋子只能横向走一格或纵向走一格。
其主要流程图如图5.16所示:
图5.16 将或帅流程图
5.6记录棋谱类设计
该文件生成的类负责创建记录棋谱的对象,该对象可以将棋谱记录到一个链表中,并在一个文本区内显示棋谱。当对弈者悔棋时,该对象将修改棋谱,并更新文本区的信息。
表5.11 成员变量
描述 | 变量类型 | 名称 |
显示棋谱的文本区 | JTextArea | text |
记录棋谱的列表 | LinkedList | 棋谱 |
记录吃掉的棋子的列表、备悔棋用 | LinkedList | 吃掉的棋子 |
表5.12 方法
名称 | 功能 | 备注 |
MakeChessManual | 创建“记录棋谱”对象 | 构造方法 |
记录棋谱 | 记录棋谱 | |
actionPerformed | 处理ActionEvent事件,悔棋 | 接口方法 |
函数actionPerformed用于处理ActionEvent事件,主要是处理悔棋这种情况时的应对方法,
if(temp.equals(“没吃棋子”))即上一步的移动没有吃掉棋子,设
int startI=lastStep.pStart.x;
int startJ=lastStep.pStart.y;
int endI=lastStep.pEnd.x;
int endJ=lastStep.pEnd.y; 分别用这4个值表示出棋子的初始及终止位置的坐标点。
point[startI][startJ].setPiece(piece,board);
(point[endI][endJ].set有棋子(false));用于将棋子放回原处。
if(temp.equals(“吃了棋子”))即上一步的移动没有吃掉棋子,
point[startI][startJ].setPiece(piece,board);用于将悔棋方的棋子放回起始棋点。
point[endI][endJ].setPiece(removePiece,board);
(point[endI][endJ].set有棋子(true));用于将被吃掉的棋子放回原处。
其主要流程图如图5.17所示:
图5.17 函数actionPerformed流程图
5.7棋谱演示类设计
该文件生成的类负责创建“演示棋谱”对象,该对象可以演示已保存的棋谱。
表5.13 成员变量
描述 | 变量类型 | 名称 |
保存棋谱的链表 | LinkedList | 棋谱 |
自动演示棋谱的线程 | Thread | 自动演示 |
用户输入的时间间隔 | JTextField | 时间间隔 |
按顺序手动演示 | JButton | next |
重新演示 | JButton | reply |
开始自动演示 | JButton | auto |
暂停或继续自动演示 | JButton | stop |
表5.14 方法
名称 | 功能 | 备注 |
Demon | 创建棋谱演示对象 | 构造方法 |
set棋谱 | 设置棋谱 | |
actionPerformed | 处理ActionEvent事件 | 接口方法 |
函数actionPerformed用于处理ActionEvent事件,
if(e.getSource()= =next)是传递下一步事件给棋盘;演示一步(index);语句处理下一步事件。
if(e.getSource()= =reply)是传递重新演示事件给棋盘,
splitH.remove(board);
splitH.setDividerSize(5);
splitH.setDividerLocation(460);
splitH.setLeftComponent(board);
splitH.validate();
index=-1;
text.setText(null);这些语句用于处理reply事件。
if(e.getSource()= =auto)是传递自动演示事件给棋盘。
time=1000*Integer.parseInt(时间间隔.getText().trim());处理auto事件。
if(e.getSource()= =stop)是传递暂停演示事件给棋盘。
演示过程=“暂停演示”;
stop.setText(“继续演示”);
stop.repaint();这些语句用于处理stop事件。
其主要流程图如图5.18所示:
图5.18 函数actionPerformed流程图
5.8小结
详细设计是系统设计的关键阶段,系统的详细设计主要包括主类设计、对弈棋盘类设计、棋子类设计、棋点类设计、走棋规则类设计、步骤类设计、记录棋谱类设计和棋谱演示类设计。
对于中国象棋游戏来说,系统的各个功能模块的设计非常重要,直接关系到系统的实现,好的设计会加快系统实现的速度。而界面是人机交互的接口,好的界面设计会使人容易理解系统的功能并接受系统。详细设计完成之后,下一步的工作就是如何实现系统了。
6 系统实现与测试
6.1系统开发环境
中国象棋游戏的主要开发环境如表6.1所示
表6.1 开发环境
操作系统 | Windows2000/XP |
内存容量 | 128MB |
显卡要求 | 8M |
声卡要求 | 支持DirectX 9.0 音效卡 |
交互工具 | 键盘/鼠标 |
CPU | 奔腾4以上 |
光驱 | 8倍速以上 |
硬盘空间 | 400MB |
显示器 | VGA以上显示器 |
开发软件 | Java可视化编程3.0以上版本 |
6.2系统实现
按照系统总体设计中设想的系统要实现的目标,完成了制作棋谱、保存棋谱和演示棋谱三大功能的实现,其实现界面如下图所示:
(1)制作棋谱:实现棋谱的显示,可以实战对弈功能,下棋规则的监督(见图6.1)。
图6.1 制作棋谱图
(2)保存棋谱:实现棋谱的保存,并生成一个棋谱文件,以便演示棋谱时使用(见图6.2)。
图6.2 保存棋谱图
(3)演示棋谱:实现对弈过程的演示,可以自动演示也可手动演示,并有时间的控制(见图6.3和图6.4)。
图6.3 演示棋谱图
图6.4 演示结束图
6.2系统测试
编码结束后,我们对系统进行了测试,测试的步骤按照软件工程中的软件测试步骤来进行:
(1)单元测试
单元测试是用来测试系统的各个功能模块的功能是否达到预期的结果。对于中国象棋游戏系统,将系统的8个Java文件分别作为一个单元来测试。
(2)集成测试
集成测试是将系统的各个功能模块集成到一起,测试系统的整体功能。对于中国象棋游戏,模拟从制作棋谱、走棋、将军、保存棋谱和演示棋谱整个流程,直到显示图6.5,游戏开发才算成功。
图6.5 测试结果
7 总结与展望
7.1总结
在做毕业设计之前,我对软件的开发流程只是停留在理论水平上,缺乏实际的软件开发经验,对于Java语言的理解也是停留在基础语法水平上。但是通过这次做毕业设计,即中国象棋游戏的开发任务,将Java应用到实际项目中,掌握了软件工程的实际应用,积累了开发经验。在整个开发过程中遇到诸多问题,如鼠标事件的处理、走棋算法的设计等,但最终还是被解决。
在初期,确实遇到很多不懂得问题,但通过在图书馆中查阅各种资料、问老师、问同学和上网搜索,使问题一一被解决。在这个过程中,我不仅学到很多的知识,还提高了自学的能力和学会了如何与他人交流,这会对我以后的人生提供极大地帮助。
作为一个不断学习新知识的过程,从设计初的不懂到最后能够顺利完成,使我意识到学习的重要性,以后我会抓紧时间学习,这对于即将开始的研究生学习具有重要意义。
通过毕业设计,使我理解了如何将软件工程应用到实践中,使我对Java有了更深的理解,同时也对游戏的开发有了一定得了解,学到了许多书本上没有学到的知识,丰富了自己的实践技能,扩展了本专业的知识面,使我受益非浅,同时也体验到了搞软件开发的困难度,对我今后的学习打下了一定的基础。
本文给出了基于Java 技术中国象棋游戏的设计实现方法,对中国象棋游戏特殊的规则提出了一种实现算法,并通过实验得以成功实现。但由于我对这样的软件开发还只是一个开始,其中还有很多的不足,有些功能还不能够完全的实现,如没有实现人机对弈和网络对战的功能。因此做得不是很好,界面的设计及整体的布局还比较简单,没有突出特色出来,这也可能是我这个系统的不足之处,在这里也恳请各位老师能够指明不足并提出修改意见。
7.2展望
经过对本游戏的开发环境、开发框架、游戏规则、相关约束规则以及界面需求的分析,做出了相应的设计方案,最终完成了该项目,并取得了预期的效果。但也还存在很多方面需要在本游戏的下一个版本中得到改进,以及未来可以继续发展和完善的方面:
(1)对游戏逻辑模块进行优化,进一步提高时间性能和可扩展性,以适应将来新的需求和变化。
(2)引入人工智能,实现人机对弈的效果。
(3)加入客户端的建立,实现网络对战的功能
(4)设置游戏级别,满足不同玩家的需求。
(5)加入新手教学模块,为新玩家介绍一些游戏技巧,并给出相关的演示。
参考文献
[1]荣钦科技. Java 2游戏设计[M]. 北京: 清华大学出版社, 2004, 4-10
[2]David Wallace Croft著, 彭晖译. Java游戏高级编程[M]. 北京: 清华大学出版社, 2005, 66-76
[3]张恒汝. Java十大经典案例[M]. 北京: 科学出版社, 2005, 116-122
[4]Herbert Schildt著, 邓劲生译. Java编程艺术[M]. 北京: 清华大学出版社, 2004, 67-104
[5]耿祥义. Java课程设计[M]. 北京: 清华大学出版社, 2004, 99-133
[6]雍俊海. Java程序设计[M]. 北京: 清华大学出版社, 2004, 55-89
[7]徐志坚. 电脑象棋的设计与实现[D], 广东广州: 中山大学, 2004
[8]张海藩. 软件工程导论[M]. 北京: 清华大学出版社, 2003, 36-65
[9]John Lewis. Java Software Solutions[M]. London: Addison Wesley Longman, 2002, 88-102
[10]Harvey M Deitel, Paul J Deitel. Java How to Program(Fourth Edition)[M]. Beijing: Publishing House of Electronics Industry, 2002, 45-78