基于VC++的四国军棋局域网联网游戏的设计与实现

Leftme

发布日期: 2018-10-01 22:39:07 浏览量: 479
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

摘 要

本文详细介绍了一个在Windows环境下的基于游戏大厅框架的四国军棋网络游戏的设计和实现。该游戏可在局域网上联机对战,实现了游戏、聊天、积分等功能。该软件在Windows环境下用VC++6.0实现。

关键词:VC++ , 网络协议,游戏协议,套接字,军棋。

Abstract

This article introduces the design and realization of a network game named junqi in Chinese,which runs under the game hall architecture. The game can run on LAN with the functions playing game, chatting, score. And the program is made in the Windows and Visual C++6.0 environment.

Key words: VC++ ; Network Protocol; Game Protocol; Socket; junqi

第一章 绪论

1.1 问题概述

1.1.1 问题的来源

随着Internet的普及,网络游戏已经成为大家耳濡目染的新生事物。网络游戏从出现到现在的发展的时间很短,但是其发展速度却非常之快。现在,可以说网络游戏已经成为人们休闲、娱乐的有效方式。国内比较有名气的网络游戏有联众网络游戏世界(http://www.ourgame.com)、qq游戏中心(http://www.tencent.com)、中国游戏中心(http://www.chinagames.net)以及这几年受玩家亲睐的传奇、魔兽世界等。

1.1.2 目的和意义

对当今网络游戏的设计、架构进行分析、探索和实践。提供友好的客户操作界面,通过客户端与服务器之间的网络传递数据,实现了多人协同游戏的目的。分析现今网络游戏体系结构及设计模式的优缺点,及对网络游戏的发展给予分析和展望。

1.1.3 国内外研究现状

现今网络游戏的体系结构(见图1-1),包括客户机程序、服务器程序、数据库服务器。

1.2 问题剖析

1.2.1 主要问题

在开发网络游戏时,首先要建立底层的网络通信类,利用网络通信类连接构建客户服务器之间的TCP/IP连接,然后在该连接的基础上利用自设定的协议进行客户端登录、进行游戏等操作。在以上协议的基础上,根据不同的游戏编写不同的游戏逻辑处理类,在该逻辑处理类中实现了对应的游戏逻辑,如实例中的军棋,则实现相互之间的对弈等功能。同时在服务器端还需要和数据库服务器交互,用于读取或保存客户信息(如用户积分、密码、个人资料等数据)。

1.2.2 难点和关键

  • 有一个或多个游戏服务器启动特定游戏服务。
  • 游戏者到游戏网站上下载客户端程序并且申请游戏账号ID。然后启动客户端程序通过某种网络协议连接游戏服务器。
  • 客户端程序负责处理客户端显示和操作界面,具有简单的逻辑处理功能,同时负责接收发送与服务器端交互的数据包。
  • 服务器程序负责处理服务器端逻辑、游戏逻辑、客户之间的网络信息传递,以及数据库之间的数据读取保存工作。同时服务器端还要承担客户端数据的接收、转发工作。

1.2.3 思路和方法

网络游戏通常的运行方式(见图1-2)。

第二章 相关的知识和工具

2.1 解决问题的知识基础

网络游戏常用的网络协议有适用于Internet的TCP/IP协议、适用局域网(比如星际)的IPX协议。

1.TCP/IP协议

TCP/IP协议(Transmission Control Protocol/InternetProtocol,传输控制协议/网际协议)是Internet中计算机进行通信的标准,其命名起源于该组协议中最重要的两个协议TCP和IP。任何关于Internet协议的讨论必须由TCP/IP开始,它也是其他所有协议的基础。TCP/IP协议是Internet网络的共同语言,主机之间必须利用TCP/IP互通信息。

TCP/IP协议目前已经成为发展最成功的通信协议之一,它起源于20世纪60年代末美国政府资助的一个分级交换网络研究项目,允许分布在各地的使用不同操作系统的网络进行通信。随着世界范围个人电脑的普及,日常无论收发邮件、访问网页和文件传输都已经离不开TCP/IP协议,TCP/IP协议已经成为Internet的基础。

2.TCP/IP结构

TCP/IP实际上就是在物理网上的一组完整的协议。其核心部分是传输层协议(TCP/UDP)、网络层(IP)和物理接口层,这三层通常在操作系统内核中实现。TCP/UDP层提供了传输层服务,而IP协议提供了网络层服务。

TCP/IP协议是一个四层协议,其结构如图2-1所示。

应用程序与TCP/IP可靠传输之间接口具有五大特性:

  • 面向数据流 

    当两个用户进程传输大量数据时,我们把这些数据当做可划分为八位组(octer,字节)的比特流,在目的机器上运行的数据流投递服务软件提给接收方的八位组与信源机上发送方送出来的完全相同。

  • 虚电路连接 

    数据流的传输与电话相似,使用“虚电路”这个术语来描述这种连接是因为在应用程序看来这种连接像是一条专用的硬件电路,这种可靠连接的错觉是由数据流投递服务提供。

  • 有缓冲的传送 

    使用虚电路服务来发送数据流的应用程序不断向协议软件递交数据八位组。为了提高效率以及减少网络延迟,协议软件在实现时都会从数据流中收集到足够多的数据,组成大小合理的数据包后再送到网络上传输。

  • 无结构的数据 

    TCP/IP协议并不区分结构化的数据流。使用数据流的应用程序必须在开始连接之前就了解数据流的内容并对其格式进行协商。这点很重要,在程序中表现为send函数只能发送字符串,这就需要将接收的字符串转化需要的结构化的数据。

  • 全双工连接 

    TCP/IP流服务所提供的功能是双向的全双工连接。其中包括了两个独立、流向相反的数据流,而这两个数据流之间不进行显式的交互。

常用协议主要包括TCP/UDP层协议和IP层协议。TCP和UDP都是传输层协议,都使用IP协议作为网络层协议。使用UDP协议的应用程序必须承担可靠性的工作,包括报文的丢失、重复、乱序以及连接失效等问题,而程序员编程时则容易疏忽。

2.2 开发平台

操作系统Windows 7,开发工具Visual C++ 6.0。

2.3 数据库

SQLServer 2000,是一个全面的数据库平台,引擎为关系型数据和结构化数据提供了安全可靠的存储功能。

第三章 总体设计

3.1 总体设计的框架

军棋游戏的总体设计框架,客户端如图3-1所示。

军棋游戏的总体设计框架,服务器如图3-2所示。

3.2 模块功能概述

客户端类的划分:

  • 游戏基本类:负责处理游戏中一些完成基本功能的类,如处理声音类、处理动画图标,图形按钮等类,基本类的特点是被其他类在特定处所调用,并不动生成对象。
  • 游戏框架类:负责处理游戏中客户端用于显示程序界面和绘制游戏界面以及显示用户信息和广告信息等处理任务。
  • 游戏通信类:负责处理游戏中客户服务器之间的网络传输细节,从而在编程中不用考虑网络通信细节,达到客户和服务器之间的透明的效果。
  • 游戏应用程序类:主要负责处理应用程序中各种设置显示对话框、程序主线程处理、程序中基本的运行类框架的管理,以及游戏中图形的处理和显示等任务的处理。
  • 游戏处理类:主要用于处理游戏简单逻辑、负责解析和处理与服务器端交互的游戏数据,以及在游戏运行中维护游戏中的各种数据,同时维护处理游戏主线程逻辑等功能。

服务器类的划分:

  • 游戏通信类:负责处理游戏中客户服务器之间的网络传输细节,从而在编程中不用考虑网络通信细节,达到客户和服务器之间透明传输的效果。
  • 游戏协议类:负责处理游戏中客户服务器之间交互所传递的数据,并且对该数据格式进行打包和解包,同时根据该包中所包含的指令串进行相应的操作。
  • 游戏逻辑类:负责处理游戏逻辑,如军棋游戏中用于维护军棋逻辑,判断下棋,得分等处理类。
  • 用户管理类:用于管理用户资料,在用户登录后通过数据库验证用户名和密码,通过验证后从数据库读取用户的详细资料。同时在程序中维护用户数据,在用户游戏结束和退出游戏时将用户数据保存到数据库中。
  • 服务器框架类:用于管理游戏大厅的数据,包括一些数据的列表。
  • 数据库类:用于网络游戏的服务器端在处理大量的客户资料时,使用数据库进行大量数据的存储和查询所调用的类方法。

3.3 关键算法

模块间的数据传递设计,如图3-3所示。

这样,在CTableView和CGameDlg中通过指针,也可向服务器发送消息。而CCGameHallFrameView收到消息后,同时也控制CTableView和CGameDlg的行为。

采用内存作图的方式,消除了闪烁。首先创建一个内存dc,将绘图的工作先在内存dc中做好,再贴到实际dc上去。做法如下:

首先创建关于屏幕的内存DC,代码为MemDC.CreateCompatibleDC( pDC); 之后创建一幅关于屏幕DC的图画,部分代码如下:

  1. CRect rect;
  2. this->GetClientRect(rect);
  3. CBitmap bmpFace;
  4. bmpFace.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

注意把握rect的尺寸为客户区域大小; 之后将这幅画选入内存DC中,部分代码如下:

  1. CBitmap* pOldBmp = NULL;
  2. pOldBmp = MemDC.SelectObject(&bmpFace);

之后可以开始在内存DC中进行任何绘制动作;部分代码如下:

  1. CBrush brush(RGB(255, 255, 255));
  2. MemDC.FillRect(rect, &brush);
  3. for(int i=0; i<500; i++)
  4. {
  5. MemDC.MoveTo(22+i, 22);
  6. MemDC.LineTo(22+i, 277);
  7. }

绘制完后将内存DC中的这幅图绘制到屏幕DC中来,部分代码如下:

  1. pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &MemDC, rect.left, rect.top, SRCCOPY);

最后进行相关的资源回收动作,部分代码如下:

  1. MemDC.SelectObject(pOldBmp);
  2. bmpFace.DeleteObject();

同时我们要把系统的ON_WM_ERASEBKGND消息函数重载为return FALSE,否则还是会出现闪烁情况。

在对话框中用这个方法的时候,要注意将有控件的部分和需要绘图的部分分开,可采用如下方法,部分代码如下:

  1. CRect rectClient;CRgn rgn1,rgn2; //rgn3,rgn4;
  2. GetClientRect(rectClient);
  3. rgn1.CreateRectRgnIndirect(rectClient);
  4. rgn2.CreateRectRgn(730,0,962,670);
  5. if(rgn1.CombineRgn(&rgn1,&rgn2,RGN_XOR) == ERROR)
  6. {
  7. return;
  8. }
  9. MemDC.FillRgn(&rgn1,&brush);

服务器对连接的用户数据的数据结构的设计,用下面两个结构:

  1. typedef struct TT{
  2. int d[4];
  3. int ID[4];
  4. int board[17][17];
  5. BOOL begin[4];
  6. };
  7. typedef struct MM{
  8. CString name,sex;
  9. int score;
  10. };

连接的socket保存在如下链表中:

  1. typedef CList <SOCKET,SOCKET&> SOCKET_ARRAY;
  2. SOCKET_ARRAY m_connectionList;

与服务器建立连接后,相应的用户数据,保存在MM结构数组中,位置为其socket在m_connectionList中的相应位置,这样就可以通过连接的socket找到相应的信息。TT是保存大厅数据的结构,d表示四个坐位有没有人,ID表示就坐的人的socket在链表中的位置。board为棋盘数据。每个桌子有每个桌子的棋盘数据,互不干扰。随着游戏的进行而不断更新。begin表示四个玩家有没有下调度完成的指令。

应用矩阵的变换实现坐标旋转。不管你坐在哪个方位,玩游戏时,你始终是在正下方,这就需要实现虚拟坐标到目标坐标的旋转变换,如图3-4。

X为实际坐标,x为虚拟坐标,转换公式推导如下,见图3-5。

  1. // direct 为自己座位方向
  2. switch(direct)
  3. case 0:(a=90度) [XY1]=[-y+16x1] //东
  4. case 1:(a=0度) [XY1]=[xy1] //南
  5. case 2:(a=-90度) [XY1]=[y,-x+161] //西
  6. case 3:(a=180度) [XY1]=[-x+16,-y+161] //北

回溯法求工兵路径,军棋游戏逻辑不复杂,只有工兵能够自由飞行。在这里没有求工兵起点到目标点的最短路径,而只是用回溯法求出其中一条路径,所以在游戏中你会发现工兵有时候会绕些弯路。这和老鼠走迷宫差不多的。

工兵迷宫数组:

  1. int RAILWAY[17][17]={
  2. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  3. {0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0},
  4. {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0},
  5. {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0},
  6. {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0},
  7. {0,0,0,0,0,1,1,3,1,3,1,1,0,0,0,0,0},
  8. {0,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1,0},
  9. {0,1,0,0,0,2,2,0,2,0,2,2,0,0,0,1,0},
  10. {0,1,0,0,0,1,1,3,1,3,1,1,0,0,0,1,0},
  11. {0,1,0,0,0,2,2,0,2,0,2,2,0,0,0,1,0},
  12. {0,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1,0},
  13. {0,0,0,0,0,1,1,3,1,3,1,1,0,0,0,0,0},
  14. {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0},
  15. {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0},
  16. {0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0},
  17. {0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0},
  18. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  19. };

0表示该位置不在工兵铁道上,1表示在该位置可向上下左右方向移动,2表示在该位置只能向上下方向移动,3表示在该位置只能向左右方向移动。

3.4 关键技术

网络通信,Windows Socket 编程接口:

Windows Socket (简称 WinSock)是在Win32平台上访问基层网络协议的接口。在不同的Win32平台上,Windows Socket以不同的方式存在着,作为网络编程接口而不是协议存在。

套接字(Socket)概念,套接字是从英文单词Socket翻译而来,它是网络通信的基本操作单元,是应用层到传输层的接口,可以将套接字看作不同主机间的进程进行双向通信的端点。

Windows Socket组成部分,Windwos Sockets实现一般都由两部分组成:开发组件和运行组件。开发组件是供程序员开发Windows Sockets应用程序使用的,主要是WinSock.h头文件。对于Windows Sockets应用程序的源文件来说,只要包括WinSock.h就可以了。除此之外,在使用WinSock的项目中还需要加入WinSock API引入库wsock32.lib。运行组件是Windows Sockets应用程序接口的动态连接库(DLL),文件名为WinSock.dll,应用程序在执行时通过装入它实现网络通信功能。

Windows Sockets编程的基本模式,要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户端,我们称之为Client Socket,另一个运行于服务器端,我们称之为Server Socket。使用Socket进行网络通信一般有两种方式:基于面向连接的流方式和基于无连接的数据报方式。面向连接的的流方式调用过程如图3-6所示。

第四章 详细设计

4.1 数据库结构

服务器端数据库结构如表4-1,玩家信息表,用作记录玩家游戏数据。

列名 数据类型 长度 允许空 描述
主键 name char 10 no 用户名
code char 10 no 密码
score int 4 yes 游戏积分
sex char 2 yes 性别

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。

CMainFrame类是游戏窗口中的游戏大厅框架类,其中包括构架广告显示窗口,可玩的游戏类、工具栏、游戏桌的信息、显示信息等,CCGameHallFrameView类是用来在游戏大厅窗口左侧显示可以玩的游戏以及游戏室,采用树状结构显示。CHtmlViewEx类用于构造广告显示窗口,用于游戏框架中显示广告页。CTableView显示包含游戏桌的游戏大厅,在大厅中多人在等待开始游戏。模块描述如图4-2。

CClient游戏通信类,功能为建立和服务器的连接,能及处理通信,采用异步机制,以自定义消息事件处理通信等功能。采用自定义消息:WM_USER + 101。模块描述如图4-3。

CGameDlg游戏应用程序类,其中包括对游戏处理类的调用。CGameDlg 主要负责处理应用程序中各种设置显示对话框、程序主线程处理、以及游戏中的图形的处理和显示等任务的处理。模块描述如图4-4。

TakeGame游戏处理类,主要用于处理游戏简单逻辑、负责解析和处理与服务器端交互的游戏数据,以及在游戏运行时维护游戏中的各种数据,同时维护处理游戏主线程逻辑等功能。模块描述如图4-5。

服务器模块:

CServer游戏通信类,负责处理游戏中客户与服务器之间的网络连接细节,从而使得对于客户和服务器之间的数据传输可以忽略通信细节。模块描述如图4-6。

CServerProtocol游戏协议类,游戏协议类负责解析客户/服务器端所传输的协议,同时根据不同的协议调用不同的操作函数,并根据用户状态变化维护用户信息。模块描述如图4-7。

CServerLogic游戏逻辑类,负责处理游戏逻辑,在军棋游戏中包括计算得分,计算赢等。游戏逻辑类和游戏协议类分离的优点是可以只通过修改逻辑类来改变为不同的网络游戏,如修改为“象棋逻辑”即可以成为象棋游戏,修改为“升级逻辑”即可以成为升级游戏。

用户管理类,直接在CCServerFrameView中实现。

数据库类,采用SQL Server 2000数据库,只是用来保存玩家的资料等一些数据。采用MFCODBC数据库编程,在程序中为CServerFrameSet类。正如MFC提供的其他类库很好地对相应的Win32 API作了封装,MFC提供的ODBC类库也相应地对ODBCAPI作了封装,通过提供一种高级接口而避免直接使用ODBC API所涉及的种种繁琐处理,简化了对ODBC数据库的应用程序编程。模块描述如图4-8。

第五章 程序编码

5.1 数据结构

用17×17的数组表示军棋的棋盘,如下:

  1. int BOARD[17][17] = {
  2. {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1},
  3. {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1},
  4. {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1},
  5. {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1},
  6. {-1,-1,-1,-1,-1,-1,0,0,0,0,0,-1,-1,-1,-1,-1,-1},
  7. {-1,-1,-1,-1,-1,0,0,0,0,0,0,0,-1,-1,-1,-1,-1},
  8. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  9. {0,0,0,0,0,0,0,-1,0,-1,0,0,0,0,0,0,0},
  10. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  11. {0,0,0,0,0,0,0,-1,0,-1,0,0,0,0,0,0,0},
  12. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  13. {-1,-1,-1,-1,-1,0,7,3,4,5,3,0,-1,-1,-1,-1,-1},
  14. {-1,-1,-1,-1,-1,-1,10,0,1,0,9,-1,-1,-1,-1,-1,-1},
  15. {-1,-1,-1,-1,-1,-1,8,10,0,1,5,-1,-1,-1,-1,-1,-1},
  16. {-1,-1,-1,-1,-1,-1,6,0,4,0,3,-1,-1,-1,-1,-1,-1},
  17. {-1,-1,-1,-1,-1,-1,2,7,6,1,11,-1,-1,-1,-1,-1,-1},
  18. {-1,-1,-1,-1,-1,-1,11,12,11,2,2,-1,-1,-1,-1,-1,-1},
  19. };

其中,为了处理工兵的走法的方便,将四个转角斜对的位置恒置为0,这样在为工兵寻找路径时会方便一些。数字的含义如下所示:

  1. -1 表示棋盘以外的位置
  2. 0 表示棋盘内位置,但无棋子
  3. 1 工兵
  4. 2 排长
  5. 3 连长
  6. 4 营长
  7. 5 团长
  8. 6 旅长
  9. 7 师长
  10. 8 军长
  11. 9 司令
  12. 10 炸弹
  13. 11 地雷
  14. 12 我方军旗
  15. 13 上方盟友或敌方的棋子
  16. 14 左边敌方的棋子
  17. 15 右边敌方的棋子
  18. 16 上方军旗
  19. 17 左方军旗
  20. 18 右方军旗

游戏协议

以char(20)作为一次信息的起始点,终点。“+”在字符串中用空格代替。以一字符串作为向服务发出的请求信息,服务器也以相应的一字符串发送回客户端作为应答。

  1. 对话:
  2. A+桌子号+聊天内容
  3. 坐下:
  4. B+桌子号+方位
  5. 调度完成:
  6. C+桌子号+方位+(棋盘数据)[用一维数据形式表示6×5的二维数组,数据之间用空格开
  7. 开始游戏:(只由服务器发出)
  8. D+对战类型
  9. 下棋:
  10. E +桌子号+方位+起点(x,y)+终点(x,y)
  11. 吃:(只由服务器发出)(包括移动)
  12. F +起点(x,y) + 终点(x,y)
  13. 被吃:(只由服务器发出)
  14. G +起点(x,y) + 终点(x,y)
  15. 炸:(只由服务器发出)
  16. H +起点(x,y) + 终点(x,y)
  17. 输:离开:(包括断线)投降:
  18. I +方位
  19. 求和:
  20. K +桌子号+方位
  21. 注册:
  22. L +用户名+密码
  23. 注册成功:
  24. M + 用户名
  25. 登录:
  26. N + 用户名+密码
  27. 登录结果:
  28. O + 1/0
  29. 离开:
  30. P + 桌子号 方位
  31. 请求接收信息:
  32. Q + 桌子号 方位
  33. 碰:
  34. R 起点(x,y) + 终点(x,y)

5.2 主要界面

服务器界面如图5-1所示。

客户端大厅界面如图5-2所示。

军棋游戏界面,如图5-3所示:

5.3 重要模块程序实现

客户端三拆分窗口的实现

  1. BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
  2. {
  3. if(NULL==m_wndSplitter.CreateStatic(this,1,2))
  4. return FALSE;
  5. if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CTableView),CSize(620,800),pContext))
  6. return FALSE;
  7. if(NULL==m_wndLeftSplitter.CreateStatic(&m_wndSplitter,2,1,
  8. WS_CHILD|WS_VISIBLE,m_wndSplitter.IdFromRowCol(0,1)))
  9. return FALSE;
  10. 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))
  11. return FALSE;
  12. pWebView=(CHtmlViewEx*)m_wndLeftSplitter.GetPane(1,0);
  13. pServerTreeView=(CCGameHallFrameView*)m_wndLeftSplitter.GetPane(0,0)
  14. pTableView=(CTableView*)m_wndSplitter.GetPane(0,0);
  15. pServerTreeView->pTable=pTableView;
  16. pWebView->Navigate2("http://free2.e-168.cn/zhou207",0,NULL);
  17. return TRUE;
  18. }

客户端初始化及连接

  1. void CClient::ClientInit()
  2. {
  3. if(WSAAsyncSelect(m_hSocket,m_hWnd,CLI_MESSAGE,FD_READ|FD_WRITE|FD_CLOSE|FD_CONNECT)>0)
  4. {
  5. AfxMessageBox("Error in select");
  6. }
  7. }
  8. BOOL CClient::InitAndConnect(HWND hWnd, UINT port, CString strServer)
  9. {
  10. m_hWnd=hWnd;
  11. m_uPort=port;
  12. m_strServer=strServer;
  13. if(m_hSocket!=NULL)
  14. {
  15. //先将以前打开的套接字关闭
  16. closesocket(m_hSocket);
  17. m_hSocket=NULL;
  18. }
  19. //创建面向连接的socket
  20. m_hSocket=socket(AF_INET,SOCK_STREAM,0);
  21. ASSERT(m_hSocket!=NULL);
  22. ClientInit();
  23. //设置连接信息:网络协议+IP地址+端口
  24. m_addr.sin_family=AF_INET;
  25. m_addr.sin_addr.S_un.S_addr=inet_addr(m_strServer.GetBuffer(0));
  26. m_addr.sin_port=htons(m_uPort);
  27. //连接服务器
  28. int ret=0;
  29. int error=0;
  30. ret=connect(m_hSocket,(LPSOCKADDR)&m_addr,sizeof(m_addr));
  31. if(ret==SOCKET_ERROR)
  32. {
  33. //连接失败
  34. if(GetLastError()!=WSAEWOULDBLOCK)
  35. {
  36. AfxMessageBox(_T("请确认服务器确实已经打开并工作在同样的端口!"));
  37. return FALSE;
  38. }
  39. }
  40. return TRUE;
  41. }

服务器初始化及连接

  1. void CServer::ServerInit()
  2. {
  3. //设置socket的异步模式
  4. if(WSAAsyncSelect(m_hSocket,m_hWnd,SER_MESSAGE,FD_ACCEPT|FD_READ|FD_WRITE|FD_CLOSE)>0)
  5. AfxMessageBox("error select");
  6. }
  7. BOOL CServer::InitAndListen(HWND hWnd, UINT port)
  8. {
  9. m_uPort=port;
  10. m_hWnd=hWnd;
  11. if(m_hSocket!=NULL)
  12. {
  13. //先关闭已经打开的socket
  14. closesocket(m_hSocket);
  15. m_hSocket=NULL;
  16. }
  17. //创建面向连接的流方式的套接字
  18. m_hSocket=socket(AF_INET,SOCK_STREAM,0);
  19. ASSERT(m_hSocket!=NULL);
  20. ServerInit();
  21. m_addr.sin_family=AF_INET;
  22. m_addr.sin_addr.S_un.S_addr=INADDR_ANY;
  23. m_addr.sin_port=htons(m_uPort);
  24. int ret=0;
  25. int error=0;
  26. //绑定套接字到本机的某个端口
  27. ret=bind(m_hSocket,(LPSOCKADDR)&m_addr,sizeof(m_addr));
  28. if(ret == SOCKET_ERROR)
  29. {
  30. AfxMessageBox("Binding Error");
  31. return FALSE;
  32. }
  33. ret = listen(m_hSocket, 64);
  34. if(ret == SOCKET_ERROR)
  35. {
  36. AfxMessageBox("Listen Error");
  37. return FALSE;
  38. }
  39. return TRUE;
  40. }

回溯法寻找工兵路径

  1. BOOL TakeGame::TakeEngineer(CPoint from, CPoint to)
  2. {
  3. POS p;int x,y;engineer=TRUE;
  4. p.d=0;p.x=from.x;p.y=from.y;top=0;
  5. if(RAILWAY[p.x][p.y]==2)
  6. p.d=1;
  7. stack[top]=p;int f[17][17];
  8. while(top>=0)
  9. {
  10. p=stack[top];
  11. if(p.x==to.x&&p.y==to.y)
  12. return TRUE;
  13. if(p.d<4)
  14. {
  15. x=p.x+d[p.d][0];y=p.y+d[p.d][1];
  16. if(x==to.x&&p.y==to.y)
  17. return TRUE;
  18. if(board[x][y]==0&&RAILWAY[x][y]!=0&&f[x][y]!=1)
  19. {
  20. if(RAILWAY[p.x][p.y]==2||RAILWAY[p.x][p.y]==3)
  21. stack[top].d+=2;
  22. else
  23. stack[top].d+=1;
  24. if(RAILWAY[x][y]==2)
  25. {
  26. f[stack[top].x][stack[top].y]=1;
  27. p.x=x;p.y=y;p.d=1;
  28. stack[++top]=p;
  29. }else
  30. {
  31. f[stack[top].x][stack[top].y]=1;
  32. p.x=x;p.y=y;p.d=0;
  33. stack[++top]=p;
  34. }
  35. }
  36. else
  37. {
  38. if(RAILWAY[p.x][p.y]==2||RAILWAY[p.x][p.y]==3)
  39. stack[top].d+=2;
  40. else
  41. stack[top].d+=1;
  42. }
  43. }else
  44. top--;
  45. }
  46. return FALSE;
  47. }

坐标旋转变换

  1. //根据坐标和所在的方位,得到旋转后的坐标。
  2. POINT CCServerFrameView::rotate(POINT original,int direct)
  3. {
  4. POINT n;
  5. switch(direct)
  6. {
  7. case 0:
  8. n.x=16-original.y;
  9. n.y=original.x;
  10. break;
  11. case 1:
  12. n.x=original.x;
  13. n.y=original.y;
  14. break;
  15. case 2:
  16. n.x=original.y;
  17. n.y=16-original.x;
  18. break;
  19. case 3:
  20. n.x=16-original.x;
  21. n.y=16-original.y;
  22. break;
  23. }
  24. return n;
  25. }
  26. //根据坐标和所在的方位,得到逆旋转后的坐标。
  27. POINT CGameDlg::AdverseRotate(POINT p, int dir)
  28. {
  29. POINT pos;
  30. switch(dir)
  31. {
  32. case 0:
  33. pos.x=p.y;pos.y=16-p.x;
  34. break;
  35. case 2:
  36. pos.x=16-p.y;pos.y=p.x;
  37. break;
  38. case 1:
  39. pos.x=p.x;pos.y=p.y;
  40. break;
  41. case 3:
  42. pos.x=16-p.x;pos.y=16-p.y;
  43. break;
  44. }
  45. return pos;
  46. }

第六章 问题和展望

6.1 特色与成功

实现了一个具有小型网络游戏特征的四国军旗游戏,有一个完整的框架,数据库、服务器、客户端、大厅、游戏框架、玩家信息、游戏中的聊天、广告信息、以及防止了闪烁的画面,游戏音乐。

应用矩阵变换,实现不同方位之间的坐标变换。

将游戏的仲裁权交给服务器,如在这个军棋游戏中,由服务器判断两个棋子的大小,客户端只识别已方每个棋子的类型,限制了客户端得到的数据流,一定程度上防止了作弊。

6.2 问题与展望

将游戏的仲裁权交给服务器并不能完全防止作弊,而且也相应增加了服务器的负担。这对于大型网络游戏来说,是不可忍受的。即使是用现在最好的服务器,网络游戏,在服务器端,都不会介入逻辑判断,重点在数据的存储、保护和更新。而在客户端,它能获得全部逻辑判断的数据。

也就是说,只要能破译数据流的格式,也就是破译前面所提到的游戏协议,就能随心所欲的操纵、更改、模拟数据,达到作弊的目的。这也是为什么在现在的网络游戏中,有那么多的作弊器、外挂的原因之一。

目前做的最好的游戏之一,如魔兽世界,也都没能杜绝外挂的出现。而它采取了一种人为的方式——玩家监督。在目前来说,已经是最好的方法了。

参考文献

[1] 朗锐. Visual C++数据库开发基础及实例解析. 第一版. 北京: 机械工业出版社, 2005

[2] 苏羽, 王媛媛. Visual C++网络游戏建模与实现. 第一版. 北京: 北京科海电子出版社, 2003

[3] 清华天则工作室. 网络游戏从入门到精通. 第一版. 北京: 内蒙古人民出版社, 2002

[4] 荣钦科技. Visual C++游戏设计. 第一版. 北京: 科海电子出版社, 2003

[5] 史银雪, 陈洪, 王荣静. 3D数学基础: 图形与游戏开发. 第一版. 北京: 清华大学出版社, 2005

[6] 王小虎, 靳自愚译. C 语言计算机游戏程序设计. 第一版. 北京: 科学出版社, 1995

[7] http://www.Microsoft.com/china/msdn/

[8] 荣钦科技. 游戏设计概论. 第一版. 北京: 科海电子出版社, 2003

[9] 潘志翔, 岑进锋. 黑客攻防编程解析. 第一版. 北京: 机械工业出版社, 2003

[10] 贾斌. 网络编程技巧与实例. 第一版. 北京: 人民邮电出版社, 2001

上传的附件 cloud_download 四国军棋联网游戏.7z ( 1.69mb, 11次下载 )
error_outline 下载需要8点积分

keyboard_arrow_left上一篇 : 基于JSP SSH框架的客车网上售票系统的设计与实现 Dash、Pandas实现数据可视化 : 下一篇keyboard_arrow_right



Leftme
2018-10-01 22:40:35
使用VC++ 6.0实现了支持局域网下联网的的四国军棋游戏,除了支持游戏之外,还支持聊天、积分等功能
会对全
2019-06-10 11:06:16
感觉不错.

发送私信

告别错的,方可遇见对的

16
文章数
17
评论数
最近文章
eject