版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
绪论2-4-四国军棋目录TOC\o"1-3"\h\z第一章绪论 11.1问题概述 11.1.1问题的来源 11.1.2目的和意义 11.1.3国内外研究现状 11.2问题剖析 21.2.1主要问题 21.2.2难点和关键 31.2.3思路和方法 3第二章相关的知识和工具 42.1解决问题的知识基础 42.2开发平台 52.3数据库 5第三章总体设计 63.1总体设计的框架 63.2模块功能概述 73.3关键算法 83.4关键技术 12第四章详细设计 144.1数据库结构 144.2模块结构 144.3重要模块详述 15第五章程序编码 195.1数据结构 195.2主要界面 225.3重要模块程序实现 24第六章问题和展望 326.1特色与成功 326.2问题与展望 32参考文献 33附录 34第一章绪论1.1问题概述1.1.1问题的来源随着Internet的普及,网络游戏已经成为大家耳濡目染的新生事物。网络游戏从出现到现在的发展的时间很短,但是其发展速度却非常之快。现在,可以说网络游戏已经成为人们休闲、娱乐的有效方式。国内比较有名气的网络游戏有联众网络游戏世界()、qq游戏中心()、中国游戏中心()以及这几年受玩家亲睐的传奇、魔兽世界等。1.1.2目的和意义对当今网络游戏的设计、架构进行分析、探索和实践。提供友好的客户操作界面,通过客户端与服务器之间的网络传递数据,实现了多人协同游戏的目的。分析现今网络游戏体系结构及设计模式的优缺点,及对网络游戏的发展给予分析和展望。1.1.3国内外研究现状现今网络游戏的体系结构(见图1-1),包括客户机程序、服务器程序、数据库服务器。图1-1网络游戏体系结构1.2问题剖析1.2.1主要问题在开发网络游戏时,首先要建立底层的网络通信类,利用网络通信类连接构建客户服务器之间的TCP/IP连接,然后在该连接的基础上利用自设定的协议进行客户端登录、进行游戏等操作。在以上协议的基础上,根据不同的游戏编写不同的游戏逻辑处理类,在该逻辑处理类中实现了对应的游戏逻辑,如实例中的军棋,则实现相互之间的对弈等功能。同时在服务器端还需要和数据库服务器交互,用于读取或保存客户信息(如用户积分、密码、个人资料等数据)。1.2.2难点和关键1、有一个或多个游戏服务器启动特定游戏服务。2、游戏者到游戏网站上下载客户端程序并且申请游戏账号ID。然后启动客户端程序通过某种网络协议连接游戏服务器。3、客户端程序负责处理客户端显示和操作界面,具有简单的逻辑处理功能,同时负责接收发送与服务器端交互的数据包。4、服务器程序负责处理服务器端逻辑、游戏逻辑、客户之间的网络信息传递,以及数据库之间的数据读取保存工作。同时服务器端还要承担客户端数据的接收、转发工作。1.2.3思路和方法网络游戏通常的运行方式(见图1-2)。图1-2网络游戏交互图网络游戏相关的知识和工具6-6-第二章相关的知识和工具2.1解决问题的知识基础网络游戏常用的网络协议有适用于Internet的TCP/IP协议、适用局域网(比如星际)的IPX协议。(一)TCP/IP协议TCP/IP协议(TransmissionControlProtocol/InternetProtocol,传输控制协议/网际协议)是Internet中计算机进行通信的标准,其命名起源于该组协议中最重要的两个协议TCP和IP。任何关于Internet协议的讨论必须由TCP/IP开始,它也是其他所有协议的基础。TCP/IP协议是Internet网络的共同语言,主机之间必须利用TCP/IP互通信息。TCP/IP协议目前已经成为发展最成功的通信协议之一,它起源于20世纪60年代末美国政府资助的一个分级交换网络研究项目,允许分布在各地的使用不同操作系统的网络进行通信。随着世界范围个人电脑的普及,日常无论收发邮件、访问网页和文件传输都已经离不开TCP/IP协议,TCP/IP协议已经成为Internet的基础。(二)TCP/IP结构TCP/IP实际上就是在物理网上的一组完整的协议。其核心部分是传输层协议(TCP/UDP)、网络层(IP)和物理接口层,这三层通常在操作系统内核中实现。TCP/UDP层提供了传输层服务,而IP协议提供了网络层服务。TCP/IP协议是一个四层协议,其结构如图2-1所示。图2-1TCP/IP协议体系结构图应用程序与TCP/IP可靠传输之间接口具有五大特性:面向数据流当两个用户进程传输大量数据时,我们把这些数据当做可划分为八位组(octer,字节)的比特流,在目的机器上运行的数据流投递服务软件提给接收方的八位组与信源机上发送方送出来的完全相同。虚电路连接数据流的传输与电话相似,使用“虚电路”这个术语来描述这种连接是因为在应用程序看来这种连接像是一条专用的硬件电路,这种可靠连接的错觉是由数据流投递服务提供。有缓冲的传送使用虚电路服务来发送数据流的应用程序不断向协议软件递交数据八位组。为了提高效率以及减少网络延迟,协议软件在实现时都会从数据流中收集到足够多的数据,组成大小合理的数据包后再送到网络上传输。无结构的数据TCP/IP协议并不区分结构化的数据流。使用数据流的应用程序必须在开始连接之前就了解数据流的内容并对其格式进行协商。这点很重要,在程序中表现为send函数只能发送字符串,这就需要将接收的字符串转化需要的结构化的数据。全双工连接TCP/IP流服务所提供的功能是双向的全双工连接。其中包括了两个独立、流向相反的数据流,而这两个数据流之间不进行显式的交互。常用协议主要包括TCP/UDP层协议和IP层协议。TCP和UDP都是传输层协议,都使用IP协议作为网络层协议。使用UDP协议的应用程序必须承担可靠性的工作,包括报文的丢失、重复、乱序以及连接失效等问题,而程序员编程时则容易疏忽。2.2开发平台操作系统Windows2000,开发工具VisualC++6.0。2.3数据库SQLServer2000,是一个全面的数据库平台,引擎为关系型数据和结构化数据提供了安全可靠的存储功能。总体设计14-14-第三章总体设计3.1总体设计的框架军棋游戏的总体设计框架,客户端如图3-1所示。图3-1军棋游戏架构-客户端军棋游戏的总体设计框架,服务器如图3-2所示。图3-2军棋游戏架构-服务器3.2模块功能概述客户端类的划分:游戏基本类:负责处理游戏中一些完成基本功能的类,如处理声音类、处理动画图标,图形按钮等类,基本类的特点是被其他类在特定处所调用,并不动生成对象。游戏框架类:负责处理游戏中客户端用于显示程序界面和绘制游戏界面以及显示用户信息和广告信息等处理任务。游戏通信类:负责处理游戏中客户服务器之间的网络传输细节,从而在编程中不用考虑网络通信细节,达到客户和服务器之间的透明的效果。游戏应用程序类:主要负责处理应用程序中各种设置显示对话框、程序主线程处理、程序中基本的运行类框架的管理,以及游戏中图形的处理和显示等任务的处理。游戏处理类:主要用于处理游戏简单逻辑、负责解析和处理与服务器端交互的游戏数据,以及在游戏运行中维护游戏中的各种数据,同时维护处理游戏主线程逻辑等功能。服务器类的划分:游戏通信类:负责处理游戏中客户服务器之间的网络传输细节,从而在编程中不用考虑网络通信细节,达到客户和服务器之间透明传输的效果。游戏协议类:负责处理游戏中客户服务器之间交互所传递的数据,并且对该数据格式进行打包和解包,同时根据该包中所包含的指令串进行相应的操作。游戏逻辑类:负责处理游戏逻辑,如军棋游戏中用于维护军棋逻辑,判断下棋,得分等处理类。用户管理类:用于管理用户资料,在用户登录后通过数据库验证用户名和密码,通过验证后从数据库读取用户的详细资料。同时在程序中维护用户数据,在用户游戏结束和退出游戏时将用户数据保存到数据库中。服务器框架类:用于管理游戏大厅的数据,包括一些数据的列表。数据库类:用于网络游戏的服务器端在处理大量的客户资料时,使用数据库进行大量数据的存储和查询所调用的类方法。3.3关键算法模块间的数据传递设计,如图3-3所示。图3-3模块间的数据传递设计这样,在CTableView和CGameDlg中通过指针,也可向服务器发送消息。而CCGameHallFrameView收到消息后,同时也控制CTableView和CGameDlg的行为。采用内存作图的方式,消除了闪烁。首先创建一个内存dc,将绘图的工作先在内存dc中做好,再贴到实际dc上去。做法如下:首先创建关于屏幕的内存DC,代码为MemDC.CreateCompatibleDC(pDC);之后创建一幅关于屏幕DC的图画,部分代码如下:CRectrect;this->GetClientRect(rect);CBitmapbmpFace;bmpFace.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());注意把握rect的尺寸为客户区域大小;之后将这幅画选入内存DC中,部分代码如下:CBitmap*pOldBmp=NULL;pOldBmp=MemDC.SelectObject(&bmpFace);之后可以开始在内存DC中进行任何绘制动作;部分代码如下:CBrushbrush(RGB(255,255,255));MemDC.FillRect(rect,&brush);for(inti=0;i<500;i++){MemDC.MoveTo(22+i,22);MemDC.LineTo(22+i,277);
}绘制完后将内存DC中的这幅图绘制到屏幕DC中来,部分代码如下:pDC->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&MemDC,rect.left,rect.top,SRCCOPY);最后进行相关的资源回收动作,部分代码如下:MemDC.SelectObject(pOldBmp);bmpFace.DeleteObject();。同时我们要把系统的ON_WM_ERASEBKGND消息函数重载为returnFALSE,否则还是会出现闪烁情况。在对话框中用这个方法的时候,要注意将有控件的部分和需要绘图的部分分开,可采用如下方法,部分代码如下::CRectrectClient;CRgnrgn1,rgn2;//rgn3,rgn4; GetClientRect(rectClient); rgn1.CreateRectRgnIndirect(rectClient); rgn2.CreateRectRgn(730,0,962,670); if(rgn1.CombineRgn(&rgn1,&rgn2,RGN_XOR)==ERROR) { return; }MemDC.FillRgn(&rgn1,&brush);服务器对连接的用户数据的数据结构的设计,用下面两个结构:typedefstructTT{ intd[4]; intID[4]; intboard[17][17]; BOOLbegin[4]; }; typedefstructMM{ CStringname,sex; intscore; };连接的socket保存在如下链表中:typedefCList<SOCKET,SOCKET&>SOCKET_ARRAY;SOCKET_ARRAYm_connectionList;与服务器建立连接后,相应的用户数据,保存在MM结构数组中,位置为其socket在m_connectionList中的相应位置,这样就可以通过连接的socket找到相应的信息。TT是保存大厅数据的结构,d表示四个坐位有没有人,ID表示就坐的人的socket在链表中的位置。board为棋盘数据。每个桌子有每个桌子的棋盘数据,互不干扰。随着游戏的进行而不断更新。begin表示四个玩家有没有下调度完成的指令。应用矩阵的变换实现坐标旋转。不管你坐在哪个方位,玩游戏时,你始终是在正下方,这就需要实现虚拟坐标到目标坐标的旋转变换,如图3-4。图3-4坐标图X为实际坐标,x为虚拟坐标,转换公式推导如下,见图3-5。图3-5坐标变换公式推导direct为自己座位方向,switch(direct)case0:(a=90o)[X,Y,1]=[-y+16,x,1]//东case1:(a=0o)[X,Y,1]=[x,y,1]//南case2:(a=-90o)[X,Y,1]=[y,-x+16,1]//西case3:(a=180o)[X,Y,1]=[-x+16,-y+16,1]//北回溯法求工兵路径,军棋游戏逻辑不复杂,只有工兵能够自由飞行。在这里没有求工兵起点到目标点的最短路径,而只是用回溯法求出其中一条路径,所以在游戏中你会发现工兵有时候会绕些弯路。这和老鼠走迷宫差不多的。工兵迷宫数组:intRAILWAY[17][17]={ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,0,0,0,1,1,3,1,3,1,1,0,0,0,0,0}, {0,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1,0}, {0,1,0,0,0,2,2,0,2,0,2,2,0,0,0,1,0}, {0,1,0,0,0,1,1,3,1,3,1,1,0,0,0,1,0}, {0,1,0,0,0,2,2,0,2,0,2,2,0,0,0,1,0}, {0,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1,0}, {0,0,0,0,0,1,1,3,1,3,1,1,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},}0表示该位置不在工兵铁道上,1表示在该位置可向上下左右方向移动,2表示在该位置只能向上下方向移动,3表示在该位置只能向左右方向移动。3.4关键技术网络通信,WindowsSocket编程接口:WindowsSocket(简称WinSock)是在Win32平台上访问基层网络协议的接口。在不同的Win32平台上,WindowsSocket以不同的方式存在着,作为网络编程接口而不是协议存在。套接字(Socket)概念,套接字是从英文单词Socket翻译而来,它是网络通信的基本操作单元,是应用层到传输层的接口,可以将套接字看作不同主机间的进程进行双向通信的端点。WindowsSocket组成部分,WindwosSockets实现一般都由两部分组成:开发组件和运行组件。开发组件是供程序员开发WindowsSockets应用程序使用的,主要是WinSock.h头文件。对于WindowsSockets应用程序的源文件来说,只要包括WinSock.h就可以了。除此之外,在使用WinSock的项目中还需要加入WinSockAPI引入库wsock32.lib。运行组件是WindowsSockets应用程序接口的动态连接库(DLL),文件名为WinSock.dll,应用程序在执行时通过装入它实现网络通信功能。WindowsSockets编程的基本模式,要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。使用Socket进行网络通信一般有两种方式:基于面向连接的流方式和基于无连接的数据报方式。面向连接的的流方式调用过程如图3-6所示。图3-6面向连接的流方式调用过程详细设计34-19-第四章详细设计4.1数据库结构服务器端数据库结构如表4-1。User列名数据类型长度允许空描述主键namechar10no用户名codechar10no密码scoreint4yes游戏积分sexchar2yes性别表4-1,玩家信息用作记录玩家游戏数据。4.2模块结构客户端模块结构:游戏基本类,该类包中包括CBorderButton、CClockObject、CWave类。游戏框架类,该类包中包括CMainFrame、CCGameHallFrameView、CHtmlViewEx、CTableView类。游戏通信类,该类包中包括CClient类。游戏应用程序类,该类包包括CGameDlg类,其中包括对游戏处理类的调用。游戏处理类,该类包中主要包括TakeGame类。服务器模块结构:游戏通信类,由CServer类实现。游戏协议类,由CServerProtocol类实现。游戏逻辑类,由CServerLogic类实现。数据库类,由CServerFrameSet类实现。用户管理类,由CServerFrameView类实现。服务器框架类,由CServerFrame类实现。4.3重要模块详述客户端模块:CBorderButton类是带有边框的图片按钮类,CClockObject类是时钟类,CWave是用来播放声音的类,当游戏用户下棋,吃子,起身或坐下时要播放声音,选择在程序中调用API函数PlaySound直接播放在资源中的声音文件。模块描述如图4-1。图4-1游戏基本类模块描述CMainFrame类是游戏窗口中的游戏大厅框架类,其中包括构架广告显示窗口,可玩的游戏类、工具栏、游戏桌的信息、显示信息等,CCGameHallFrameView类是用来在游戏大厅窗口左侧显示可以玩的游戏以及游戏室,采用树状结构显示。CHtmlViewEx类用于构造广告显示窗口,用于游戏框架中显示广告页。CTableView显示包含游戏桌的游戏大厅,在大厅中多人在等待开始游戏。模块描述如图4-2。图4-2游戏框架类模块描述游戏通信类,CClient,功能为建立和服务器的连接,能及处理通信,采用异步机制,以自定义消息事件处理通信等功能。采用自定义消息:WM_USER+101。模块描述如图4-3。图4-3游戏通信类模块描述游戏应用程序类,CGameDlg,其中包括对游戏处理类的调用。CGameDlg主要负责处理应用程序中各种设置显示对话框、程序主线程处理、以及游戏中的图形的处理和显示等任务的处理。模块描述如图4-4。图4-4游戏应用程序类模块描述游戏处理类,TakeGame主要用于处理游戏简单逻辑、负责解析和处理与服务器端交互的游戏数据,以及在游戏运行时维护游戏中的各种数据,同时维护处理游戏主线程逻辑等功能。模块描述如图4-5。图4-5游戏处理类模块描述服务器模块:游戏通信类,由CServer类实现。负责处理游戏中客户与服务器之间的网络连接细节,从而使得对于客户和服务器之间的数据传输可以忽略通信细节。模块描述如图4-6。图4-6游戏通信类模块描述游戏协议类,由CServerProtocol类实现。游戏协议类负责解析客户/服务器端所传输的协议,同时根据不同的协议调用不同的操作函数,并根据用户状态变化维护用户信息。模块描述如图4-7。图4-7游戏协议类模块描述游戏逻辑类负责处理游戏逻辑,在军棋游戏中包括计算得分,计算赢等。游戏逻辑类CServerLogic和游戏协议类分离的优点是可以只通过修改逻辑类来改变为不同的网络游戏,如修改为“象棋逻辑”即可以成为象棋游戏,修改为“升级逻辑”即可以成为升级游戏。用户管理类,直接在CCServerFrameView中实现。数据库类,采用SQLServer2000数据库,只是用来保存玩家的资料等一些数据。采用MFCODBC数据库编程,在程序中为CServerFrameSet类。正如MFC提供的其他类库很好地对相应的Win32API作了封装,MFC提供的ODBC类库也相应地对ODBCAPI作了封装,通过提供一种高级接口而避免直接使用ODBCAPI所涉及的种种繁琐处理,简化了对ODBC数据库的应用程序编程。模块描述如图4-8。图4-8数据库类模块描述程序编码32-第五章程序编码5.1数据结构用17×17的数组表示军棋的棋盘,如下:intBOARD[17][17]={ {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,0,0,0,0,0,0,0,-1,-1,-1,-1,-1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,-1,0,-1,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,-1,0,-1,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {-1,-1,-1,-1,-1,0,7,3,4,5,3,0,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,10,0,1,0,9,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,8,10,0,1,5,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,6,0,4,0,3,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,2,7,6,1,11,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,11,12,11,2,2,-1,-1,-1,-1,-1,-1},};-1表示棋盘以外的位置0表示棋盘内位置,但无棋子1工兵2排长3连长4营长5团长6旅长7师长8军长9司令10炸弹11地雷12我方军旗13上方盟友或敌方的棋子14左边敌方的棋子15右边敌方的棋子16上方军旗17左方军旗18右方军旗为了处理工兵的走法的方便,将四个转角斜对的位置恒置为0,这样在为工兵寻找路径时会方便一些。游戏协议以char(20)作为一次信息的起始点,终点。“+”在字符串中用空格代替。以一字符串作为向服务发出的请求信息,服务器也以相应的一字符串发送回客户端作为应答。对话:A+桌子号+聊天内容坐下:B+桌子号+方位调度完成:C+桌子号+方位+(棋盘数据)[用一维数据形式表示6×5的二维数组,数据之间用空格开。开始游戏:(只由服务器发出)D+对战类型下棋:E+桌子号+方位+起点(x,y)+终点(x,y)吃:(只由服务器发出)(包括移动)F+起点(x,y)+终点(x,y)被吃:(只由服务器发出)G+起点(x,y)+终点(x,y)炸:(只由服务器发出)H+起点(x,y)+终点(x,y)输:离开:(包括断线)投降:I+方位求和:K+桌子号+方位注册:L+用户名+密码注册成功:M+用户名登录:N+用户名+密码登录结果:O+1/0离开:P+桌子号+方位请求接收信息:Q+桌子号+方位碰:R+起点(x,y)+终点(x,y)5.2主要界面服务器界面如图5-1所示。图5-1服务器启动界面客户端大厅界面如图5-2所示。图5-2客户端大厅界面军棋游戏界面,如图5-3所示:图5-3军棋游戏界面5.3重要模块程序实现客户端三拆分窗口的实现BOOLCMainFrame::OnCreateClient(LPCREATESTRUCTlpcs,CCreateContext*pContext){ if(NULL==m_wndSplitter.CreateStatic(this,1,2)) returnFALSE; if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CTableView),CSize(620,800),pContext)) returnFALSE; if(NULL==m_wndLeftSplitter.CreateStatic(&m_wndSplitter,2,1, WS_CHILD|WS_VISIBLE,m_wndSplitter.IdFromRowCol(0,1))) returnFALSE; if(!m_wndLeftSplitter.CreateView(0,0,RUNTIME_CLASS(CCGameHallFrameView),CSize(380,300),pContext)|| !m_wndLeftSplitter.CreateView(1,0,RUNTIME_CLASS(CHtmlViewEx),CSize(380,500),pContext)) returnFALSE; pWebView=(CHtmlViewEx*)m_wndLeftSplitter.GetPane(1,0); pServerTreeView=(CCGameHallFrameView*)m_wndLeftSplitter.GetPane(0,0) pTableView=(CTableView*)m_wndSplitter.GetPane(0,0); pServerTreeView->pTable=pTableView; pWebView->Navigate2("/zhou207",0,NULL); returnTRUE;}客户端初始化及连接voidCClient::ClientInit(){ if(WSAAsyncSelect(m_hSocket,m_hWnd,CLI_MESSAGE,FD_READ|FD_WRITE|FD_CLOSE|FD_CONNECT)>0) { AfxMessageBox("Errorinselect"); }}BOOLCClient::InitAndConnect(HWNDhWnd,UINTport,CStringstrServer){ m_hWnd=hWnd; m_uPort=port; m_strServer=strServer; if(m_hSocket!=NULL) { //先将以前打开的套接字关闭 closesocket(m_hSocket); m_hSocket=NULL; } //创建面向连接的socket m_hSocket=socket(AF_INET,SOCK_STREAM,0); ASSERT(m_hSocket!=NULL); ClientInit(); //设置连接信息:网络协议+IP地址+端口 m_addr.sin_family=AF_INET; m_addr.sin_addr.S_un.S_addr=inet_addr(m_strServer.GetBuffer(0)); m_addr.sin_port=htons(m_uPort); //连接服务器 intret=0; interror=0; ret=connect(m_hSocket,(LPSOCKADDR)&m_addr,sizeof(m_addr)); if(ret==SOCKET_ERROR) { //连接失败 if(GetLastError()!=WSAEWOULDBLOCK) { AfxMessageBox(_T("请确认服务器确实已经打开并工作在同样的端口!")); returnFALSE; } } returnTRUE;}服务器初始化及连接voidCServer::ServerInit(){ //设置socket的异步模式 if(WSAAsyncSelect(m_hSocket,m_hWnd,SER_MESSAGE,FD_ACCEPT|FD_READ|FD_WRITE|FD_CLOSE)>0) AfxMessageBox("errorselect");}BOOLCServer::InitAndListen(HWNDhWnd,UINTport){ m_uPort=port; m_hWnd=hWnd; if(m_hSocket!=NULL) { //先关闭已经打开的socket closesocket(m_hSocket); m_hSocket=NULL; } //创建面向连接的流方式的套接字 m_hSocket=socket(AF_INET,SOCK_STREAM,0); ASSERT(m_hSocket!=NULL); ServerInit(); m_addr.sin_family=AF_INET; m_addr.sin_addr.S_un.S_addr=INADDR_ANY; m_addr.sin_port=htons(m_uPort); intret=0; interror=0; //绑定套接字到本机的某个端口 ret=bind(m_hSocket,(LPSOCKADDR)&m_addr,sizeof(m_addr)); if(ret==SOCKET_ERROR) { AfxMessageBox("BindingError"); returnFALSE; } ret=listen(m_hSocket,64); if(ret==SOCKET_ERROR) { AfxMessageBox("ListenError"); returnFALSE; }returnTRUE;}回溯法寻找工兵路径BOOLTakeGame::TakeEngineer(CPointfrom,CPointto){ POSp;intx,y;engineer=TRUE; p.d=0;p.x=from.x;p.y=from.y;top=0; if(RAILWAY[p.x][p.y]==2) p.d=1; stack[top]=p;intf[17][17]; while(top>=0) { p=stack[top]; if(p.x==to.x&&p.y==to.y) returnTRUE; if(p.d<4) { x=p.x+d[p.d][0];y=p.y+d[p.d][1]; if(x==to.x&&p.y==to.y) returnTRUE; if(board[x][y]==0&&RAILWAY[x][y]!=0&&f[x][y]!=1) { if(RAILWAY[p.x][p.y]==2||RAILWAY[p.x][p.y]==3) stack[top].d+=2; else stack[top].d+=1; if(RAILWAY[x][y]==2) { f[stack[top].x][stack[top].y]=1; p.x=x;p.y=y;p.d=1; stack[++top]=p; }else { f[stack[top].x][stack[top].y]=1; p.x=x;p.y=y;p.d=0; stack[++top]=p; } } else { if(RAILWAY[p.x][p.y]==2||RAILWAY[p.x][p.y]==3) stack[top].d+=2; else stack[top].d+=1; } }else top--; } returnFALSE;}坐标旋转变换//根据坐标和所在的方位,得到旋转后的坐标。POINTCCServerFrameView::rotate(POINToriginal,intdirect){ POINTn; switch(direct) { case0: n.x=16-original.y; n.y=original.x; break; case1: n.x=original.x; n.y=original.y; break; case2: n.x=original.y; n.y=16-original.x; break; case3: n.x=16-original.x; n.y=16-original.y; break; } returnn;}//根据坐标和所在的方位,得到逆旋转后的坐标。POINTCGameDlg::AdverseRotate(POINTp,intdir){ POINTpos; switch(dir) { case0: pos.x=p.y;pos.y=16-p.x; break; case2: pos.x=16-p.y;pos.y=p.x; break; case1: pos.x=p.x;pos.y=p.y; break; case3: pos.x=16-p.x;pos.y=16-p.y; break; } returnpos;}问题与展望34-第六章问题和展望6.1特色与成功实现了一个具有小型网络游戏特征的四国军旗游戏,有一个完整的框架,数据库、服务器、客户端、大厅、游戏框架、玩家信息、游戏中的聊天、广告信息、以及防止了闪烁的画面,游戏音乐。应用矩阵变换,实现不同方位之间的坐标变换。将游戏的仲裁权交给服务器,如在这个军棋游戏中,由服务器判断两个棋子的大小,客户端只识别已方每个棋子的类型,限制了客户端得到的数据流,一定程度上防止了作弊。6.2问题与展望将游戏的仲裁权交给服务器并不能完全防止作弊,而且也相应增加了服务器的负担。这对于大型网络游戏来说,是不可忍受的。即使是用现在最好的服务器,网络游戏,在服务器端,都不会介入逻辑判断,重点在数据的存储、保护和更新。而在客户端,它能获得全部逻辑判断的数据。也就是说,只要能破译数据流的格式,也就是破译前面所提到的游戏协议,就能随心所欲的操纵、更改、模拟数据,达到作弊的目的。这也是为什么在现在的网络游戏中,有那么多的作弊器、外挂的原因之一。目前做的最好的游戏之一,如魔兽世界,也都没能杜绝外挂的出现。而它采取了一种人为的方式──玩家监督。在目前来说,已经是最好的方法了。参考文献参考文献[1]朗锐.VisualC++数据库开发基础及实例解析.第一版.北京:机械工业出版社2005[2]苏羽王媛媛.VisualC++网络游戏建模与实现.第一版.北京:北京科海电子出版社2003[3]清华天则工作室.网络游戏从入门到精通.第一版.北京:内蒙古人民出版社
2002[4]荣钦科技.VisualC++游戏设计.第一版.北京:科海电子出版社
2003[5]史银雪,陈洪,王荣静.3D数学基础:图形与游戏开发.第一版.北京:清华大学出版社
2005[6]王小虎,靳自愚译.C语言计算机游戏程序设计.第一版.北京:科学出版社1995[7]http://www.M/china/msdn/[8]荣钦科技.游戏设计概论.第一版.北京:科海电子出版社
2003[9]潘志翔,岑进锋.黑客攻防编程解析.第一版.北京:机械工业出版社,2003
[10]贾斌.网络编程技巧与实例.第一版.北京:人民邮电出版社,2001绪论18-19-附录游戏大厅classCCGameHallFrameView:publicCformViewvoidCCGameHallFrameView::OnDblclkServertree(NMHDR*pNMHDR,LRESULT*pResult){ //TODO:Addyourcontrolnotificationhandlercodehere HTREEITEMitem; item=m_wndTree.GetSelectedItem(); CStrings; s=m_wndTree.GetItemText(item); if(s=="四国军棋") { if(m_Init==FALSE) { login=newCLogin(); if(login->DoModal()==IDOK) { name=login->m_name; code=login->m_code; ServerIP=login->m_ServerIP; if(name==""||code==""||ServerIP=="") MessageBox("每项内容都不能为空"); else r=1; deletelogin; }else { register1=newCRegister1(); if(register1->DoModal()) { if(register1->m_code1==register1->m_code2) { code=register1->m_code1; sex=register1->m_sex; ServerIP=register1->m_IP; if(code==""||sex==""||ServerIP=="") MessageBox("每项内容都不能为空"); else r=2; deleteregister1; } } } if(r==1||r==2) { if(ClientConnect()==TRUE) { //m_Init=TRUE; //pTable->flag=1; //pTable->DrawTable(); pTable->pClient=&m_client; if(r==1) { ClientMessage('N'); }elseif(r==2) { ClientMessage('L'); } } } } } *pResult=0;}voidCCGameHallFrameView::OnSize(UINTnType,intcx,intcy){ CFormView::OnSize(nType,cx,cy); //TODO:Addyourmessagehandlercodehere if(m_wndTree.m_hWnd){//FillthewholeClientAreaoftheViewwiththeControlm_wndTree.MoveWindow(0,0,cx,cy);}}LRESULTCCGameHallFrameView::OnClientMessage(WPARAMwParam,LPARAMlParam){ chars[1024],k; intlen;CStringstr; switch(lParam) { caseFD_CONNECT: len=GetLastError(); if(len!=0) { AfxMessageBox("ErrorinConnecting"); } else { m_Init=TRUE; } return0; caseFD_READ: len=recv(m_client.m_hSocket,s,1024,0); s[len]=NULL;str=s; k=protocol.Anaysys(str); dealMessage(k); return0; caseFD_WRITE: return0; caseFD_CLOSE: return0; default: MessageBox("服务器已关闭!"); return0; }}BOOLCCGameHallFrameView::ClientConnect(){ if(m_client.InitAndConnect(m_hWnd,PORT,ServerIP)==FALSE) { returnFALSE; } returnTRUE;}charCH=char(20);voidCCGameHallFrameView::ClientMessage(chark){ CStringstr; str=CH; str+=k; switch(k) { case'L': str+=code; if(sex=="女") str+="2"; else str+="1"; str+=CH; //MessageBox(str); Sleep(100); m_client.SendString(str); break; case'N': str+=name;str+='';str+=code; str+=CH; Sleep(100); m_client.SendString(str); break; }}voidCCGameHallFrameView::dealMessage(chark){ CStrings;inti; switch(k) { case'M': name=; s.Format("你的用户名是:%s",name); MessageBox(s); pTable->flag=1; pTable->DrawTable(); break; case'O': if(protocol.right==1) { for(inti=0;i<4;i++) for(intj=0;j<4;j++) { pTable->table[i][j].east=protocol.table[i][j].d[0]; pTable->table[i][j].south=protocol.table[i][j].d[1]; pTable->table[i][j].west=protocol.table[i][j].d[2]; pTable->table[i][j].north=protocol.table[i][j].d[3]; } pTable->flag=1; pTable->DrawTable(); } else { MessageBox("用户名或密码错误!"); } break; case'B': switch(protocol.direct) { case0: pTable->table[protocol.desk/4][protocol.desk%4].east=1; break; case1: pTable->table[protocol.desk/4][protocol.desk%4].south=1; break; case2: pTable->table[protocol.desk/4][protocol.desk%4].west=1; break; case3: pTable->table[protocol.desk/4][protocol.desk%4].north=1; break; default: break; } pTable->DrawTable(); break; case'P': switch(protocol.direct) { case0: pTable->table[protocol.desk/4][protocol.desk%4].east=0; break; case1: pTable->table[protocol.desk/4][protocol.desk%4].south=0; break; case2: pTable->table[protocol.desk/4][protocol.desk%4].west=0; break; case3: pTable->table[protocol.desk/4][protocol.desk%4].north=0; break; default: break; } pTable->dlg->user.ID[protocol.direct]=0; pTable->dlg->GameEnd(protocol.direct); pTable->DrawTable(); break; case'Q': for(i=0;i<4;i++) { pTable->dlg->user.ID[i]=protocol.user.ID[i]; pTable->dlg->[i]=[i]; pTable->dlg->user.score[i]=protocol.user.score[i]; pTable->dlg->user.sex[i]=protocol.user.sex[i]; } pTable->dlg->userMessage(); break; case'A': pTable->dlg->chatMessage(protocol.chat); break; case'D': //pTable->table[pTable->dlg->desk/4][pTable->dlg->desk%4].begin=TRUE; pTable->dlg->game.war_kind=protocol.war_kind; pTable->dlg->right=protocol.r; pTable->dlg->GameBegin(); break; case'F'://吃 pTable->dlg->right=protocol.r; pTable->dlg->PlayResult('F',protocol.from,protocol.to); break; case'G'://被吃 pTable->dlg->right=protocol.r; pTable->dlg->PlayResult('G',protocol.from,protocol.to); break; case'R'://碰 pTable->dlg->right=protocol.r; pTable->dlg->PlayResult('R',protocol.from,protocol.to); break; case'H'://炸 pTable->dlg->right=protocol.r; pTable->dlg->PlayResult('H',protocol.from,protocol.to); break; case'I': pTable->dlg->GameEnd(protocol.direct); break; }}游戏类classCGameDlg:publicCDialogvoidCGameDlg::DrawChess(CDC*dc){ /*CBitmapbitmap,right,left;intx,y,xr,xl,yr,yl; BITMAPBMap,br,bl;bitmap.LoadBitmap(IDB_SOUTHCHESS); right.LoadBitmap(IDB_RIGHTCHESS); left.LoadBitmap(IDB_LEFTCHESS); bitmap.GetBitmap(&BMap); left.GetBitmap(&bl); right.GetBitmap(&br); x=BMap.bmWidth; y=BMap.bmHeight; xr=br.bmWidth; yr=br.bmHeight; xl=bl.bmWidth; yl=bl.bmHeight;*/ CDCdcMem;intdir,x,y,pos; dcMem.CreateCompatibleDC(dc);//创建与pDC兼容的设备上下文 //dcMem.SelectObject(&left);//将位图对象选入设备上下文 //dc->SetStretchBltMode(HALFTONE);//设置图缩放模式 for(inti=0;i<17;i++) for(intj=0;j<17;j++) { if(board[i][j]>0&&board[i][j]<=18) { dir=1; if(i>=0&&i<6&&j>=6&&j<11) dir=3; if(i>=6&&i<11&&j>=0&&j<6) dir=2; if(i>=11&&i<17&&j>=6&&j<11) dir=1; if(i>=6&&i<11&&j>=11&&j<17) dir=0; if(board[i][j]<13) { switch(dir) { case0:dcMem.SelectObject(&bitmap_down[2]); x=BMap_down[2].bmWidth; y=BMap_down[2].bmHeight; break; case1: case3:dcMem.SelectObject(&bitmap_down[0]); x=BMap_down[0].bmWidth; y=BMap_down[0].bmHeight; break; case2:dcMem.SelectObject(&bitmap_down[1]); x=BMap_down[1].bmWidth; y=BMap_down[1].bmHeight; break; } } elseif(board[i][j]==13||board[i][j]==16) { switch(dir) { case0:dcMem.SelectObject(&bitmap_up[2]); x=BMap_up[2].bmWidth; y=BMap_up[2].bmHeight; break; case1: case3:dcMem.SelectObject(&bitmap_up[0]); x=BMap_up[0].bmWidth; y=BMap_up[0].bmHeight; break; case2:dcMem.SelectObject(&bitmap_up[1]); x=BMap_up[1].bmWidth; y=BMap_up[1].bmHeight; break; } } elseif(board[i][j]==14||board[i][j]==17) { switch(dir) { case0:dcMem.SelectObject(&bitmap_left[2]); x=BMap_left[2].bmWidth; y=BMap_left[2].bmHeight; break; case1: case3:dcMem.SelectObject(&bitmap_left[0]); x=BMap_left[0].bmWidth; y=BMap_left[0].bmHeight; break; case2:dcMem.SelectObject(&bitmap_left[1]); x=BMap_left[1].bmWidth; y=BMap_left[1].bmHeight; break; } } elseif(board[i][j]==15||board[i][j]==18) { switch(dir) { case0:dcMem.SelectObject(&bitmap_right[2]); x=BMap_right[2].bmWidth; y=BMap_right[2].bmHeight; break; case1: case3:dcMem.SelectObject(&bitmap_right[0]); x=BMap_right[0].bmWidth; y=BMap_right[0].bmHeight; break; case2:dcMem.SelectObject(&bitmap_right[1]); x=BMap_right[1].bmWidth; y=BMap_right[1].bmHeight; break; } } pos=FindImage(board[i][j],dir); switch(dir) { case0: dc->BitBlt(38+j*39,242+(i-6)*39,x/13,y,&dcMem,x/13*pos,0,SRCCOPY); break; case1: dc->BitBlt(265+(j-6)*40,13+i*39,x/13,y,&dcMem,x/13*pos,0,SRCCOPY); break; case2: dc->BitBlt(38+j*39,242+(i-6)*39,x/13,y,&dcMem,x/13*pos,0,SRCCOPY); break; case3: dc->BitBlt(265+(j-6)*40,13+i*39,x/13,y,&dcMem,x/13*pos,0,SRCCOPY); break; default:break; } } } dcMem.DeleteDC();}intCGameDlg::FindImage(intk,intdir){ intpos; switch(k) { case1: pos=8; break; case2: pos=7; break; case3: pos=6; break; case4: pos=5; break; case5: pos=4; break; case6: pos=3; break; case7: pos=2; break; case8: pos=1; break; case9: pos=10; break; case10: pos=12; break; case11: pos=11; break; case12: case16: case17: case18: pos=9; break; case13: case14: case15: pos=0; break; default:break; } returnpos;}CPointCGameDlg::FindPos(intx,inty){ CPointpos; pos.x=-1;pos.y=-1; inti,j; j=(x-38)/39; i=(y-13)/39; if(((i>5&&i<11)||(j>5&&j<11))&&board[i][j]>=0) { pos.x=i;pos.y=j; } returnpos;}voidCGameDlg::DrawArrow(inti,intj,intk,CDC*dc){ intx,y; x=j*39+30;y=i*39+10; CDCdcMem; dcMem.CreateCompatibleDC(dc);//创建与pDC兼容的设备上下文 if(k<8) { dcMem.SelectObject(&bitmap_arrow); dc->BitBlt(x,y,BMap_arrow.bmWidth/9,BMap_arrow.bmHeight,&dcMem,BMap_arrow.bmWidth/9*k,0,MERGEPAINT); } elseif(k==8) { dcMem.SelectObject(&bitmap_k1); dc->BitBlt(x,y,BMap_k1.bmWidth,BMap_k1.bmHeight,&dcMem,0,0,MERGEPAINT); } elseif(k==9) { x+=5;y-=3; dcMem.SelectObject(&bitmap_k2); dc->BitBlt(x,y,BMap_k2.bmWidth,BMap_k2.bmHeight,&dcMem,0,0,MERGEPAINT); } dcMem.DeleteDC();}voidCGameDlg::userMessage(){ inti,j; for(i=6;i<11;i++) for(j=11;j<17;j++) if(user.ID[rotateDirect(0)]==1&&!(i==7&&j==12)&&!(i==7&&j==14)&& !(i==8&&j==13)&&!(i==9&&j==12)&&!(i==9&&j==14)) board[i][j]=15; else board[i][j]=0; for(i=6;i<11;i++) for(j=0;j<6;j++) if(user.ID[rotateDirect(2)]==1&&!(i==7&&j==2)&&!(i==7&&j==4)&& !(i==8&&j==3)&&!(i==9&&j==2)&&!(i==9&&j==4)) board[i][j]=14; else board[i][j]=0; for(i=0;i<6;i++) for(j=6;j<11;j++) if(user.ID[rotateDirect(3)]==1&&!(i==2&&j==7)&&!(i==2&&j==9)&& !(i==3&&j==8)&&!(i==4&&j==7)&&!(i==4&&j==9)) board[i][j]=13; else board[i][j]=0; for(i=6;i<11;i++) for(j=6;j<11;j++) { if(board[i][j]>0) board[i][j]=0; } m_
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论