网络游戏实例vc++网络五子棋.docx
- 文档编号:5287807
- 上传时间:2022-12-14
- 格式:DOCX
- 页数:27
- 大小:121.54KB
网络游戏实例vc++网络五子棋.docx
《网络游戏实例vc++网络五子棋.docx》由会员分享,可在线阅读,更多相关《网络游戏实例vc++网络五子棋.docx(27页珍藏版)》请在冰豆网上搜索。
网络游戏实例vc++网络五子棋
小型网络游戏实例——网络五子棋
前面介绍了Socket网络开发基础,下面通过一个网络五子棋的例子来介绍小型网络游戏的制作方
法和流程。
1.1主程序结构和流程
对于一般的小型网络游戏设计来说,可以分成如下的几个模块。
_服务器模块:
负责建立游戏服务器并接受客户端的连接和处理客户端的数据。
_客户端模块:
负责连接到服务器并处理和服务器的会话。
_游戏规则模块:
负责游戏的控制和结果的判断,使游戏按规则进行。
_视图控制模块:
负责控制整个程序的操作,负责处理用户的输入和游戏的界面绘制。
本节的网络五子棋游戏就是按照以上的设计思路来安排的。
第4章网络游戏开发207
1.2实例演示
实例运行时的界面如图4.7所示。
实现的主要功能有:
双人对下五子棋,聊天。
208VisualC++游戏开发技术与实例
图4.7网络五子棋的运行界面
1.3实例设计
利用Win32AppWizard建立一个单文档工程,工程名称为FiveChess。
工程主要包括以下的一些
文件。
其中窗体/视图/文档文件包括如下文件。
_MainFrm.h/MainFrm.cpp:
主窗体框架。
_FiveChessView.h/FiveChessView.cpp:
棋盘界面的显示,鼠标消息的处理和发送网络数据(聊
天和下棋)。
_FiveChessDoc.h/FiveChessDoc.cpp:
文档部分。
网络控制部分包括如下文件。
_ServerSocket.h/ServerSocket.cpp:
服务器端用来监听的socket,它继承自CSocket。
它负责响应
客户端连接消息。
_ClientSocket.h/ClientSocket.cpp:
用来会话的socket,它也继承于CSocket。
它负责响应Receive
的消息。
服务器端和客户端都用这个socket发送和接受数据。
_CMessg.h/CMessg.cpp:
发送和接受的数据的定义。
它采用串行化的方式对网络数据进行处理。
五子棋规则部分包括如下文件。
_Match.h/Match.cpp:
Match类中保存着当前的棋盘,它还负责判断是否能够在指定的位置落子,
是否出现输赢局面。
1.4代码详细分析
下面将分析网络五子棋游戏中的网络控制、五子棋规则和视图控制部分的相关代码。
_网络控制部分:
CServerSocket、CClientSocket和CMessg。
第4章网络游戏开发209
CServerSocket类是一个专门用来监听客户端连接的Socket类,它继承于CSocket,响应OnAccept
消息。
CServerSocket类声明如下:
classCServerSocket:
publicCSocket
{
public:
CFiveChessView*m_view;//主视图的指针,为接受连接函数服务
UINTm_uPort;//需要监听的端口
BOOLInit(UINTport,CFiveChessView*dlg);//socket的初始化
public:
virtualvoidOnAccept(intnErrorCode);//接受连接消息处理函数
};
在ServerSocket.cpp中的核心代码如下,其中成员函数Init负责初始化ServerSocket,而OnAccept
函数负责接收客户端连接。
BOOLCServerSocket:
:
Init(UINTport,CFiveChessView*view)
{
m_uPort=port;
m_view=view;
//创建socket
if(Create(m_uPort)==FALSE)
{
AfxMessageBox("ServerSocketCreateError");
returnFALSE;
}
//监听socket
if(Listen()==FALSE)
{
AfxMessageBox("ServerListenError");
returnFALSE;
}
returnTRUE;
}
voidCServerSocket:
:
OnAccept(intnErrorCode)//接受客户端连接
{
CSocket:
:
OnAccept(nErrorCode);
//调用视图类中的连接处理函数
m_view->ProcessPendingAccept();
}
CClientSocket类是一个专门用做会话的类,它负责数据的发送和接收。
它继承自CSocket,负责
响应OnReceive和OnClose消息。
另外,作为CSocket类的继承类,理所当然的采用了串行化的方式
来处理网络数据。
网络数据是由CMessg的对象构成的。
另外,在CClientSocket类中还定义了几个静态函数,它们用来得到本机的名字和本机的IP地址。
CClientSocket类声明如下:
classCClientSocket:
publicCSocket
{
public:
210VisualC++游戏开发技术与实例
CArchive*m_aSessionIn;//输入数据的串行化
CArchive*m_aSessionOut;//输出数据的串行化
CSocketFile*m_sfSocketFile;//Socket关联文件指针
CFiveChessView*m_view;//视图指针
boolm_bInit;//是否初始化
boolm_bClose;//连接是否已关闭
public:
voidInit(CFiveChessView*view);//socket和串行化相关的初始化
BOOLSendMessage(CMessg*msg);//发送消息的函数
voidCloseSocket();//关闭socket函数
public:
//几个静态函数,用来得到主机名和主机IP地址
staticintGetLocalHostName(CString&sHostName);//获得本地计算机名称
staticintGetIpAddress(constCString&sHostName,CString&sIpAddress);
//获得本地IP
staticintGetIpAddress(constCString&sHostName,BYTE&f0,BYTE&f1,BYTE&f2,BYTE
&f3);//获得本地IP
//这个静态函数根据GetLastError()返回的tag找到网络失败的原因
staticCStringErrorReason(inttag);
public:
virtualvoidOnReceive(intnErrorCode);//数据接受消息的处理函数
virtualvoidOnClose(intnErrorCode);//socket关闭的消息处理函数
};
ClientSocket.cpp中的核心代码如下(构造函数负责部分初始化工作):
CClientSocket:
:
CClientSocket()
{
m_aSessionIn=NULL;
m_aSessionOut=NULL;
m_sfSocketFile=NULL;
m_bInit=false;
m_bClose=false;
}
析构函数负责清除内存。
CClientSocket:
:
~CClientSocket()
{
if(m_aSessionIn)
deletem_aSessionIn;
if(m_aSessionOut)
deletem_aSessionOut;
if(m_sfSocketFile)
deletem_sfSocketFile;
}
OnReceive函数负责接收对方发送过来的数据,并对数据进行相应的处理。
这是一个消息响应
函数:
voidCClientSocket:
:
OnReceive(intnErrorCode)
{
CSocket:
:
OnReceive(nErrorCode);
do
{
第4章网络游戏开发211
CMessgtemp;
temp.Serialize(*m_aSessionIn);//输入数据的串行化
m_view->m_sMsgList+=temp.m_strText;//加入新的对话内容
//把新的对话内容显示在主视图窗口的一个Edit控件中
m_view->m_outputedit.SetWindowText(m_view->m_sMsgList);
//把Edit窗口中显示的内容滚动到当前的位置
m_view->m_iLineCurrentPos=m_view->m_outputedit.GetLineCount();
m_view->m_outputedit.LineScroll(m_view->m_iLineCurrentPos);
//判断对方发送过来的数据是否是落子的位置
if(m_view->m_match.CanDown(temp.m_x,temp.m_y,m_view->m_who%2+1))
{
//是对方发送的落子信息
m_view->m_turn=temp.m_turn;//该轮到我走棋了
m_view->Invalidate(FALSE);//刷新视图,显示对方的走子位置
//对方是否赢了
if(m_view->m_match.IsWin(m_view->m_who%2+1,m_view->m_winpos))
{
m_view->m_bWin=TRUE;//对方赢了
m_view->m_bOver=TRUE;
m_view->Invalidate(FALSE);
AfxMessageBox("你输了");//显示输棋的对话框
m_view->m_sMsgList+="你输了";
m_view->m_outputedit.SetWindowText(m_view->m_sMsgList);
m_view->m_iLineCurrentPos=m_view->m_outputedit.GetLineCount();
m_view->m_outputedit.LineScroll(m_view->m_iLineCurrentPos);
}
}
}
while(!
m_aSessionIn->IsBufferEmpty());
}
Init函数负责初始化串行化socket:
voidCClientSocket:
:
Init(CFiveChessView*view)
{
m_sfSocketFile=newCSocketFile(this);
m_aSessionIn=newCArchive(m_sfSocketFile,CArchive:
:
load);
m_aSessionOut=newCArchive(m_sfSocketFile,CArchive:
:
store);
m_bClose=false;
this->m_view=view;
}
SendMessage函数负责串行化数据到对方socket:
BOOLCClientSocket:
:
SendMessage(CMessg*msg)
{
if(m_aSessionOut!
=NULL)
{
msg->Serialize(*m_aSessionOut);//输出串行化
m_aSessionOut->Flush();//直接发送
returnTRUE;
}
212VisualC++游戏开发技术与实例
else
{
//对方关闭了连接
m_bClose=true;
CloseSocket();
returnFALSE;
}
}
关闭Socket、清除串行化数据的工作由函数CloseSocket完成:
voidCClientSocket:
:
CloseSocket()
{
if(m_aSessionIn)
{
deletem_aSessionIn;
m_aSessionIn=NULL;
}
if(m_aSessionOut)
{
deletem_aSessionOut;
m_aSessionOut=NULL;
}
if(m_sfSocketFile)
{
deletem_aSessionOut;
m_sfSocketFile=NULL;
}
Close();
m_bInit=false;
m_bClose=true;
}
对方关闭了Socket的消息响应处理函数OnClose:
voidCClientSocket:
:
OnClose(intnErrorCode)
{
m_bClose=true;
CloseSocket();
CSocket:
:
OnClose(nErrorCode);
}
GetLocalHostName函数负责获得本地计算机名称:
intCClientSocket:
:
GetLocalHostName(CString&sHostName)
{
charszHostName[256];
intnRetCode;
nRetCode=gethostname(szHostName,sizeof(szHostName));
if(nRetCode!
=0)
{
//产生错误
sHostName=_T("没有取得");
returnGetLastError();
第4章网络游戏开发213
}
sHostName=szHostName;
return0;
}
GetIpAddress函数负责获得本地IP地址:
intCClientSocket:
:
GetIpAddress(constCString&sHostName,CString&sIpAddress)
{
structhostentFAR*lpHostEnt=gethostbyname(sHostName);
if(lpHostEnt==NULL)
{
//产生错误
sIpAddress=_T("");
returnGetLastError();
}
//获取IP
LPSTRlpAddr=lpHostEnt->h_addr_list[0];
if(lpAddr)
{
structin_addrinAddr;
memmove(&inAddr,lpAddr,4);
//转换为标准格式
sIpAddress=inet_ntoa(inAddr);
if(sIpAddress.IsEmpty())
sIpAddress=_T("没有取得");
}
return0;
}
GetIpAddress的一个重载:
intCClientSocket:
:
GetIpAddress(constCString&sHostName,BYTE&f0,BYTE&f1,BYTE
&f2,BYTE&f3)
{
structhostentFAR*lpHostEnt=gethostbyname(sHostName);
if(lpHostEnt==NULL)
{
//产生错误
f0=f1=f2=f3=0;
returnGetLastError();
}
//获取IP
LPSTRlpAddr=lpHostEnt->h_addr_list[0];
if(lpAddr)
{
structin_addrinAddr;
memmove(&inAddr,lpAddr,4);
f0=inAddr.S_un.S_un_b.s_b1;
f1=inAddr.S_un.S_un_b.s_b2;
f2=inAddr.S_un.S_un_b.s_b3;
f3=inAddr.S_un.S_un_b.s_b4;
}
return0;
214VisualC++游戏开发技术与实例
}
CMessg类是一个数据类,它继承于CObject。
在这个程序中,让CMessg类发挥串行化数据的作
用。
CMessg类中包括了几个主要的成员变量:
CStringm_strText;
intm_turn;
intm_x;
intm_y;
其中m_strText表示聊天的字符串;m_turn表示轮到谁走棋;m_x表示落子的水平方向的位置;
m_y表示落子的竖直方向的位置。
这4个数据构成了网络传输数据的全部信息。
事实上,在发送具体的CMessg数据的时候并不需要用到所有这4个成员变量。
例如,用户只是
聊天的时候,只需要填充m_strText就可以了,m_turn、m_x和m_y会被初始填充为–1,表示没有子
落下。
而当用户落子的时候,m_strText会被初始填充成NULL,m_turn、m_x和m_y会被填充成具体
的落子位置和轮换信息。
CMessg.h中的核心代码如下:
classCMessg:
publicCObject
{
protected:
DECLARE_DYNCREATE(CMessg)
public:
CStringm_strText;//聊天的内容
intm_turn;//轮到谁落子
intm_x;//落子的水平位置
intm_y;//落子的竖直位置
public:
voidInit();//初始化
public:
virtualvoidSerialize(CArchive&ar);//串行化数据类
};
CMessg.cpp中的核心代码如下:
IMPLEMENT_DYNCREATE(CMessg,CObject)
voidCMessg:
:
Init()//初始化
{
m_strText=_T("");
m_x=-1;
m_y=-1;
m_turn=-1;
}
voidCMessg:
:
Serialize(CArchive&ar)//数据串行化
{
if(ar.IsStoring())
{
ar< ar< ar< ar< } else 第4章网络游戏开发215 { ar>>m_strText; ar>>m_turn; ar>>m_x; ar>>m_y; } } _Match类(五子棋规则) 正如前面所说的,每一种游戏都有一定的规则,Match类正是五子棋的规则处理类。 在Match类中保存着一个当前的棋盘,它是一个二维的数组intchessboard[LW][LW]。 如果 chessboard[x][y]为0,则表示(x,y)的位置上没有棋子;如果chessboard[x][y]为1表示该位置上有黑子; 如果chessboard[x][y]为2,则表示该位置上有白子。 Match类还负责判断是否能在某个位置落子,是否有人赢棋。 Cmatch类声明如下: #defineLW19//棋盘的水平竖直方向的格数 classMatch { public: Match(); virtual~Match(); public: intchessboard[LW][LW];//0表示没有子落下;1表示黑子落下;2表示白子落下 public: BOOLCanDown(intx,inty,intwho);//是否能在x,y的位置落子 BOOLIsWin(intwho,intpos[5][2]);//判断是否赢棋 voidClear();//清除棋盘内容 }; Match.cpp中的核心代码如下(在构造函数里初始化棋盘): Match: : Match() { for(inti=0;i for(intj=0;j { chessboard[i][j]=0; } } Clear函数负责清除棋盘内容: voidMatch: : Clear() { for(inti=0;i for(intj=0;j { chessboard[i][j]=0; } } CanDown函数负责判断在点(x,y)位置是否能落子: 216VisualC++游戏开发技术与实例 BOOLMatch: : CanDown(intx,inty,intwho) { if(x<0||x>=LW||y<0||y>=LW) returnFALSE; if(chessboard[x][y]! =0) returnFALSE; chessboard[x][y]=who; returnTRUE; } 判断玩家是否赢棋,如果赢棋则标注五子连珠的位置。 BOOLMatch: : IsWin(intwho,intpos[5][2]) { inti,j; for(i=0;i for(j=0;j { if(chessboard[i][j]==who) { //水平方向 if(j+4 { if(chessboard[i][j+1]==who&&chessboard[i][j+2]==who&&chessboard[i][j+3]==who&&chess board[i][j+4]==who) {//赢棋 //标注赢棋的关键子的位置 for(intk=0;k<5;k++) { pos[k][0]=i; pos[k][1]=j+k; } returnTRUE; } } //垂直 if(i+4 { if(chessboard[i+1][j]==who&&chessboard[i+2][j]==who&&chessboard[i+3][j]==who&&chess board[i+4][j]==who) {//赢棋 //标注赢棋的关键子的位置 for(intk=0;k<5;k++) { pos[k][0]=i+k; pos[k][1]=j; } returnTRUE; } } if(i+4 { 第4章网络游戏开发217 if(chessboard[i+1][j+1]==who&&chessboard[i+2][j+2]==who&&chessboard[i+3][j+3]==who& &chessboard[i+4][j+4]==who) {//赢棋 for(intk=0;k<5;k++)//标注赢棋的关键子的位置 { pos[k][0]=i+k; pos[k][1]=j+k; } returnTRUE; } } //东北 if(i-4>0&&j+4 { if(chessboard[i-1][j+1]==who&&chessboard[i-2][j+2]==who&&chessboard[i-
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 网络游戏 实例 vc 网络 五子棋
![提示](https://static.bdocx.com/images/bang_tan.gif)