毕业论文范文网-论文范文
电气工程 会计论文 金融论文 国际贸易 财务管理 人力资源 学前教育 德语论文 工程管理 文化产业 工商管理 会计专业 行政管理 广告学
机械设计 汉语文学 英语论文 物流论文 电子商务 法律论文 工商管理 旅游管理 市场营销 药学论文 播音主持 人力资源 金融论文 保险学
制药工程 生物工程 包装工程 模具设计 测控专业 工业工程 教育管理 行政管理 计算机论 电子信息 市场营销 法学论文 财务管理 投资学
体育教育 小学教育 印刷工程 土木工程 书法论文 护理论文 心理学论 信息管理 公共事业 给水排水 新闻专业 摄影专业 广电编导 经济学
  • 范文首页 |
  • 毕业论文 |
  • 论文范文 |
  • 计算机论文 |
  • 外文翻译 |
  • 工作总结 |
  • 工作计划 |
  • 现成论文 |
  • 论文下载 |
  • 教学设计 |
  • 免费论文 |
  • 原创论文 |
搜索 高级搜索

原创毕业论文

当前位置:毕业论文范文网-论文范文 -> 免费论文 -> 计算机论文

网络游戏开发

作者: 浏览:12次
免费专业论文范文
免费专业论文
政治工作论文
计算机论文
营销专业论文
工程管理论文范文
医药医学论文范文
法律论文范文
生物专业论文
物理教学论文范文
人力资源论文范文
化学教学论文范文
电子专业论文范文
历史专业论文
电气工程论文
社会学专业论文
英语专业论文
行政管理论文范文
语文专业论文
电子商务论文范文
焊工钳工技师论文
社科文学论文
教育论文范文
数学论文范文
物流论文范文
建筑专业论文
食品专业论文
财务管理论文范文
工商管理论文范文
会计专业论文范文
专业论文格式
化工材料专业论文
英语教学专业论文
电子通信论文范文
旅游管理论文范文
环境科学专业论文
经济论文
人力资源论文范文
营销专业论文范文
财务管理论文范文
物流论文范文
财务会计论文范文
数学教育论文范文
数学与应用数学论文
电子商务论文范文
法律专业论文范文
工商管理论文范文
汉语言文学论文
计算机专业论文
教育管理论文范文
现代教育技术论文
小学教育论文范文
机械模具专业论文
报告,总结,申请书
心理学论文范文
学前教育论文范文

收费计算机专业论文范文
收费计算机专业论文
Delphi
ASP
VB
JSP
ASP.NET
VB.NET
java
VC
pb
VS
dreamweaver
c#.net
vf
VC++
计算机论文
毕业论文范文题目:网络游戏开发,论文范文关键词:网络游戏开发
网络游戏开发毕业论文范文介绍开始:

毕业设计(论文)中文摘要

网络游戏开发

摘  要:本课题旨在用VC++ 6.0实现一个基于C/S模式的五子棋网络对战游戏。作为客户端的玩家可以通过服务器端与其它玩家进行对战、聊天等功能。
本软件的开发和设计主要采用基于Windows的面向对象的开发工具Visual C++ 6.0进行编程,再嵌入WinSock类,增加该设计对网络的支持。通过编程可实现在网络连接之后人与人在网上对战,以及观看电脑和电脑对战的演示功能。并且,也可以在游戏之后,保存棋局,在之后可以随时的打开以前的棋局,观看战局的情况。
程序经过测试后,可以良好运行,且界面清晰,功能全面,菜单能正确有效的表明其所能表达的功能,而程序内部使用了一个链表,使得每盘的棋谱都能够正确的保存,以方便将来战术的研究。该网络游戏可以集娱乐和益智于一体,随着网络的发展能够得到广泛的应用。

关键词:五子棋  网络  VC++ 6.0

 

毕业设计(论文)外文摘要

Development of the network game

Abstract: This topic is for the purpose of with VC++ 6.0 realizing based on the C/S pattern Gobang network to the fight game. Players as the client to be allowed to play game through the server with other to carry on fighting, chatting and so on.
 This project is developed with Visual C++ 6.0, which is a developing tool. And use WinSock for the network supporting, and supports this design to the network. May realize through the programming after the network connection, two players can play against each other on line. The purpose of this project is not only play against computer, but also with your friends on line, and even a demo with two computers. Besides we can save our chessboard after game for discussing, of course, at any time you want.
 After the test, the system could work better. And the interface was cleared, the function was comprehended. The menu could express the function in a correct effective way. At the same time, the project used a linked-list to save the chessboard for your study tactics. This network game may with entertainment and intelligence. With the network development, the system can obtain the widespread application.
 
Keywords: Gobang; network; VC++ 6.0.

 

 

 

 

 

 

 

目    录

1  引言 1
1.1  课题的研究意义和背景 1
1.1.1  研究的意义 1
1.1.2  研究背景 1
1.2  前期调研 2
1.3  课题简介与分析 3
1.3.1  课题简介 3
1.3.2  课题分析 3
1.4  开发途径和可行性分析 3
1.4.1  开发途径 3
1.4.2  可行性分析 3
1.5  传统五子棋软件与网络五子棋软件的异同 4
2  网络五子棋软件的系统需求分析与总体设计 5
2.1 系统需求分析 5
2.2  总体设计 5
2.2.1  总体设计结构图 5
2.2.2  接口设计 6
2.2.3  运行设计 6
2.2.4  安全保密设计 6
3  通信协议及编程语言的分析 6
3.1  TCP/IP参考模型 6
3.2  管套Socket与Winsock 7
3.3  网络游戏通信协议 8
3.3.1  游戏通信协议简介 8
3.3.2  协议打包/解包 8
3.3.3  通信协议的选择 9
3.4  编程语言的分析 9
3.4.1  Visual C++ 6.0 概述 9
3.4.2  Visual C++ 6.0 的特点 9
4  详细设计与开发 9
4.1  软件架构 10
4.1.1  棋盘类 10
4.1.2  游戏模式类 10
4.2  主要算法 11
4.2.1  判断胜负的算法设计 11
4.2.2  人机对弈算法设计 12
4.3  消息机制 17
4.3.1  消息机制的架构 17
4.3.2  各种消息说明 17
4.4  棋盘类——Ctable的设计 19
4.4.1  主要成员变量说明 19
4.4.2  主要成员函数说明 20
4.5  游戏模式类——Cgame的设计 22
4.5.1  主要成员变量说明 23
4.5.2  主要成员函数说明 23
5  网络五子棋软件的测试 24
5.1  问题的发现 24
5.2  问题的解决 24
6  系统功能评价 25
6.1  系统的主要功能 25
6.2  系统存在的不足与改进方案 25
7  用户使用手册 25
7.1  运行环境简介 25
7.2  系统功能简介 25
7.3  系统运行与操作指南 26
8  毕业设计心得体会 26
结  论 27
致  谢 28
参 考 文 献 29
附 录 30

1  引言
 游戏,如今俨然已经成为人们生活中不可或缺的元素。游戏可以简单地理解为调节人们生活节奏和缓解人们生活压力的一种手段。现在理解的游戏基本意义都是娱乐性质较浓,要有特定的行为模式,遵循一定规则以达到游戏者目的的行动。游戏的最初目的就是让游戏者(玩家)在游戏中得到放松。游戏一直存在于人类活动中,如今定义的游戏从早期的猜谜游戏,发展到如今的单机RPG游戏,网络游戏等,已经升华为更高级别意义上的娱乐活动,对人们的感官刺激也越发强烈,得到的乐趣也更多。
 基于Windows系统平台的图形用户界面网络五子棋,结合Microsoft Visual C++ 6.0,更能深受人们的欢迎。对于一个好的网络五子棋游戏而言,重要的是如何让其更贴近于玩家,让游戏者真正能在游戏中体验到五子棋游戏的乐趣。对于设计者而言,就是要在游戏中设计更多的变量元素以及更多变的算法,使游戏更具真实感。本设计考虑最多的就是这点,让游戏更能真实地表现出来。
1.1  课题的研究意义和背景
1.1.1  研究的意义
 随着计算机技术的发展,计算机已不再只是一种工具,人们也利用它为生活带来了娱乐,使人们享受到了科技为生活带来的快乐。在计算机越来越普及的今天,人们已不满足于利用计算机作为工作和学习的工具,计算机已进入了多媒体、视频、游戏的领域,为人们的生活增添了许多色彩。
 基于Windows系统平台的图形用户界面的操作使得网络游戏越来越受到人们的欢迎。本课题主要研究的是基于网络的五子棋游戏,该游戏主要是通过计算机的相关技术,把日常生活的游戏利用计算机模拟出来,便于玩家用于休闲娱乐,既可以益智又可以通过游戏交流一下技巧和心得。随着网络的普及,该益智游戏正日益广泛地深入到人们日常的工作、学习和生活中。
1.1.2  研究背景
 国内的发展趋势是以二维为基础,逐渐发展和拓宽3D游戏,未来国内的网络游戏市场应该会呈现出一定的垄断,只有像金山、目标、洪恩、盛大、九城、光通等技术实力深厚、资金充裕的大厂才能存活下来。在用户胃口不断提高的情况下,像网络游戏市场形成初期靠小产品、小制作、大行销就能占据足够市场份额的情况应该不复再现了。
 国外的(欧美等国家)是以三维的研究对象,进行各方面的开发,3D游戏发展越来越迅速,对电脑的要求也越来越高,既推动了游戏界的发展又推动了电脑的更新换代,在游戏界起着领头羊的作用。
 现在的网络游戏多是基于Internet上客户/服务器模式,服务端程序运行在游戏服务器上,各地的玩家可以通过运行客户端程序同时登录到游戏中。简单地说,网络游戏实际上就是由游戏开发商提供一个游戏环境,而玩家们就是在这个环境中相对自由和开放地进行游戏操作。我们要做的工作就是分析客户端和服务器之间往来的数据,这样我们就可以提取到对我们有用的数据进行修改,然后模拟服务器发给客户端,或者模拟客户端发送给服务器,这样就可以实现我们修改游戏的目的了。
1.2  前期调研
 电脑游戏行业经过二十年的发展,已经成为与影视、音乐等并驾齐驱的全球最重要的娱乐产业之一,其年销售额超过好莱坞的全年收入。互联网的出现为电脑游戏行业发展注入了新的活力,凭借信息双向交流、速度快、不受空间限制等优势,让真人参与游戏,提高了游戏的互动性、仿真性和竞技性,使玩家在虚拟世界里可以发挥现实世界无法展现的潜能,改变了单机版游戏固定、呆板、与机器对话的状况。网络游戏的这些优势不仅使其在电脑游戏行业中异军突起并在某种程度上取代了单机版游戏,而且成为网络业三大(网上金融、网上教育和网络游戏)赢利且利润优厚的领域之一。
 现在的网络游戏多是基于Internet上客户/服务器模式,服务端程序运行在游戏服务器上,游戏的设计者在其中创造一个庞大的游戏空间,各地的玩家可以通过运行客户端程序同时登录到游戏中。简单地说,网络游戏实际上就是由游戏开发商提供一个游戏环境,而玩家们就是在这个环境中相对自由和开放地进行游戏操作。知道了这个道理,接下来我们要做的工作就是分析客户端和服务器之间往来的数据,这样我们就可以提取到对我们有用的数据进行修改,然后模拟服务器发给客户端,或者模拟客户端发送给服务器,这样就可以实现我们修改游戏的目的了。
 在调研时我了解了不少网络游戏的知识,由于自己的水平有限,对那些3D的游戏里面需要的东西,自己一个人是无法实现的,它对游戏的开发要求很高,所以我就多了解了一些二维的游戏,现在五子棋游戏一直很流行,适合不同年龄段的人娱乐,可以说90%以上的人都玩过,手机里生产商也会放入这款游戏。
 目前在国外,休闲游戏如棋类等,玩家的年龄跨度非常大,这和我国目前网游市场以青少年为主要消费人群的状况截然不同。其实,网络可以解决空间的问题,网络和生活越来越息息相关,因此,开辟适合各个年龄层的游戏产品迫在眉睫。同时,这也涉及到一个企业开发的能力。娱乐产业发展到一定的程度,通过不断锻炼和经验的积累,完全可以通过融入娱乐的成分把教条的东西深入浅出地展现给消费者。
 就国内的发展来看,最近的这两三年内国内的游戏公司如雨后春笋般的成立,所开发或代理的网络游戏更是不胜枚举。以全球游戏业界的发展来看,这几年韩国的表现最为突出,特別是在网络游戏的技术研发与游戏制作方面,其所发行的网络游戏更成为全球游戏产业重要的指标之一。去年在美国洛杉矶所举行的E3(Electronic Entertainment Exposition)展中,已经有几家韩国厂商挤入世界第一线的游戏开发厂商之列。
 近几年来,由于3D硬体绘图技术的突破,使得即时描绘的书面越来越精致,而且3D遊戏具有多元化性,且更逼近真实世界,因此在遊戏产业中,3D 游戏已经逐渐取代2D游戏为游戏市场的主流,即使是网络游戏,也慢慢趋向3D化。然而游戏3D化将会带来的游戏开发上的困难等问题,这些问题以后都需要逐步解决。
 五子棋作为一个棋类竞技运动,在民间十分流行,这次开发的目的是使这个程序不光有与电脑对战的功能,更有在网络连接之后人与人在网上对战,以及观看电脑和电脑对战的演示功能。并且,我们也可以在游戏之后,保存棋局,在之后可以随时的打开以前的棋局,观看战局的情况。
1.3  课题简介与分析
1.3.1  课题简介
本人这次毕业设计的课题是:网络五子棋的开发。五子棋作为一个棋类竞技运动,深受人们的喜爱,这次开发的目的是使这个程序不光有与电脑对战的功能,更有在网络连接之后人与人在网上对战,以及观看电脑和电脑对战的演示功能。并且,我们也可以在游戏之后,保存棋局,在之后可以随时的打开以前的棋局,观看战局的情况。
1.3.2  课题分析
 本课题是一款模拟实时对战的网络游戏——网络五子棋,使两个不同计算机的使用者通过一定的网络连接,达到两人对战的功能,以及单机上人机对战的功能。它的总体设计内容有:
 (1)游戏引擎的设计:它是控制所有游戏功能的主程序,从连接,到接收玩家的输入,以及按正确的音量正确的输出声音等等。游戏引擎是一个为运行某一类游戏的机器设计的能够被机器识别的代码(指令)集合。它像一个发动机,控制着游戏的运行。一个游戏作品可以分为游戏引擎和游戏资源两大部分。游戏资源包括图像,声音,动画等部分,列一个公式就是:游戏=引擎(程序代码)+资源(图像,声音,动画等)。游戏引擎则是按游戏设计的要求顺序的调用这些资源。
 (2)游戏的客户端的设计:通过IP让用户进入,实现两个玩家进入游戏。其功能主要利用Winsock通信技术,通过双方IP进行连接进入游戏。
 (3)游戏的服务端设计:服务端是对负责接受、处理、发送数据的。所有服务器中必须有一个线程池以及它的管理系统,一个数据处理系统、一个用户管理系统,一个数据解码和加密系统。服务器端的整体构架如下:通讯模块,消息传递模块,游戏规则模块,线程管理模块,游戏管理模块。
 (4)游戏的数据库的设计:主要利用VC++里面的封装和类库来实现。其数据信息通过VC++类库保存和发送信息。
1.4  开发途径和可行性分析
1.4.1  开发途径
 本课题采用开发平台为Windows 2000 Professional ;整体布局利用VC++ 6.0进行棋盘绘制;画图工具为Microsoft Visio 其画图极其方便易用。
 本课题的开发工具:Microsoft Visual C++ 6.0由于VC是微软的产品,用它来编写Windows程序有强大的程序接口和丰富的开发资源的支持,加之VC严谨的内存管理,在堆栈上良好的分配处理,生成代码的体积小,稳定性高的优点,所以我选择VC++ 6.0作为网络五子棋的开发工具。
1.4.2  可行性分析
 (1)经济可行性分析
 本课题研究的内容是涉及网络的游戏对战。如今网络游戏俨然已经成为年轻人最时兴的消费方式之一,加之本课题研究比较适合不同年龄段的人用于娱乐和比赛,所以本人研究的基础是建立于二维动画的网络五子棋游戏,不需要耗费巨大资金,在其中还实现在聊天系统,而且在一定的条件下还能带来一定的经验效益。
 (2)技术可行性分析
 本课题涉及的技术学科主要有:VC++6.0编程基础,计算机网络,软件工程,图形学与数字图形处理等等。涉及的内容有:数据库的建立,二维图形与动画,交互性与用户界面,声效于音乐,创建二维平台游戏,网络多人游戏,路径搜索,人工智能,优化技术,最后生成游戏图形与声音。四年的学习加上大量的书本阅读与资料查询,以及现在的不断实践,相信利用所学的技术是能够实现的。
 (3)法律可行性
 本系统只是本人的毕业设计作品,系自己动手编程去实践,在设计过程中参阅了相关资料,但没有照搬照抄。所以,不会和网络上的任何一个学位论文管理系统出现雷同现象,也就不会出现侵犯别人版权的问题,因此从法律角度来说,是切实可行的。
 总体来说,这个系统不论是在经济上,还是在技术上,异或是在法律上,都是切实可行的。相信本设计完成后,对我校的论文管理将起到一定的推动作用。
1.5  传统五子棋软件与网络五子棋软件的异同
 在很早以前五子棋软件就已经被开发出来了,当时的五子棋软件仅提供了“人机模式”与“双人模式”。“人机模式”是指,人与计算机进行对弈,计算机按照事先编写好的算法程序来进行下棋;“双人模式”是指,两个人通过交替使用同一台计算机来进行下棋。无论是选择哪一种模式,所有的操作都必须在同一台计算机上来往完成,棋局无法在异地计算机之间来进行,故而也叫单机版五子棋软件。
 随着计算机网络的迅猛发展,异地计算机之间的通信变得十分便捷。这也为异地间的用户通过网络来进行下棋提供了可能。
 网络五子棋软件利用TCP/IP协议,在异地的玩家之间建立起TCP连接,并用它来交换棋局的各种数据信息。所以,网络五子棋软件在下棋以前必须要对相关的网络参数进行设置,才能实现玩家双方的成功连接。连接成功后,玩家双方轮流下棋,并将每一步下棋的信息通过网络传送给对方,使得双方棋盘上的棋子保持一致。
 与单机版五子棋软件相同,网络版五子棋软件也提供了倒计时器、“认输”、“和棋”和“悔棋”等功能。但这些功能的都是通过网络来实现的,所以,实现过程较单机版要复杂。
 在胜负判别方面,单机版五子棋软件与网络版五子棋软件并没有什么不同,只是网络版的玩家双方各自有一套胜负判别的程序。当一方玩家下一步棋后,他的胜负判别程序就执行一次,然后再将落子的信息传送给对方,对方的胜负判别程序也将执行一次。这样就完成一次胜负的判别过程。
 另外,网络版五子棋的双方可能相距邀远,双方的语言交流没有单机版方便。所以,网络版五子棋还必须提供聊天功能。
2  网络五子棋软件的系统需求分析与总体设计
2.1 系统需求分析
 软件的需求分析是软件生存期中重要的一步,也是决定性的一步。只有通过需求分析才能把软件功能和性能的总体概念描述为具体的软件需求规格说明,从而奠定软件开发的基础。
 在此阶段,了解用户要求本软件必须满足的所有功能和限制,以及用户对软件功能和性能的要求,弄清用户想要软件“做什么”,准确地表达用户的要求。
 (1)功能需求
 能通过网络进行下棋;符合五子棋的基本规则;提供“认输”、“和棋”、“悔棋”等功能;提供双方的倒计时功能;提供水平坐标和垂直坐标功能;能在棋子上显示的落子的顺序数;提供多种背景;提供玩家之间的聊天功能;有背景音乐;可自行输入用户名称。
 (2)环境需求
 10/10Mbps共享式HUB一台;装有Windows95/98/NT/XP操作系统的计算机两台;计算机的CPU不低于PII450,内存不小于64M;10/10Mbps自适应网卡每台计算机各一块,SB兼容声卡各一块,5类双绞线若干米;每台计算机都应配置有TCP/IP协议。
 (3)用户界面需求
 界面友好、亲切;界面简洁、不花哨;操作界面直观、不繁琐。
 (4)异常处理需求
 当端口号冲突时,错误提示并可重新输入;当出现错误行列时,此次操作无效;如果网络非正常断开,则终止此局。
2.2  总体设计
2.2.1  总体设计结构图
 在需求分析结束后,已经弄清楚了软件的各种需求,较好地解决了用户要软件“做什么”的问题,接下来就将着手实现软件的需求,即要着手解决“怎么做”的问题。
 在这个阶段着重实现需求的程序模块设计问题,并将需求转化为软件的系统结构,进行模块的划分,确定每个模块的功能、接口及模块之间的调用关系。
 根据需求分析制订出整个软件的系统结构图,如图2.1所示:
                                  
图2.1  系统结构图
2.2.2  接口设计 
 (1)外部接口:通过一定的计算机硬件,建立服务器系统,用户通过服务器程序进行交互,从而达到游戏、聊天等功能。 
 (2)内部接口:程序内部需要共同的类、函数定义和描述,此程序使用四个模块,它们分别为:客户端主程序、客户端的界面、棋盘的绘制、服务器端实现游戏基本功能,可满足用户的基本要求。模块之间实现是相互承接,相互进行调用,各个模块必须统一进行数据定义等,才能使该程序性能达到最好,并且要尽量消除和其他模块之间存在的数据冗余,才能使程序做到高效,方便。 
2.2.3  运行设计 
 (1)运行模块的组合:运行模块主要分为四个模块,即客户端主程序、客户端的界面、棋盘的绘制、服务器端,这使得整个程序的维护容易,简单;结构层次简单,易懂。 
 (2)运行控制:本程序采用的控制方式主要有三种,即顺序,选择,循环。 
2.2.4  安全保密设计 
 考虑到不同用户的权限不同,第一次进入该游戏程序时该程序自动分配一个用户名字。该用户可以进行名字修改等功能,方便用户进行识别。当用户通过登录进入操作界面之后,程序分配用户之后,才能进入该用户所允许做的权限。
3  通信协议及编程语言的分析
3.1  TCP/IP参考模型
 TCP/IP参考模型是计算机网络的祖父ARPANET和其后继的Internet所使用的参考模型。TCP/IP参考模型共有四层:应用层、传输层、网络(Internet)层和主机至网络层。TCP/IP参考模型如表1所示。
表1  TCP/IP参考模型
ISO OSI  RM TCP/IP  RM TCP/IP协议集
应用层 应用层 HTTP、FTP、SMTP、DNS、
RFC、SNM
表示层  
会话层  
传输层 传输层 TCP、UDP、NVP
网络层 Internet层 ARP、ICMP、IP、RARP
数据链路层 主机至网络层 以太网、令牌网、帧中继、ATM
物理层  
  (1)网络层(Internet Layer)
 它是整个体系结构的关键部分。它的功能是负责路由选择合适的通信节点,使数据分组能从源主机发往目的主机。这些分组到达目的主机的顺序和发送的顺序可能不同,因此如果需要按顺序发送和接收时,高层必须对分组进行排序。
 (2)传输层(Transmission Layer)
 传输层位于互联网层的上一层,传输层的协议只存在于主机之中,它的功能是提供主机之间进程与进程的有效数据传输。传输层使用端口号向应用层中不同进程提供与该端口号相应的服务。在传输层中有TCP协议(Transmission Control Protocol)即传输控制协议,和UDP协议(User Datagram Protocol)即用户数据报协议。
 (3)应用层(Application Layer)
 在TCP/IP模型的最上层是应用层,它包含所有的高层的协议。高层协议有:虚拟终端协议TELNET、文件传输协议FTP、电子邮件传输协议SMTP、域名系统服务DNS、网络新闻传输协议NNTP和HTTP协议等等。
3.2  管套Socket与Winsock
 虽TCP/IP协议的作用在于网络互联,但其本身就是物理网上的一组完整的网络协议集。TCP在传输层上提供服务,IP在互联网层上提供服务。这两个主要协议提供的服务,加上高层的服务,共同实现了TCP/IP协议集的功能。
 Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。
 Socket是一种应用程序的编程接口,它通过一组管套函数使应用程序在本地系统和远程系统之间建立通信信道,实现连接的管理和数据的传输。它为网络上的进程之间的通信提供了一种透明的方法,并支持包括TCP/IP在内的多种网络通信协议。它同TLI一样也是UNIX提供的一种网络应用程序编程接口,应用程序通过编程接口访问TCP/IP。如图3.1所示。
                               
图3.1  Socket通信信道
 Winsock其实就是Windows操作系统环境下的TCP/IP应用程序的编程接口,即Windows API。它是在1991年由Sun Microsoft、FTP Software和Microsoft等几家公司联合制定了一套标准的、通用的TCP/IP编程接口,它类似于UNIX环境下的Socket,并很快得到了广泛的应用。当前,Windows环境下的Internet软件几乎都是基于Winsock开发的,而且Winsock已经被集成到Windows95/98以及Windows NT/XP中。
3.3  网络游戏通信协议
3.3.1  游戏通信协议简介
  游戏通信协议是在网络模型的应用层定义一个高层的通信协议,类似于FTP和SMTP协议等,使得网络游戏的客户端和服务器程序通过预先设定好的格式传输数据。其连接和发送过程为:
 客户端与服务器建立TCP连接;
 客户端发送HELLO命令标识发件人的身份;
 客户端发送MAIL命令,服务器以OK应答;
 客户端发送RCPT命令,标识接收人;
 客户端发送DATA,发送信息;
 客户端发送“.”,表示结束;
 客户端发送QUIT命令。结束会话。
 通过这样一整套预先规定好的协议流程,服务器端就与客户端程序相对独立。服务器端程序与客户端程序运行的环境、运行机器的架构、程序实现语言都无关,因此,设计优秀的游戏通信协议对网络游戏十分重要。
3.3.2  协议打包/解包
 定义了游戏通信协议,自然需要协议打包/解包程序的支持。需要注意的一点是,打包/解包代码的编写过程中应该最大限度地保持代码的通用性和灵活性。因为游戏通信协议并不是一个标准的协议,很有可能在游戏开发过程中需要修改补充,甚至推倒重来。为了减少修改代码的工作,打包/解包程序的编写必须从一开始就尽量做到内核的独立性,只留几个协议相关的外部接口。这样,即使协议发生了很大的变化,打包/解包代码也只需很少的修改即可。
3.3.3  通信协议的选择
 TCP协议是建立在IP协议之上的面向连接的端到端的通信协议。由于IP协议是无连接的不可靠的协议,IP协议不可能提供任何可靠性保证机制,所以,TCP协议的可靠性完全由自身实现。TCP协议采取了确认、超时重发、流量控制等各种保证可靠性的技术和措施。TCP和IP两种协议结合在一起,实现了传输数据的可靠方法。它的安全和可靠性比UDP要高出很多。
 UDP协议是建立在IP协议之上的无连接的端到端的通信协议。UDP协议不提供任何可靠性保证机制,提供的是不可靠传输,但UDP增加和扩充了IP协议接口能力,具有高效传输、协议和协议格式都简单等特点。UDP的应用程序在高可靠性、低延迟的网络中运行的很好,在通信子网QOS相对低下的情况下,可能无法正常运行,原因在于数据包的频繁丢失。虽然UDP如此不可靠,但是由于它传输的高效率,仍然被广泛应用。在一些场合,一来一往只有两次的数据报交换中,用UDP比TCP更有效率,因为TCP的建立连接与撤消连接的开销往往要大的多。网络五子棋对实时性要求较高,综合考虑的结果,本软件应使用TCP/IP协议。
3.4  编程语言的分析
3.4.1  Visual C++ 6.0 概述
 Visual C++ 6.0是一个功能强大的可视化软件开发工具。自1993年Microsoft公司推出Visual C++1.0后,随着其新版本的不断问世,Visual C++已成为专业程序员进行软件开发的首选工具。Visual C++ 6.0不仅是一个C++编译器,而且是一个Windows操作系统的可视化集成开发环境(Integrated Development Environment, IDE)。Visual C++ 6.0 由许多组件组成,包括编辑器、编译器、调试器以及程序向导AppWizard、类向导ClassWizard等开发工具。这些组件通过一个名为Developer Studio的组件集成为和谐的开发环境。
3.4.2  Visual C++ 6.0 的特点
 Visual C++ 6.0的源程序要求用C++语言编写,它支持面向对象设计方法,并可以使用功能强大的微软基础类库MFC(Microsoft Foundation Class),充分体现了Microsoft公司的技术精华。由于Microsoft公司在操作系统市场上的垄断地位,用Visual C++ 6.0开发出来的软件稳定性好、可移植性强,而且软件与硬件相互独立。
 与传统的面向过程的程序设计语言相比,C++语言的最大特征是支持面向对象程序(Object Oriented Programming, OOP),它引入了类、继承、多态和重载等面向对象的新机制。网络五子棋的制作和设计语言就是用的Visual C++ 6.0里面的这些特点,使用和操作起来更方便。
4  详细设计与开发
 详细设计阶段的根本目标是确定怎么样具体实现所要求的系统,也就是说,经过这个阶段的设计工作,应该得出对目标系统的精确描述,从而在编码阶段可以把这个描述直接翻译成某种程序设计语言书写的程序。详细设计的结果基本上决定了最终的程序代码的质量。详细设计的目标不仅仅是逻辑上正确的实现每个模块的功能,更重要的是设计出的处理过程应该尽可能简明易懂。
4.1  软件架构
 软件的总体架构如图4.1所示。

 

 

 

 

 

 

 


图4.1 软件架构
 考虑到整个的下棋过程(无论对方是电脑抑或其他网络玩家)可以分为:己方落子、等待对方落子、对方落子、设置己方棋盘数据这一系列过程,因此一人游戏类、二人游戏类和棋盘类之间的关系参考了Abstract Factory(抽象工厂)模式,以实现对两个不同模块进行一般化的控制。
4.1.1  棋盘类
 整个架构的核心部分,类名为CTable。封装了棋盘的各种可能用到的功能,如保存棋盘数据、初始化、判断胜负等。用户操作主界面,主界面与CTable进行交互来完成对游戏的操作。
4.1.2  游戏模式类
 用来管理人机对弈/网络对弈两种游戏模式,类名为CGame。CGame是一个抽象类,经由它派生出一人游戏类COneGame和网络游戏类CTwoGame,如图4.2:

 

 


                          图4.2  CGame类派生关系
 这样,CTable类就可以通过一个CGame类的指针,在游戏初始化的时候根据具体游戏模式的要求实例化COneGame或CTwoGame类的对象;然后利用多态性,使用CGame类提供的公有接口就可以完成不同游戏模式下的不同功能了。
4.2  主要算法
 五子棋游戏中,有相当的篇幅是算法的部分。无论是人机对弈,还是网络对弈,都需要合理算法的支持,本节中将详细介绍五子棋中使用的算法。
4.2.1  判断胜负的算法设计
 五子棋的胜负,在于判断棋盘上是否有一个点,从这个点开始的右、下、右下、左下四个方向是否有连续的五个同色棋子出现,如图4.3所示。
 
 
 
 

 图4.3 判断胜负方向
 这个算法也就是CTable的Win成员函数。从设计的思想上,需要它接受一个棋子颜色的参数,然后返回一个布尔值,这个值来指示是否胜利,代码如下:
 BOOL CTable::Win( int color ) const
 {
     int x, y;
     // 判断横向
     for ( y = 0; y < 15; y++ )
     {
         for ( x = 0; x < 11; x++ )
         {
             if ( color == m_data[x][y] &&color == m_data[x + 1][y] &&color ==m_data[x + 2][y] &&color == m_data[x + 3][y] &&color == m_data[x + 4][y] )
             {
                 return TRUE;
             }
         }
     }
     // 判断纵向
     for ( y = 0; y < 11; y++ )
     {
         for ( x = 0; x < 15; x++ )
         {
             if ( color == m_data[x][y] &&color == m_data[x][y + 1] &&color == m_data[x][y + 2] &&color == m_data[x][y + 3] &&color == m_data[x][y + 4] )
             {
                 return TRUE;
             }
         }
     }
     // 判断“\”方向
     for ( y = 0; y < 11; y++ )
     {
         for ( x = 0; x < 11; x++ )
         {
             if ( color == m_data[x][y] &&color == m_data[x + 1][y + 1] &&color == m_data[x + 2][y + 2] &&color == m_data[x + 3][y + 3] &&color == m_data[x + 4][y + 4] )
             {
                 return TRUE;
             }
         }
     }
     // 判断“/”方向
     for ( y = 0; y < 11; y++ )
     {
         for ( x = 4; x < 15; x++ )
         {
             if ( color == m_data[x][y] &&color == m_data[x - 1][y + 1] &&color == m_data[x - 2][y + 2] &&color == m_data[x - 3][y + 3] &&color == m_data[x - 4][y + 4] )
             {
                 return TRUE;
             }
         }
     }
     // 不满足胜利条件
     return FALSE;
 }
 需要说明的一点是,由于这个算法所遵循的搜索顺序是从左到右、自上而下,因此在每次循环的时候,都有一些坐标无需纳入考虑范围。例如对于横向判断而言,由于右边界所限,因而所有横坐标大于等于11的点,都构不成达到五子连的条件,所以横坐标的循环上界也就定为11,这样也就提高了搜索的速度。
4.2.2  人机对弈算法设计
 人机对弈算法完全按照CGame基类定义的接口标准,封装在了COneGame派生类之中。下面将对这个算法进行详细地介绍。
 (1)获胜组合
 获胜组合是一个三维数组,它记录了所有取胜的情况。也就是说,参考于CTable::Win中的情况,对于每一个落子坐标,获胜的组合一共有:15 * 11 * 2 + 11 * 11 * 2 = 572种。
 而对于每个坐标的获胜组合,应该设置一个[15][15][572]大小的三维数组。
 在拥有了这些获胜组合之后,就可以参照每个坐标的572种组合给自己的局面和玩家的局面进行打分,也就是根据当前盘面中某一方所拥有的获胜组合多少进行权值的估算,给出最有利于自己的一步落子坐标。
 由于是双方对弈,所以游戏的双方都需要一份获胜组合,也就是:
 bool m_Computer[15][15][572]; // 电脑获胜组合
 bool m_Player[15][15][572]; // 玩家获胜组合
 在每次游戏初始化(COneGame::Init)的时候,需要将每个坐标下可能的获胜组合都置为true。
 此外,还需要设置计算机和玩家在各个获胜组合中所填入的棋子数:
 int m_Win[2][572];
 在初始化的时候,将每个棋子数置为0。
 (2)落子后处理
 每当一方落子后,都需要作如下处理:
 (1)如果己方此坐标的获胜组合仍为true,且仍有可能在此获胜组合处添加棋子,则将此获胜组合添加棋子数加1;
 (2)如果对方此坐标的获胜组合仍为true,则将对方此坐标的获胜组合置为false,并将对方此获胜组合添加棋子数置为-1(不可能靠此组合获胜)。
 以玩家落子为例,代码为:
 for ( i = 0; i < 572; i++ )
 {
     // 修改状态变化
     if ( m_Player[stepPut.x][stepPut.y][i] &&m_Win[0][i] != -1 )
         m_Win[0][i]++;
     if ( m_Computer[stepPut.x][stepPut.y][i] )
     {
         m_Computer[stepPut.x][stepPut.y][i] = false;
         m_Win[1][i] = -1;
     }
 }
 (3)查找棋盘空位
 在计算机落子之前,需要查找棋盘的空位,所以需要一个SearchBlank成员函数完成此项工作,此函数需要进行不重复的查找,也就是说,对已查找过的空位进行标记,并返回找到空位的坐标,其代码如下:
 bool COneGame::SearchBlank( int &i, int &j,
 int nowTable[][15] )
 {
     int x, y;
     for ( x = 0; x < 15; x++ )
     {
         for ( y = 0; y < 15; y++ )
         {
             if ( nowTable[x][y] == -1 && nowTable[x][y] != 2 )
             {
                 i = x;
                 j = y;
                 return true;
             }
         }
     }
     return false;
 }
 (4)落子打分
 找到空位后,需要对这个点的落子进行打分,这个分数也就是这个坐标重要性的体现,代码如下:
 int COneGame::GiveScore( const STEP& stepPut )
 {
     int i, nScore = 0;
     for ( i = 0; i < 572; i++ )
     {
         if ( m_pTable->GetColor() == stepPut.color )
         {
             // 玩家下
             if ( m_Player[stepPut.x][stepPut.y][i] )
             {
                 switch ( m_Win[0][i] )
                 {
                 case 1:
                     nScore -= 5;
                     break;
                 case 2:
                     nScore -= 50;
                     break;
                 case 3:
                     nScore -= 500;
                     break;
                 case 4:
                     nScore -= 5000;
                     break;
                 default:
                     break;
                 }
             }
         }
         else
         {
             // 计算机下
             if ( m_Computer[stepPut.x][stepPut.y][i] )
             {
                 switch ( m_Win[1][i] )
                 {
                 case 1:
                     nScore += 5;
                     break;
                 case 2:
                     nScore += 50;
                     break;
                 case 3:
                     nScore += 100;
                     break;
                 case 4:
                     nScore += 10000;
                     break;
                 default:
                     break;
                 }
             }
         }
     }
     return nScore;
 }
 如代码所示,考虑到攻守两方面的需要,所以将玩家落子给的分数置为负值。
 (5)防守策略
 落子的考虑不单单要从进攻考虑,还要从防守考虑。这一细节的实现其实就是让计算机从玩家棋盘布局分析战况,然后找出对玩家最有利的落子位置。整个过程如下所表示。
 for ( m = 0; m < 572; m++ )
 {
     // 暂时更改玩家信息
     if ( m_Player[i][j][m] )
     {
         temp1[n] = m;
         m_Player[i][j][m] = false;
         temp2[n] = m_Win[0][m];
         m_Win[0][m] = -1;
         n++;
     }
 }
 ptempTable[i][j] = 0;
 
 pi = i;
 pj = j;
 while ( SearchBlank( i, j, ptempTable ) )
 {
     ptempTable[i][j] = 2; // 标记已被查找
     step.color = m_pTable->GetColor();
     step.x = i;
     step.y = j;
     ptemp = GiveScore( step );
     if ( pscore > ptemp ) // 此时为玩家下子,运用极小极大法时应选取最小值
     pscore = ptemp;
 }
 for ( m = 0; m < n; m++ )
 {
     // 恢复玩家信息
     m_Player[pi][pj][temp1[m]] = true;
     m_Win[0][temp1[m]] = temp2[m];
 }
 (6)选取最佳落子
 在循环结束的时候,就可以根据攻、守两方面的打分综合地考虑落子位置了。代码如下:
 if ( ctemp + pscore > cscore )
 {
     cscore = ctemp + pscore;
     bestx = pi;
     besty = pj;
 }
 在这之后,重新改变一下棋盘的状态(6.2.2)即可。
4.3  消息机制
 Windows系统拥有自己的消息机制,在不同事件发生的时候,系统也可以提供不同的响应方式。五子棋程序也模仿Windows系统实现了自己的消息机制,主要为网络对弈服务,以响应多种多样的网络消息。
4.3.1  消息机制的架构
 当继承自CAsyncSocket的套接字类CFiveSocket收到消息时,会触发CFiveSocket::OnReceive事件,在这个事件中调用CTable::Receive,CTable::Receive开始按照自定义的消息格式接收套接字发送的数据,并对不同的消息类型进行分发处理。
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                         图4.4 自定义的消息机制
如图4.4所示,当CTable获得了来自网络的消息之后,就可以使用一个switch结构来进行消息的分发了。
4.3.2  各种消息说明
网络间传递的消息,都遵循以下一个结构体的形式:
// 摘自Messages.h
typedef struct _tagMsgStruct {
    // 消息ID
    UINT uMsg;
    // 落子信息
    int x;
    int y;
    int color;
    // 消息内容
    TCHAR szMsg[128];
} MSGSTRUCT;
随着uMsg表示消息ID,x、y表示落子的坐标,color表示落子的颜色,szMsg随着uMsg的不同而有不同的含义。
(1)落子消息——MSG_PUTSTEP
表明对方落下了一个棋子,其中x、y和color成员有效,szMsg成员无效。在人机对弈游戏模式下,亦会模拟发送此消息以达到程序模块一般化的效果。
(2)悔棋消息——MSG_BACK
表明对方请求悔棋,除uMsg成员外其余成员皆无效。接到这个消息后,会弹出MessageBox询问是否接受对方的请求(如图4.5所示),并根据玩家的选择回返MSG_AGREEBACK或MSG_REFUSEBACK消息。另外,在发送这个消息之后,主界面上的某些元素将不再响应用户的操作。

图4.5 请求悔棋对话框
(3)同意悔棋消息——MSG_AGREEBACK
表明对方接受了玩家的悔棋请求,除uMsg成员外其余成员皆无效。接到这个消息后,将进行正常的悔棋操作。
(4)拒绝悔棋消息——MSG_REFUSEBACK
表明对方拒绝了玩家的悔棋请求,除uMsg成员外其余成员皆无效。接到这个消息后,整个界面将恢复发送悔棋请求前的状态。
(5)和棋消息——MSG_DRAW
表明对方请求和棋,除uMsg成员外其余成员皆无效。接到这个消息后,会弹出MessageBox询问是否接受对方的请求(如图4.6所示),并根据玩家的选择回返MSG_AGREEDRAW或MSG_REFUSEDRAW消息。另外,在发送这个消息之后,主界面上的某些元素将不再响应用户的操作。

 图4.6 请求和棋对话框
(6)同意和棋消息——MSG_AGREEDRAW
表明对方接受了玩家的和棋请求,除uMsg成员外其余成员皆无效。接到这个消息后,双方和棋。
(7)拒绝和棋消息——MSG_REFUSEDRAW
表明对方拒绝了玩家的和棋请求(如图4.7所示),除uMsg成员外其余成员皆无效。接到这个消息后,整个界面将恢复发送和棋请求前的状态。

 图4.7 拒绝和棋对话框
(8)认输消息——MSG_GIVEUP
表明对方已经投子认输(如图4.8所示),除uMsg成员外其余成员皆无效。接到这个消息后,整个界面将转换为胜利后的状态。

 图4.8 认输对话框
(9)聊天消息——MSG_CHAT
表明对方发送了一条聊天信息,szMsg表示对方的信息,其余成员无效。接到这个信息后,会将对方聊天的内容显示在主对话框的聊天记录窗口内。
(10)对方信息消息——MSG_INFORMATION
用来获取对方玩家的姓名,szMsg表示对方的姓名,其余成员无效。在开始游戏的时候,由客户端向服务端发送这条消息,服务端接到后设置对方的姓名,并将自己的姓名同样用这条消息回发给客户端。
(11)再次开局消息——MSG_PLAYAGAIN
表明对方希望开始一局新的棋局,除uMsg成员外其余成员皆无效。接到这个消息后,会弹出MessageBox询问是否接受对方请求,并根据玩家的选择回返MSG_AGREEAGAIN消息或直接断开网络。
(12)同意再次开局消息——MSG_AGREEAGAIN
表明对方同意了再次开局的请求,除uMsg成员外其余成员皆无效。接到这个消息后,将开启一局新游戏。
4.4  棋盘类——Ctable的设计
4.4.1  主要成员变量说明
(1)网络连接标志——m_bConnected
用来表示当前网络连接的情况,在网络对弈游戏模式下客户端连接服务器的时候用来判断是否连接成功;事实上,它也是区分当前游戏模式的唯一标志。
(2)棋盘等待标志——m_bWait与m_bOldWait
由于在玩家落子后需要等待对方落子,m_bWait标志就用来标识棋盘的等待状态。当m_bWait为TRUE时,是不允许玩家落子的。
在网络对弈模式下,玩家之间需要互相发送诸如悔棋、和棋这一类的请求消息,在发送请求后等待对方回应时,也是不允许落子的,所以需要将m_bWait标志置为TRUE。在收到对方回应后,需要恢复原有的棋盘等待状态,所以需要另外一个变量在发送请求之前保存棋盘的等待状态做恢复之用,也就是m_bOldWait。
等待标志的设置,由成员函数SetWait和RestoreWait完成。
(3)网络套接字——m_sock和m_conn
在网络对弈游戏模式下,需要用到这两个套接字对象。其中m_sock对象用于做服务器时的监听之用,m_conn用于网络连接的传输。
(4)棋盘数据——m_data
这是一个15*15的二位数组,用来保存当前棋盘的落子数据。其中对于每个成员来说,0表示落黑子,1表示落白子,-1表示无子。
(5)游戏模式指针——m_pGame
这个CGame类的对象指针是CTable类的核心内容。它所指向的对象实体决定了CTable在执行一件事情时候的不同行为。
4.4.2  主要成员函数说明
(1)套接字的回调处理——Accept、Connect、Receive
本程序的套接字派生自MFC的CAsyncSocket类,CTable的这三个成员函数就分别提供了对套接字回调事件OnAccept、OnConnect、OnReceive的实际处理,其中尤以Receive成员函数重要,它之中包含了对所有网络消息的分发处理。
(2)清空棋盘——Clear
在每一局游戏开始的时候都需要调用这个函数将棋盘清空,也就是棋盘的初始化工作。在这个函数中,主要发生了这么几件事情:
将m_data中每一个落子位都置为无子状态(-1);
按照传入的参数设置棋盘等待标志m_bWait,以供先、后手的不同情况之用;
使用delete将m_pGame指针所指向的原有游戏模式对象从堆上删除。
(3)绘制棋子——Draw
这无疑是很重要的一个函数,它根据参数给定的坐标和颜色绘制棋子。绘制的详细过程如下:
将给定的棋盘坐标换算为绘图的像素坐标。
根据坐标绘制棋子位图。
如果先前曾下过棋子,则利用R2_NOTXORPEN将上一个绘制棋子上的最后落子指示矩形擦除。
在刚绘制完成的棋子四周绘制最后落子指示矩形。
(4)左键消息——OnLButtonUp
作为棋盘唯一响应的左键消息,也需要做不少的工作:
①如果棋盘等待标志m_bWait为TRUE,则直接发出警告声音并返回,即禁止落子。
②如果点击时的鼠标坐标在合法坐标(0, 0)~(14, 14)之外,亦禁止落子。
③如果走的步数大于1步,方才允许悔棋。
④进行胜利判断,如胜利则修改UI状态并增加胜利数的统计。
⑤如未胜利,则向对方发送已经落子的消息。
⑥落子完毕,将m_bWait标志置为TRUE,开始等待对方回应。
(5)绘制棋盘——OnPaint
每当WM_PAINT消息触发时,都需要对棋盘进行重绘。OnPaint作为响应绘制消息的消息处理函数使用了双缓冲技术,减少了多次绘图可能导致的图像闪烁问题。这个函数主要完成了以下工作:
①装载棋盘位图并进行绘制。
②根据棋盘数据绘制棋子。
③绘制最后落子指示矩形。
(6)对方落子完毕——Over
在对方落子之后,仍然需要做一些判断工作,这些工作与OnLButtonUp中的类似,在此不再赘述。
(7)设置游戏模式——SetGameMode
这个函数通过传入的游戏模式参数对m_pGame指针进行了初始化,代码如下:
void CTable::SetGameMode( int nGameMode )
{
    if ( 1 == nGameMode )
        m_pGame = new COneGame( this );
    else
        m_pGame = new CTwoGame( this );
    m_pGame->Init();
}
这之后,就可以利用OO的继承和多态特点[8]来使m_pGame指针使用相同的调用来完成不同的工作了,事实上,COneGame::Init和CTwoGame::Init都是不同的。运行情况如图4.9所示。
 
 图4.9 运行界面
(8)胜负的判断——Win
这是游戏中一个极其重要的算法,用来判断当前棋盘的形势是哪一方获胜。
4.5  游戏模式类——Cgame的设计
这个类负责对游戏模式进行管理,以及在不同的游戏模式下对不同的用户行为进行不同的响应。由于并不需要CGame本身进行响应,所以将其设计为了一个纯虚类,它的定义如下:
class CGame
{
protected:
    CTable *m_pTable;
public:
    // 落子步骤
    list< STEP > m_StepList;
public:
    // 构造函数
    CGame( CTable *pTable ) : m_pTable( pTable ) {}
    // 析构函数
    virtual ~CGame();
    // 初始化工作,不同的游戏方式初始化也不一样
    virtual void Init() = 0;
    // 处理胜利后的情况,CTwoGame需要改写此函数完成善后工作
    virtual void Win( const STEP& stepSend );
    // 发送己方落子
    virtual void SendStep( const STEP& stepSend ) = 0;
    // 接收对方消息
    virtual void ReceiveMsg( MSGSTRUCT *pMsg ) = 0;
    // 发送悔棋请求
    virtual void Back() = 0;
};
4.5.1  主要成员变量说明
(1)棋盘指针——m_pTable
由于在游戏中需要对棋盘以及棋盘的父窗口——主对话框进行操作及UI状态设置,故为CGame类设置了这个成员。当对主对话框进行操作时,可以使用m_pTable->GetParent()得到它的窗口指针。
(2)落子步骤——m_StepList
一个好的棋类程序必须要考虑到的功能就是它的悔棋功能,所以需要为游戏类设置一个落子步骤的列表。由于人机对弈和网络对弈中都需要这个功能,故将这个成员直接设置到基类CGame中。另外,考虑到使用的简便性,这个成员使用了C++标准模板库(Standard Template Library,STL)中的std::list,而不是MFC的CList。
4.5.2  主要成员函数说明
(1)悔棋操作——Back
在不同的游戏模式下,悔棋的行为是不一样的。
人机对弈模式下,计算机是完全允许玩家悔棋的,但是出于对程序负荷的考虑,只允许玩家悔当前的两步棋(计算机一步,玩家一步)。
双人网络对弈模式下,悔棋的过程为:首先由玩家向对方发送悔棋请求(悔棋消息),然后由对方决定是否允许玩家悔棋,在玩家得到对方的响应消息(允许或者拒绝)之后,才进行悔棋与否的操作。
(2)初始化操作——Init
对于不同的游戏模式而言,也就有不同的初始化方式。对于人机对弈模式而言,初始化操作包括以下几个步骤:
①设置网络连接状态m_bConnected为FALSE。
②设置主界面计算机玩家的姓名。
③初始化所有的获胜组合。
④如果是计算机先走,则占据天元(棋盘正中央)的位置。
(3)接收来自对方的消息——ReceiveMsg
这个成员函数由CTable棋盘类的Receive成员函数调用,用于接收来自对方的消息。对于人机对弈游戏模式来说,所能接收到的就仅仅是本地模拟的落子消息MSG_PUTSTEP;对于网络对弈游戏模式来说,这个成员函数则负责从套接字读取对方发过来的数据,然后将这些数据解释为自定义的消息结构,并回到CTable::Receive来进行处理。
(4)发送落子消息——SendStep
在玩家落子结束后,要向对方发送自己落子的消息。对于不同的游戏模式,发送的目标也不同:
①对于人机对弈游戏模式,将直接把落子的信息(坐标、颜色)发送给COneGame类相应的计算函数。
②对于网络对弈游戏模式,将把落子消息发送给套接字,并由套接字转发给对方。
(5)胜利后的处理——Win
这个成员函数主要针对CTwoGame网络对弈模式。在玩家赢得棋局后,这个函数仍然会调用SendStep将玩家所下的制胜落子步骤发送给对方玩家,然后对方的游戏端经由CTable::Win来判定自己失败。
5  网络五子棋软件的测试
为了保证软件的质量和可靠性,在软件编码完成以后,应该对软件进行测试。测试出程序中存在的错误,然后进行修改。软件测试分为黑盒测试和白盒测试。黑盒测试不关心程序内部的逻辑,而只是根据程序的功能说明来设计测试用例;白盒测试则是一种结构从测试,它与程序的内部结构有关,要利用程序的结构实现细节来设计测试用例。
本程序的测试主要采用黑盒测试和白盒测试相结合的方法。
5.1  问题的发现
(1)地址越界
在下棋过程中,如果用户鼠标点击的范围超过公式所计算出的Fix (X - 9) / 25+ 1=16时X=384时,X将大于15,超过最大行列数15,导致“地址越界”错误的出现,而被系统终止运行。
(2)非正常断线而导致对方崩溃
在棋局进行过程中,由于各种原因导致网络连接突然断开,例如:网线接触不良、一方停电、死机等等,将会导致对方程序出现“错误连接状态”而被系统终止运行。
(3)双方计时器不同步
双方计时器的激活和禁止指令的互操作是通过网络传输来实现的,但是,由于网络传输会存在时延,所以几次传输后,双方的计时器所显示的时间将会有所不同。随着次数的增多,差距也将在不断增大。
5.2  问题的解决
(1)地址越界的解决
规定鼠标点击的有效范围,其它部分的点击均视为无效。
(2)非正常断线而导致对方崩溃
在程序增加Winsock_Close( )事件,一旦远程计算机关闭连接就触发该事件。触发后程序将关闭本方的连接,以避免出错。
(3)时延的解决
在传输时钟激活和禁止指令时同时发送时钟数据,让双方的时钟始终保持一次时延时间。
6  系统功能评价
6.1  系统的主要功能
本课题是一款模拟实时对战的网络游戏——网络五子棋,使两个不同计算机的使用者通过一定的网络连接,达到两人对战的功能,以及单机上人机对战的功能,同时具有聊天的功能。
6.2  系统存在的不足与改进方案
本软件和其它网络五子棋软件相比在功能和性能还是存在着比较大的差距的,例如:只有知道主机的IP地址和端口号才能建立连接、第三方不能加入棋局等等。
要想设计出更完善的网络五子棋软件,仍然需要做大量的工作:
①服务器的设置。在网络中将一台计算机设置成为服务器,服务器的IP地址和端口号是固定不变的,客户机可以直接连接到服务器上,然后通过服务器选择对手,这样就省去了玩家在游戏前必须知道主机的IP地址和端口号的麻烦了。
②界面需要进一步美化。增加改变字体颜色以及背景颜色功能,增加按钮闪烁功能等。
③提高通信的可靠性。在一般情况下,网络五子棋软件可以稳定的工作,但是仍不能保证它在任何情况下都不出错,一旦在网络传输过程中,由于某种原因造成数据丢失,将造成双方数据不一致,使游戏无法继续进行下去。因此,因该改善程序的可靠性。
④扩充功能。增加记录棋谱功能,以供五子棋爱好者能在日后研究。
7  用户使用手册
7.1  运行环境简介
运行环境配置从硬件和软件两方面来描述,所列出的都是系统所能正常运行的最低限度。而更高的硬件配置和软件环境将会带来更快的运行速度以及更稳定的运行效果。
(1)硬件要求
CPU:最低Intel Pentium II  300MHz(最好Intel Pentium II  600MHz 以上)。
内存:最低64MB(最好128MB以上)。
磁盘空间:最低3.2GB(完全安装,包括.NET Framework以及.NET IDE全部开发工具和MSDN) 2.6GB(选择安装包括.NET Framework以及.NET IDE部分开发工具)。
(2)软件要求
本软件运行平台为Microsoft Windows 2000 采用Visual C++ 6.0。
7.2  系统功能简介
本课题是网络五子棋的开发。五子棋作为一个棋类竞技运动,在民间十分流行,这次开发的目的是使这个程序不光有与电脑对战的功能,更有在网络连接之后人与人在网上对战,以及观看电脑和电脑对战的演示功能。并且,我们也可以在游戏之后,保存棋局,在之后可以随时的打开以前的棋局,观看战局的情况。
本软件在网络开发,主要是使用WINSOCK,使软件只要使用IP就能连接到所对应的计算机上去,这样就可以让两台计算机互相连接,让使用者可以联机对战。本课题主要用VC++ 6.0开发,这是一个基于WINDOWS的面向对象的开发工具,再嵌入WinSock类,增加该设计对网络的支持。
7.3  系统运行与操作指南
本系统是一款无须手动操作的简易游戏,使用方法简单方便,界面友好。使用者可以在可执行文件的目录下,执行Five.exe文件即可。对于人机对战,直接点“游戏”下拉菜单“单人游戏”即可;对于双人对战,主机玩家点击进入“网络对战”,点击“建立游戏”,对方玩家点击“加入游戏”输入主机IP地址,点“连接”即可加入游戏进行二人对战。
8  毕业设计心得体会
时至今日,几个月的毕业设计终于可以画上一个句号了,但是现在回想起来做毕业设计的整个过程颇有心得,其中有苦也有甜!经过一学期的设计和开发,我通过自己动手实现了网络游戏开发设计与实现。其功能基本符合用户需求,网络五子棋软件的开发让我经历了一次前所未有的体验,让我真正体会到了“书到用时方恨少”的含义,体会到了厚积薄发的意义所在,这次的开发是对自己所学的知识的一次大考验,也是对自己潜能的一次挖掘。现在把一些开发的经验总结如下:
(1)在工程开发中,系统的分析是整个开发的核心,只有前期进行很好的分析、规划,才能做出好的软件产品,这个阶段要进行很好的策划,精密的分析。尤其是流程的设计,直接关系到系统功能和编码的成败。
(2)良好的程序设计风格很重要。这在一个比较大的工程中,程序设计的风格非常重要,这影响到软件的测试以及后期功能扩展,在增量型的开发模型中,混乱的代码会使维护变的十分困难。在多人合作团队开发的时候,你的代码要影响到全局,所以会有“你的代码是写给别人看”的观点的存在。
(3)运用高效的测试手段。测试要占开发很大的时间,软件编码以后,并非总能百分百的成功,那就要进行测试。在大程序中,多写异常处理的代码,以及变量合法的检验等,DEBUG中混合使用断点测试以及报告函数(MessageBox),以提高DEBUG效率。
(4)善于捕捉最新的技术资料。在每个软件开发过程中,资料是必不可少的,虽然书本上有比较系统的资料,但是真正能运用的还是不多。在开发时,不要急于设计编码,应该学会先查找软件开发中涉及到的各种最新资料,学习他们关于本系统的一些开发经验。学会在编码前,吸取别人的代码的一些优点,然后改进设计,使之更加的完善。
此外,我深深的体会到,知识必须通过应用才能实现其价值!有些东西以为学会了,但真正到用的时候才发现是两回事,所以我认为只有到真正会用的时候才是真的学会了。

结  论

软件的开发是十分辛苦的工作,尤其是一个人独立开发更是困难,毕竟一个人的知识和精力十分有限,所以一个好的软件产品是团队的合作结果,是多人知识的聚集的成果。
本软件的开发和设计主要采用基于Windows的面向对象的开发工具Visual C++ 6.0进行编程,再嵌入WinSock类,增加该设计对网络的支持。通过编程可实现在网络连接之后人与人在网上对战,以及实现聊天功能。其中引擎的设计、客户端和服务端的设计是整个设计的难点,在整个设计中花费了大量时间调试运行。
此次的开发运用了大部分所学的知识,像网络操作系统,局域网,软件工程,计算机图形学,程序设计方法学,数据结构等课程的知识,在本设计中均发挥了潜移默化的作用,让我深深体会到万丈高楼平地起,需要的是有扎实的地基,我们不应该轻视书本上的知识,不要认为书本上的东西很肤浅,只有学好书本的知识才有可能在计算机的世界中游刃有余。
网络五子棋软件的开发再一次让我体会到了,微软的操作系统其简单快捷背后所隐藏的极其复杂的驱动机制,微软确实为我们带来了很好的使用环境和开发环境,所有我们要做的事情都是站在巨人的肩膀上完成的。 然而,由于本人的水平有限,加之毕业设计时间比较仓促,认识问题的广度和深度不够,使得该设计存在一些不完善的地方,本人将在以后的学习和工作中对这些问题不断地完善。
致  谢

经过这段时间的努力,本人终于完成了毕业设计和毕业论文,大学四年的生活也由此将画上了一个较圆满的句号。回首这几个月,好多人给予我太多的帮助。首先我要向我的指导老师××老师表示我最诚挚的谢意。在毕业设计的这段时间,××老师无论是在学习上还是在思想上都给予我的热情鼓励和深刻知道,他的谆谆教诲将让我终身受益。××老师严谨的治学态度、诲人不倦的教学风范,平易近人和真诚的待人性格将对我今后的学习和生活产生深刻的影响。
此外,我还要感谢计算机系的所有老师,他们让我在课堂上学到了丰富的知识,让我见识了众多学术领域的精华。他们不仅教会了我一些书面知识,还教我许多待人接物与为人处世的道理。我相信这些道理将成为我人生道路上的一笔巨大财富!
最后 ,向我敬爱的父母表示衷心的感谢。感谢他们在我求学的生涯中对我无微不至的关怀、体贴和照顾,我人生的每个阶段、每一个脚印都凝结着他们无私的心血和汗水。我相信,我学业的顺利完成是对他们最好的回报。
在此,对所有帮助过我的人,忠心的说一声谢谢!

参 考 文 献

 [1]  姚晓光,《网络游戏开发》.北京:机械工业出版社,2004.
 [2]  刘彦明,李鹏,《实用网络编程技术》.陕西:西安电子科技大学,1998.
 [3]  梁普选,《Visual C++程序设计与实践》.北京:清华大学出版社,2005.
 [4]  九一工作组,《Internet 网络游戏大全》. 北京:电子工业出版社,1998.
 [5]  全洪(韩)著,《网络游戏服务器编程》.北京:人民邮电出版社,2006.
 [6]  王育坚著,《Visual C++面向对象编程教程》.北京:清华大学出版社,2003.
 [7]  张海潘,《软件工程导论》. 清华大学出版社,2003.
 [8]  Bruce Eckel著,《C++编程思想》. 北京:机械工业出版社, 2005.
 [9]  汪晓平著,《Visual C++网络通信协议分析与应用实现》. 北京:人民邮电出版社,2006.
 [10] Nicolai M.Josuttis著,《C++标准程序库》. 西安:华中科技大学出版社, 2003.
 [11] Charles Petzold著,《Windows程序设计》. 北京:北京大学出版社,2004.
 [12] 《五子棋的核心算法》. http://blog.joycode.com/ghj/articles/12727.aspx
 [13] 郑人杰著, 《软件工程概论》.北京:清华大学出版社,1998.

 

 

 

 

 

 


附 录
#include "stdafx.h"
#include "Five.h"
#include "Table.h"
#include "Messages.h"
#include "Resource.h"
//////////////////////////////////////////////////////////////////////////
// 构造函数,初始化棋盘数据以及图像数据
//////////////////////////////////////////////////////////////////////////
CTable::CTable()
{
// 初始化玩家姓名
TCHAR str[10];
CFiveApp *pApp = (CFiveApp *)AfxGetApp();
    ::GetPrivateProfileString( _T("Options"), _T("Name"), _T("Renjiu"), str, 15, pApp->m_szIni );
m_strMe = str;
// 初始化图像列表
    m_iml.Create( 24, 24, ILC_COLOR24 | ILC_MASK, 0, 2 );
// 载入黑、白棋子掩码位图
CBitmap bmpBlack, bmpWhite;
bmpBlack.LoadBitmap( IDB_BMP_BLACK );
m_iml.Add( &bmpBlack, 0xff00ff );
bmpWhite.LoadBitmap( IDB_BMP_WHITE );
m_iml.Add( &bmpWhite, 0xff00ff );
// 初始化游戏模式
m_pGame = NULL;
}
//////////////////////////////////////////////////////////////////////////
// 析构函数,释放m_pGame指针
//////////////////////////////////////////////////////////////////////////
CTable::~CTable()
{
// 写入玩家姓名
CFiveApp *pApp = (CFiveApp *)AfxGetApp();
::WritePrivateProfileString( _T("Options"), _T("Name"), m_strMe, pApp->m_szIni );
// 写入战绩统计
TCHAR str[10];
wsprintf( str, _T("%d"), pApp->m_nWin );
    ::WritePrivateProfileString( _T("Stats"), _T("Win"), str, pApp->m_szIni );
    wsprintf( str, _T("%d"), pApp->m_nDraw );
    ::WritePrivateProfileString( _T("Stats"), _T("Draw"), str, pApp->m_szIni );
    wsprintf( str, _T("%d"), pApp->m_nLost );
    ::WritePrivateProfileString( _T("Stats"), _T("Lost"), str, pApp->m_szIni );
    if ( NULL != m_pGame )
        delete m_pGame;
}
//////////////////////////////////////////////////////////////////////////
// 在指定棋盘坐标处绘制指定颜色的棋子
//////////////////////////////////////////////////////////////////////////
void CTable::Draw( int x, int y, int color )
{
POINT pt;
pt.x = 12 + 25 * x;
pt.y = 84 + 25 * y;
CDC *pDC = GetDC();
CPen pen;
pen.CreatePen( PS_SOLID, 1, 0xff );
pDC->SelectObject( &pen );
pDC->SetROP2( R2_NOTXORPEN );
m_iml.Draw( pDC, color, pt, ILD_TRANSPARENT );
STEP step;
// 利用R2_NOTXORPEN擦除先前画出的矩形
if ( !m_pGame->m_StepList.empty() )
{
// 获取最后一个点
step = *( m_pGame->m_StepList.begin() );
pDC->MoveTo( 11 + 25 * step.x, 83 + 25 * step.y );
pDC->LineTo( 36 + 25 * step.x, 83 + 25 * step.y );
pDC->LineTo( 36 + 25 * step.x, 108 + 25 * step.y );
pDC->LineTo( 11 + 25 * step.x, 108 + 25 * step.y );
pDC->LineTo( 11 + 25 * step.x, 83 + 25 * step.y );
    }
// 更新最后落子坐标数据,画新的矩形
    step.color = color;
    step.x = x;
    step.y = y;
    m_pGame->m_StepList.push_front( step );
    pDC->MoveTo( 11 + 25 * step.x, 83 + 25 * step.y );
    pDC->LineTo( 36 + 25 * step.x, 83 + 25 * step.y );
    pDC->LineTo( 36 + 25 * step.x, 108 + 25 * step.y );
    pDC->LineTo( 11 + 25 * step.x, 108 + 25 * step.y );
    pDC->LineTo( 11 + 25 * step.x, 83 + 25 * step.y );
    ReleaseDC( pDC );
}
//////////////////////////////////////////////////////////////////////////
// 清空棋盘
//////////////////////////////////////////////////////////////////////////
void CTable::Clear( BOOL bWait )
{
    int x, y;
    for ( y = 0; y < 15; y++ )
    {
        for ( x = 0; x < 15; x++ )
        {
            m_data[x][y] = -1;
        }
    }
    // 设置等待标志
    m_bWait = bWait;
    Invalidate();
    // 删除游戏
    if ( m_pGame != NULL )
    {
        delete m_pGame;
        m_pGame = NULL;
    }
}
//////////////////////////////////////////////////////////////////////////
// 设置玩家颜色
//////////////////////////////////////////////////////////////////////////
void CTable::SetColor( int color )
{
    m_color = color;
}
//////////////////////////////////////////////////////////////////////////
// 获取玩家颜色
//////////////////////////////////////////////////////////////////////////
int CTable::GetColor() const
{
    return m_color;
}
//////////////////////////////////////////////////////////////////////////
// 设置等待标志,返回先前的等待标志
//////////////////////////////////////////////////////////////////////////
BOOL CTable::SetWait( BOOL bWait )
{
    m_bOldWait = m_bWait;
    m_bWait = bWait;
    return m_bOldWait;
}
//////////////////////////////////////////////////////////////////////////
// 设置棋盘数据,并绘制棋子
//////////////////////////////////////////////////////////////////////////
void CTable::SetData( int x, int y, int color )
{
    m_data[x][y] = color;
    Draw( x, y, color );
}
//////////////////////////////////////////////////////////////////////////
// 判断指定颜色是否胜利
//////////////////////////////////////////////////////////////////////////
BOOL CTable::Win( int color ) const
{
    int x, y;
    // 判断横向
    for ( y = 0; y < 15; y++ )
    {
        for ( x = 0; x < 11; x++ )
        {
            if ( color == m_data[x][y] && color == m_data[x + 1][y] &&
                color == m_data[x + 2][y] && color == m_data[x + 3][y] &&
                color == m_data[x + 4][y] )
            {
                return TRUE;
            }
        }
    }
    // 判断纵向
    for ( y = 0; y < 11; y++ )
    {
        for ( x = 0; x < 15; x++ )
        {
            if ( color == m_data[x][y] && color == m_data[x][y + 1] &&
                color == m_data[x][y + 2] && color == m_data[x][y + 3] &&
                color == m_data[x][y + 4] )
            {
                return TRUE;
            }
        }
    }
    // 判断“\”方向
    for ( y = 0; y < 11; y++ )
    {
        for ( x = 0; x < 11; x++ )
        {
            if ( color == m_data[x][y] && color == m_data[x + 1][y + 1] &&
                color == m_data[x + 2][y + 2] && color == m_data[x + 3][y + 3] &&
                color == m_data[x + 4][y + 4] )
            {
                return TRUE;
            }
        }
    }
    // 判断“/”方向
    for ( y = 0; y < 11; y++ )
    {
        for ( x = 4; x < 15; x++ )
        {
            if ( color == m_data[x][y] && color == m_data[x - 1][y + 1] &&
                color == m_data[x - 2][y + 2] && color == m_data[x - 3][y + 3] &&
                color == m_data[x - 4][y + 4] )
            {
                return TRUE;
            }
        }
    }
    // 不满足胜利条件
    return FALSE;
}
//////////////////////////////////////////////////////////////////////////
// 发送再玩一次请求
//////////////////////////////////////////////////////////////////////////
void CTable::PlayAgain()
{
    MSGSTRUCT msg;
    msg.uMsg = MSG_PLAYAGAIN;
    m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
}
//////////////////////////////////////////////////////////////////////////
// 发送和棋请求
//////////////////////////////////////////////////////////////////////////
void CTable::DrawGame()
{
    CDialog *pDlg = (CDialog *)AfxGetMainWnd();
    // 使按钮失效
    pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
    pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
    pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
    // 设置等待标志
    SetWait( TRUE );
    MSGSTRUCT msg;
    msg.uMsg = MSG_DRAW;
    m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
}
//////////////////////////////////////////////////////////////////////////
// 设置游戏模式
//////////////////////////////////////////////////////////////////////////
void CTable::SetGameMode( int nGameMode )
{
    if ( 1 == nGameMode )
        m_pGame = new COneGame( this );
    else
        m_pGame = new CTwoGame( this );
    m_pGame->Init();
}
//////////////////////////////////////////////////////////////////////////
// 悔棋
//////////////////////////////////////////////////////////////////////////
void CTable::Back()
{
    m_pGame->Back();
}
//////////////////////////////////////////////////////////////////////////
// 处理对方落子后的工作
//////////////////////////////////////////////////////////////////////////
void CTable::Over()
{
    // 判断对方是否胜利
    if ( Win( 1 - m_color ) )
    {
        CFiveApp *pApp = (CFiveApp *)AfxGetApp();
        pApp->m_nLost++;
        CDialog *pDlg = (CDialog *)GetParent();
        PlaySound( MAKEINTRESOURCE( IDR_WAVE_LOST ), NULL, SND_RESOURCE | SND_SYNC );
        pDlg->MessageBox( _T("您输了,不过不要灰心,失败乃成功之母哦!"), _T("失败"), MB_ICONINFORMATION );
        pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
        pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
        pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
        // 如果是网络对战,则生效“重玩”
        if ( m_bConnected )
        {
            pDlg->GetMenu()->EnableMenuItem( ID_MENU_PLAYAGAIN, MF_ENABLED | MF_BYCOMMAND );
        }
        return;
    }
    m_bWait = FALSE;
}
//////////////////////////////////////////////////////////////////////////
// 设置菜单状态(主要为网络对战准备)
//////////////////////////////////////////////////////////////////////////
void CTable::SetMenuState( BOOL bEnable )
{
    UINT uEnable, uDisable;
    if ( bEnable )
    {
        uEnable = MF_ENABLED;
        uDisable = MF_GRAYED | MF_DISABLED;
    }
    else
    {
        uEnable = MF_GRAYED | MF_DISABLED;
        uDisable = MF_ENABLED;
    }
    CMenu *pMenu = GetParent()->GetMenu();
    pMenu->GetSubMenu( 0 )->EnableMenuItem( 0, uEnable | MF_BYPOSITION );
    pMenu->EnableMenuItem( ID_MENU_SERVER, uEnable );
    pMenu->EnableMenuItem( ID_MENU_CLIENT, uEnable );
    pMenu->EnableMenuItem( ID_MENU_LEAVE, uDisable );
    pMenu->EnableMenuItem( ID_MENU_PLAYAGAIN, uEnable );
}
//////////////////////////////////////////////////////////////////////////
// 接受连接
//////////////////////////////////////////////////////////////////////////
void CTable::Accept( int nGameMode )
{
    if ( 2 == nGameMode )
    {
        m_sock.Accept( m_conn );
    }
    m_bConnected = TRUE;
 SetColor( 0 );
    Clear( FALSE );
    SetGameMode( nGameMode );
}
//////////////////////////////////////////////////////////////////////////
// 主动连接
//////////////////////////////////////////////////////////////////////////
void CTable::Connect( int nGameMode )
{
    SetColor( 1 );
    Clear( TRUE );
    SetGameMode( nGameMode );
}
//////////////////////////////////////////////////////////////////////////
// 发送聊天消息
//////////////////////////////////////////////////////////////////////////
void CTable::Chat( LPCTSTR lpszMsg )
{
    MSGSTRUCT msg;
    msg.uMsg = MSG_CHAT;
    lstrcpy( msg.szMsg, lpszMsg );
    m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
}
//////////////////////////////////////////////////////////////////////////
// 发送认输消息
//////////////////////////////////////////////////////////////////////////
void CTable::GiveUp()
{
    CFiveApp *pApp = (CFiveApp *)AfxGetApp();
    pApp->m_nLost++;
    CDialog *pDlg = (CDialog *)AfxGetMainWnd();
    // 使按钮失效
    pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
    pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
    pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
    // 修改等待状态
    SetWait( TRUE );
    // 生效菜单项
    CMenu *pMenu = pDlg->GetMenu();
    pMenu->EnableMenuItem( ID_MENU_PLAYAGAIN, MF_ENABLED | MF_BYCOMMAND );
    // 发送认输消息
    MSGSTRUCT msg;
    msg.uMsg = MSG_GIVEUP;
    m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
}
//////////////////////////////////////////////////////////////////////////
// 接收来自对方的数据
//////////////////////////////////////////////////////////////////////////
void CTable::Receive()
{
    MSGSTRUCT msgRecv;
    m_pGame->ReceiveMsg( &msgRecv );
    // 对各种消息分别进行处理
    switch ( msgRecv.uMsg )
    {
    case MSG_PUTSTEP:
        {
            PlaySound( MAKEINTRESOURCE( IDR_WAVE_PUT ), NULL, SND_RESOURCE | SND_SYNC );
            SetData( msgRecv.x, msgRecv.y, msgRecv.color );
            // 大于1步才能悔棋
            GetParent()->GetDlgItem( IDC_BTN_BACK )->EnableWindow( m_pGame->m_StepList.size() > 1 );
            Over();
        }
        break;
    case MSG_BACK:
        {
            if ( IDYES == GetParent()->MessageBox( _T("对方请求悔棋,接受这个请求吗?"),
                _T("悔棋"), MB_ICONQUESTION | MB_YESNO ) )
            {
                // 发送允许悔棋消息
                MSGSTRUCT msg;
                msg.uMsg = MSG_AGREEBACK;
                m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
                // 给自己悔棋
                STEP step;
                step = *( m_pGame->m_StepList.begin() );
                m_pGame->m_StepList.pop_front();
                m_data[step.x][step.y] = -1;
                step = *( m_pGame->m_StepList.begin() );
                m_pGame->m_StepList.pop_front();
                m_data[step.x][step.y] = -1;
                // 大于1步才能悔棋
GetParent()->GetDlgItem( IDC_BTN_BACK )->EnableWindow( m_pGame->m_StepList.size() > 1 );
                Invalidate();
            }
            else
            {
                // 发送不允许悔棋消息
                MSGSTRUCT msg;
                msg.uMsg = MSG_REFUSEBACK;
                m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
            }
        }
        break;
    case MSG_REFUSEBACK:
        {
            CDialog *pDlg = (CDialog *)AfxGetMainWnd();
            pDlg->MessageBox( _T("很抱歉,对方拒绝了您的悔棋请求。"), _T("悔棋"), MB_ICONINFORMATION );
            pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow();
            pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow();
            pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow();
            RestoreWait();
        }
        break;
    case MSG_AGREEBACK:
        {
            STEP step;
            step = *( m_pGame->m_StepList.begin() );
            m_pGame->m_StepList.pop_front();
            m_data[step.x][step.y] = -1;
            step = *( m_pGame->m_StepList.begin() );
            m_pGame->m_StepList.pop_front();
            m_data[step.x][step.y] = -1;
            CDialog *pDlg = (CDialog *)AfxGetMainWnd();
            pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow();
            pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow();
            // 大于1步才能悔棋
 pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( m_pGame->m_StepList.size() > 1 );
            RestoreWait();
            Invalidate();
        }
        break;
    case MSG_DRAW:
        {
            if ( IDYES == GetParent()->MessageBox( _T("对方请求和棋,接受这个请求吗?"),_T("和棋"), MB_ICONQUESTION | MB_YESNO ) )
            {
                CFiveApp *pApp = (CFiveApp *)AfxGetApp();
                pApp->m_nDraw++;
                // 发送允许和棋消息
                MSGSTRUCT msg;
                msg.uMsg = MSG_AGREEDRAW;
                m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
                // 和棋后,禁用按钮和棋盘
                CDialog *pDlg = (CDialog *)GetParent();
                pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
                pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
                pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
                SetWait( TRUE );   // 使“重玩”菜单生效
                pDlg->GetMenu()->EnableMenuItem( ID_MENU_PLAYAGAIN, MF_ENABLED | MF_BYCOMMAND );
            }
            else
            {   // 发送拒绝和棋消息
                MSGSTRUCT msg;
                msg.uMsg = MSG_REFUSEDRAW;
                m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
            }
        }
        break;
    case MSG_AGREEDRAW:
        {
            CFiveApp *pApp = (CFiveApp *)AfxGetApp();
            pApp->m_nDraw++;
            CDialog *pDlg = (CDialog *)GetParent();
            pDlg->MessageBox( _T("看来真是棋逢对手,对方接受了您的和棋请求。"), _T("和棋"), MB_ICONINFORMATION );  // 和棋后,使“重玩”菜单生效
            pDlg->GetMenu()->EnableMenuItem(ID_MENU_PLAYAGAIN, MF_ENABLED | MF_BYCOMMAND );
        }
        break;
    case MSG_REFUSEDRAW:
        {
            CDialog *pDlg = (CDialog *)GetParent();
            pDlg->MessageBox( _T("看来对方很有信心取得胜利,所以拒绝了您的和棋请求。"),_T("和棋"), MB_ICONINFORMATION );
            // 重新设置按钮状态,并恢复棋盘状态
            pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow();
            pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow();
            pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow();
            RestoreWait();
        }
        break;
    case MSG_CHAT:
        {
            CString strAdd;
            strAdd.Format( _T("%s 说:%s\r\n"), m_strAgainst, msgRecv.szMsg );
            CEdit *pEdit = (CEdit *)GetParent()->GetDlgItem( IDC_EDT_CHAT );
            pEdit->SetSel( -1, -1, TRUE );
            pEdit->ReplaceSel( strAdd );
        }
        break;
    case MSG_INFORMATION:
        {
            m_strAgainst = msgRecv.szMsg;
            GetParent()->GetDlgItem( IDC_ST_ENEMY )->SetWindowText( m_strAgainst );
            // 在先手接到姓名信息后,回返自己的姓名信息
            if ( 0 == m_color )
            {
                MSGSTRUCT msg;
                msg.uMsg = MSG_INFORMATION;
                lstrcpy( msg.szMsg, m_strMe );
                m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
            }
        }
        break;
    case MSG_GIVEUP:
        {
            CFiveApp *pApp = (CFiveApp *)AfxGetApp();
            pApp->m_nWin++;
            CDialog *pDlg = (CDialog *)GetParent();
            pDlg->MessageBox( _T("对方已经投子认输,恭喜您不战而屈人之兵!"), _T("胜利"), MB_ICONINFORMATION );
            // 禁用各按钮及棋盘
            pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
            pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
            pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
            SetWait( TRUE );
            // 设置“重玩”为真
            pDlg->GetMenu()->EnableMenuItem( ID_MENU_PLAYAGAIN, MF_ENABLED | MF_BYCOMMAND );
        }
        break;
    case MSG_PLAYAGAIN:
        {
            CDialog *pDlg = (CDialog *)GetParent();
            if ( IDYES == pDlg->MessageBox( _T("对方看来意犹未尽,请求与您再战一局,接受这个请求吗?\n\n选“否”将断开与他的连接。"),
                _T("再战"), MB_YESNO | MB_ICONQUESTION ) )
            {
                pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
                pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow();
                pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow();
                MSGSTRUCT msg;
                msg.uMsg = MSG_AGREEAGAIN;
                m_conn.Send( (LPCVOID)&msg, sizeof( MSGSTRUCT ) );
                Clear( (BOOL)m_color );
                SetGameMode( 2 );
            }
            else
            {
                m_conn.Close();
                m_sock.Close();
                pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
                pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
                pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
                pDlg->GetDlgItem( IDC_CMB_CHAT )->EnableWindow( FALSE );
                // 设置菜单状态
                SetMenuState( TRUE );
                // 设置棋盘等待状态
                SetWait( TRUE );
                // 设置网络连接状态
                m_bConnected = FALSE;
                // 重新设置玩家名称
                pDlg->SetDlgItemText( IDC_ST_ENEMY, _T("无玩家加入") );
            }
        }
        break;
    case MSG_AGREEAGAIN:
        {
            CDialog *pDlg = (CDialog *)GetParent();
            pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
            pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow();
            pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow();
            Clear( (BOOL)m_color );
            SetGameMode( 2 );
        }
        break;
    }
}
// 消息映射表
BEGIN_MESSAGE_MAP( CTable, CWnd )
 //{{AFX_MSG_MAP(CTable)
    ON_WM_PAINT()
    ON_WM_LBUTTONUP()
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////////////////
// 处理WM_PAINT消息
//////////////////////////////////////////////////////////////////////////
void CTable::OnPaint()
{
    CPaintDC dc( this );
    CDC MemDC;
    MemDC.CreateCompatibleDC( &dc );
    // 装载棋盘
    CBitmap bmp;
    CPen pen;
    bmp.LoadBitmap( IDB_BMP_QP );
    pen.CreatePen( PS_SOLID, 1, 0xff );
    MemDC.SelectObject( &bmp );
    MemDC.SelectObject( &pen );
    MemDC.SetROP2( R2_NOTXORPEN );
    // 根据棋盘数据绘制棋子
    int x, y;
    POINT pt;
    for ( y = 0; y < 15; y++ )
    {
        for ( x = 0; x < 15; x++ )
        {
            if ( -1 != m_data[x][y] )
            {
                pt.x = 12 + 25 * x;
                pt.y = 84 + 25 * y;
                m_iml.Draw( &MemDC, m_data[x][y], pt, ILD_TRANSPARENT );
            }
        }
    }
    // 绘制最后落子的指示矩形
    if ( NULL != m_pGame && !m_pGame->m_StepList.empty() )
    {
        STEP step = *( m_pGame->m_StepList.begin() );
        MemDC.MoveTo( 11 + 25 * step.x, 83 + 25 * step.y );
        MemDC.LineTo( 36 + 25 * step.x, 83 + 25 * step.y );
        MemDC.LineTo( 36 + 25 * step.x, 108 + 25 * step.y );
        MemDC.LineTo( 11 + 25 * step.x, 108 + 25 * step.y );
        MemDC.LineTo( 11 + 25 * step.x, 83 + 25 * step.y );
    }
    // 完成绘制
    dc.BitBlt( 0, 0, 395, 472, &MemDC,0, 0, SRCCOPY );
}
//////////////////////////////////////////////////////////////////////////
// 处理左键弹起消息,为玩家落子之用
//////////////////////////////////////////////////////////////////////////
void CTable::OnLButtonUp( UINT nFlags, CPoint point )
{
    STEP stepPut;
    if ( m_bWait )
    {
        MessageBeep( MB_OK );
        return;
    }
    int x, y;
    x = ( point.x - 12 ) / 25;
    y = ( point.y - 84 ) / 25;
    // 如果在(0, 0)~(14, 14)范围内,且该坐标没有落子,则落子于此,否则发声警告并退出过程
    if ( x < 0 || x > 14 || y < 0 || y > 14 || m_data[x][y] != -1 )
    {
        MessageBeep( MB_OK );
        return;
    }
    else
    {
        // 如果位置合法,则落子
        SetData( x, y, m_color );
        stepPut.color = m_color;
        stepPut.x = x;
        stepPut.y = y;
        // 大于1步才能悔棋
        GetParent()->GetDlgItem( IDC_BTN_BACK )->EnableWindow( m_pGame->m_StepList.size() > 1 );
    }
    // 判断胜利的情况
    if ( Win( m_color ) )
    {
        CFiveApp *pApp = (CFiveApp *)AfxGetApp();
        pApp->m_nWin++;
        m_pGame->Win( stepPut );
        CDialog *pDlg = (CDialog *)GetParent();
        PlaySound( MAKEINTRESOURCE( IDR_WAVE_WIN ), NULL, SND_SYNC | SND_RESOURCE );
        pDlg->MessageBox( _T("恭喜,您获得了胜利!"), _T("胜利"), MB_ICONINFORMATION );
        pDlg->GetDlgItem( IDC_BTN_HQ )->EnableWindow( FALSE );
        pDlg->GetDlgItem( IDC_BTN_BACK )->EnableWindow( FALSE );
        pDlg->GetDlgItem( IDC_BTN_LOST )->EnableWindow( FALSE );
        m_bWait = TRUE;
        return;
    }
    else
    {
        // 开始等待
        m_bWait = TRUE;
        // 发送落子信息
        PlaySound( MAKEINTRESOURCE( IDR_WAVE_PUT ), NULL, SND_SYNC | SND_RESOURCE );
        m_pGame->SendStep( stepPut );
    }
}
//////////////////////////////////////////////////////////////////////////
// 重新设置先前的等待标志
//////////////////////////////////////////////////////////////////////////
void CTable::RestoreWait()
{
    SetWait( m_bOldWait );
}


以上为本篇毕业论文范文网络游戏开发的介绍部分。
本论文在计算机论文栏目,由论文网(www.zjwd.net)整理,更多论文,请点论文范文查找

毕业论文降重 相关论文

收费专业论文范文
收费专业论文
汉语言文学论文
物理学论文
自动化专业论文
测控技术专业论文
历史学专业论文
机械模具专业论文
金融专业论文
电子通信专业论文
材料科学专业论文
英语专业论文
会计专业论文
行政管理专业论文
财务管理专业论文
电子商务国贸专业
法律专业论文
教育技术学专业论文
物流专业论文
人力资源专业论文
生物工程专业论文
市场营销专业论文
土木工程专业论文
化学工程专业论文
文化产业管理论文
工商管理专业论文
护理专业论文
数学教育专业论文
数学与应用数学专业
心理学专业论文
信息管理专业论文
工程管理专业论文
工业工程专业论文
制药工程专业论文
电子机电信息论文
现代教育技术专业
新闻专业论文
艺术设计专业论文
采矿专业论文
环境工程专业论文
西班牙语专业论文
热能与动力设计论文
工程力学专业论文
酒店管理专业论文
安全管理专业论文
交通工程专业论文
体育教育专业论文
教育管理专业论文
日语专业论文
德语专业论文
理工科专业论文
轻化工程专业论文
社会工作专业论文
乡镇企业管理
给水排水专业
服装设计专业论文
电视制片管理专业
旅游管理专业论文
物业管理专业论文
信息管理专业论文
包装工程专业论文
印刷工程专业论文
动画专业论文
环境艺术专业论文
信息计算科学专业
物流专业论文范文
人力资源论文范文
营销专业论文范文
工商管理论文范文
汉语言文学论文范文
法律专业论文范文
教育管理论文范文
小学教育论文范文
学前教育论文范文
财务会计论文范文

电子商务论文范文

上一篇:基于DREAMWARE和ASP技术的班级网.. 下一篇:网络五子棋开题报告表

最新论文

精品推荐

毕业论文排版

热门论文


本站简介 | 联系方式 | 论文改重 | 免费获取 | 论文交换

本站部分论文来自网络,如发现侵犯了您的权益,请联系指出,本站及时确认删除 E-mail:229120615@qq.com

毕业论文范文-论文范文-论文同学网(www.zjwd.net)提供计算机论文毕业论文,毕业论文范文,毕业设计,论文范文,毕业设计格式范文,论文格式范文

Copyright@ 2010-2024 zjwd.net 毕业论文范文-论文范文-论文同学网 版权所有