小型网络游戏实例(vc++)——网络五子棋

来源:互联网 发布:2345下载软件 编辑:程序博客网 时间:2024/04/29 20:34

转载请注明出处

本文章的源代码下载地址

       前面介绍了Socket网络开发基础,下面通过一个网络五子棋的例子来介绍小型网络游戏的制作方

法和流程。

 

 

 

1.1 主程序结构和流程

 

对于一般的小型网络游戏设计来说,可以分成如下的几个模块。

_ 服务器模块:负责建立游戏服务器并接受客户端的连接和处理客户端的数据。

_ 客户端模块:负责连接到服务器并处理和服务器的会话。

_ 游戏规则模块:负责游戏的控制和结果的判断,使游戏按规则进行。

_ 视图控制模块:负责控制整个程序的操作,负责处理用户的输入和游戏的界面绘制。

本节的网络五子棋游戏就是按照以上的设计思路来安排的。

第4 章网络游戏开发207

1.2 实例演示

 

实例运行时的界面如图4.7 所示。实现的主要功能有:双人对下五子棋,聊天。

208 Visual C++游戏开发技术与实例

4.7 网络五子棋的运行界面

1.3 实例设计

 

利用Win32 AppWizard 建立一个单文档工程,工程名称为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.cppMatch类中保存着当前的棋盘,它还负责判断是否能够在指定的位置落子,

是否出现输赢局面。



1.4 代码详细分析

 

下面将分析网络五子棋游戏中的网络控制、五子棋规则和视图控制部分的相关代码。

_ 网络控制部分:CServerSocketCClientSocketCMessg

第4 章网络游戏开发209

CServerSocket 类是一个专门用来监听客户端连接的Socket类,它继承于CSocket,响应OnAccept

消息。CServerSocket 类声明如下:

classCServerSocket : public CSocket{public:CFiveChessView* m_view; //主视图的指针,为接受连接函数服务UINTm_uPort; //需要监听的端口BOOLInit(UINT port, CFiveChessView* dlg); //socket 的初始化public:virtualvoid OnAccept(int nErrorCode); //接受连接消息处理函数};

ServerSocket.cpp 中的核心代码如下,其中成员函数Init 负责初始化ServerSocket,而OnAccept

函数负责接收客户端连接。

BOOLCServerSocket::Init(UINT port, CFiveChessView* view){m_uPort=port;m_view=view;//创建socketif(Create(m_uPort)==FALSE){AfxMessageBox("ServerSocket Create Error");returnFALSE;}//监听socketif(Listen()==FALSE){AfxMessageBox("ServerListen Error");returnFALSE;}returnTRUE;}voidCServerSocket::OnAccept(int nErrorCode) //接受客户端连接{CSocket::OnAccept(nErrorCode);//调用视图类中的连接处理函数m_view->ProcessPendingAccept();}

CClientSocket 类是一个专门用做会话的类,它负责数据的发送和接收。它继承自CSocket,负责

响应OnReceive OnClose消息。另外,作为CSocket类的继承类,理所当然的采用了串行化的方式

来处理网络数据。网络数据是由CMessg 的对象构成的。

另外,在CClientSocket 类中还定义了几个静态函数,它们用来得到本机的名字和本机的IP 地址。

CClientSocket 类声明如下:

class CClientSocket : public CSocket{public:210 Visual C++游戏开发技术与实例CArchive* m_aSessionIn; //输入数据的串行化CArchive* m_aSessionOut; //输出数据的串行化CSocketFile* m_sfSocketFile; //Socket 关联文件指针CFiveChessView * m_view; //视图指针bool m_bInit; //是否初始化bool m_bClose; //连接是否已关闭public:void Init(CFiveChessView * view); //socket 和串行化相关的初始化BOOL SendMessage(CMessg * msg); //发送消息的函数void CloseSocket(); //关闭socket 函数public: //几个静态函数,用来得到主机名和主机IP 地址static int GetLocalHostName(CString&sHostName); //获得本地计算机名称static int GetIpAddress(const CString&sHostName, CString &sIpAddress);//获得本地IPstatic int GetIpAddress(const CString&sHostName, BYTE &f0,BYTE &f1,BYTE &f2,BYTE&f3);//获得本地IP//这个静态函数根据GetLastError()返回的tag 找到网络失败的原因static CString ErrorReason(int tag);public:virtual void OnReceive(int nErrorCode); //数据接受消息的处理函数virtual void OnClose(int nErrorCode); //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)delete m_aSessionIn;if(m_aSessionOut)delete m_aSessionOut;if(m_sfSocketFile)delete m_sfSocketFile;}

OnReceive 函数负责接收对方发送过来的数据,并对数据进行相应的处理。这是一个消息响应

函数:

void CClientSocket::OnReceive(int nErrorCode){CSocket::OnReceive(nErrorCode);do{CMessg temp;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

void CClientSocket::Init(CFiveChessView * view){m_sfSocketFile= new CSocketFile(this);m_aSessionIn=new CArchive(m_sfSocketFile,CArchive::load);m_aSessionOut=newCArchive(m_sfSocketFile,CArchive::store);m_bClose=false;this->m_view=view;}

SendMessage 函数负责串行化数据到对方socket

BOOL CClientSocket::SendMessage(CMessg * msg)

{

if (m_aSessionOut != NULL)

{

msg->Serialize(*m_aSessionOut); //输出串行化

m_aSessionOut->Flush(); //直接发送

return TRUE;

}

else

{

//对方关闭了连接

m_bClose=true;

CloseSocket();

return FALSE;

}

}

关闭Socket、清除串行化数据的工作由函数CloseSocket完成:

void CClientSocket::CloseSocket(){if(m_aSessionIn){delete m_aSessionIn;m_aSessionIn=NULL;}if(m_aSessionOut){delete m_aSessionOut;m_aSessionOut=NULL;}if(m_sfSocketFile){delete m_aSessionOut;m_sfSocketFile=NULL;}Close();m_bInit=false;m_bClose=true;}

对方关闭了Socket 的消息响应处理函数OnClose

void CClientSocket::OnClose(int nErrorCode){m_bClose=true;CloseSocket();CSocket::OnClose(nErrorCode);}

GetLocalHostName 函数负责获得本地计算机名称:

int CClientSocket::GetLocalHostName(CString&sHostName){char szHostName[256];int nRetCode;nRetCode=gethostname(szHostName,sizeof(szHostName));if(nRetCode!=0){//产生错误sHostName=_T("没有取得");return GetLastError();}sHostName=szHostName;return 0;}

GetIpAddress 函数负责获得本地IP地址:

int CClientSocket::GetIpAddress(const CString&sHostName, CString &sIpAddress)

{

struct hostent FAR * lpHostEnt=gethostbyname(sHostName);

if(lpHostEnt==NULL)

{

//产生错误

sIpAddress=_T("");

return GetLastError();

}

//获取IP

LPSTR lpAddr=lpHostEnt->h_addr_list[0];

if(lpAddr)

{

struct in_addr inAddr;

memmove(&inAddr,lpAddr,4);

//转换为标准格式

sIpAddress=inet_ntoa(inAddr);

if(sIpAddress.IsEmpty())

sIpAddress=_T("没有取得");

}

return 0;

}

GetIpAddress 的一个重载:

int CClientSocket::GetIpAddress(const CString&sHostName, BYTE &f0,BYTE &f1,BYTE

&f2,BYTE &f3)

{

struct hostent FAR *lpHostEnt=gethostbyname(sHostName);

if(lpHostEnt==NULL)

{

//产生错误

f0=f1=f2=f3=0;

return GetLastError();

}

//获取IP

LPSTR lpAddr=lpHostEnt->h_addr_list[0];

if(lpAddr)

{

struct in_addr inAddr;

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;

}

return 0;

214 Visual C++游戏开发技术与实例

}

CMessg 类是一个数据类,它继承于CObject。在这个程序中,让CMessg类发挥串行化数据的作

用。CMessg 类中包括了几个主要的成员变量:

CString m_strText;

int m_turn;

int m_x;

int m_y;

其中m_strText 表示聊天的字符串;m_turn表示轮到谁走棋;m_x表示落子的水平方向的位置;

m_y 表示落子的竖直方向的位置。这4个数据构成了网络传输数据的全部信息。

事实上,在发送具体的CMessg 数据的时候并不需要用到所有这4 个成员变量。例如,用户只是

聊天的时候,只需要填充m_strText 就可以了,m_turnm_xm_y会被初始填充为–1,表示没有子

落下。而当用户落子的时候,m_strText 会被初始填充成NULLm_turnm_xm_y会被填充成具体

的落子位置和轮换信息。

CMessg.h 中的核心代码如下:

class CMessg : public CObject

{

protected:

DECLARE_DYNCREATE(CMessg)

public:

CString m_strText; //聊天的内容

int m_turn; //轮到谁落子

int m_x; //落子的水平位置

int m_y; //落子的竖直位置

public:

void Init(); //初始化

public:

virtual void Serialize(CArchive& ar); //串行化数据类

};

CMessg.cpp 中的核心代码如下:

IMPLEMENT_DYNCREATE(CMessg, CObject)

void CMessg::Init() //初始化

{

m_strText = _T("");

m_x=-1;

m_y=-1;

m_turn=-1;

}

void CMessg::Serialize(CArchive& ar) //数据串行化

{

if (ar.IsStoring())

{

ar << m_strText;

ar << m_turn;

ar << m_x;

ar << m_y;

}

else

第4 章网络游戏开发215

{

ar >> m_strText;

ar >> m_turn;

ar >> m_x;

ar >> m_y;

}

}

_ Match 类(五子棋规则)

正如前面所说的,每一种游戏都有一定的规则,Match类正是五子棋的规则处理类。

Match 类中保存着一个当前的棋盘,它是一个二维的数组int chessboard[LW][LW]。如果

chessboard[x][y]0,则表示(x,y)的位置上没有棋子;如果chessboard[x][y]1表示该位置上有黑子;

如果chessboard[x][y]2,则表示该位置上有白子。

Match 类还负责判断是否能在某个位置落子,是否有人赢棋。

Cmatch 类声明如下:

#define LW 19 //棋盘的水平竖直方向的格数

class Match

{

public:

Match();

virtual ~Match();

public:

int chessboard[LW][LW]; //0 表示没有子落下;1 表示黑子落下;2 表示白子落下

public:

BOOL CanDown(int x,int y,int who); //是否能在x,y 的位置落子

BOOL IsWin(int who,int pos[5][2]); //判断是否赢棋

void Clear(); //清除棋盘内容

};

Match.cpp 中的核心代码如下(在构造函数里初始化棋盘):

Match::Match()

{

for(int i=0;i<LW;i++)

for(int j=0;j<LW;j++)

{

chessboard[i][j]=0;

}

}

Clear 函数负责清除棋盘内容:

void Match::Clear()

{

for(int i=0;i<LW;i++)

for(int j=0;j<LW;j++)

{

chessboard[i][j]=0;

}

}

CanDown 函数负责判断在点(x,y)位置是否能落子:

216 Visual C++游戏开发技术与实例

BOOL Match::CanDown(int x,int y,int who)

{

if(x<0||x>=LW||y<0||y>=LW)

return FALSE;

if(chessboard[x][y]!=0)

return FALSE;

chessboard[x][y]=who;

return TRUE;

}

判断玩家是否赢棋,如果赢棋则标注五子连珠的位置。

BOOL Match::IsWin(int who,int pos[5][2])

{

int i,j;

for(i=0;i<LW;i++)

for(j=0;j<LW;j++)

{

if(chessboard[i][j]==who)

{

//水平方向

if(j+4<LW)

{

if(chessboard[i][j+1]==who&&chessboard[i][j+2]==who&&chessboard[i][j+3]==who&&chess

board[i][j+4]==who)

{ //赢棋

//标注赢棋的关键子的位置

for(int k=0;k<5;k++)

{

pos[k][0]=i;

pos[k][1]=j+k;

}

return TRUE;

}

}

//垂直

if(i+4<LW)

{

if(chessboard[i+1][j]==who&&chessboard[i+2][j]==who&&chessboard[i+3][j]==who&&chess

board[i+4][j]==who)

{ //赢棋

//标注赢棋的关键子的位置

for(int k=0;k<5;k++)

{

pos[k][0]=i+k;

pos[k][1]=j;

}

return TRUE;

}

}

if(i+4<LW&&j+4<LW)//东南向

{

第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(int k=0;k<5;k++)//标注赢棋的关键子的位置

{

pos[k][0]=i+k;

pos[k][1]=j+k;

}

return TRUE;

}

}

//东北

if(i-4>0&&j+4<LW)

{

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(int k=0;k<5;k++)//标注赢棋的关键子的位置

{

pos[k][0]=i-k;

pos[k][1]=j+k;

}

return TRUE;

}

}

}

}

return false;

}

_ 主视图FiveChessView

FiveChessView 是程序的主视图类。它负责在主窗口中绘制棋盘和棋子,显示聊天的内容,负责对

鼠标消息进行处理等。

FiveChessView.h 中的核心代码如下:

#include "ServerSocket.h"

#include "ClientSocket.h"

#include "Match.h"

class CFiveChessDoc;

class CFiveChessView : public CView

{

public:

CString m_sMsgList; //记录聊天的内容

CServerSocket m_ListenSocket; //做服务器时的监听socket

CClientSocket m_ClientSocket; //会话socket

BOOL m_bIsClient; //是否是客户端

BOOL m_bIsInit; //是否已经初始化

int m_port; //socket 端口号

CString m_ip; //本机的IP 地址,字符串

BYTE m_bIP[4]; //本机的IP 地址,字节

218 Visual C++游戏开发技术与实例

CString m_sLocalName; //本机的名字

CEdit m_inputedit; //聊天的输入框

CEdit m_outputedit; //聊天的显示框

int m_iLineCurrentPos; //当前的显示框中的行数

public:

Match m_match; //五子棋的规则类

int m_who; //黑棋或者白棋,黑棋先行

int m_turn; //轮谁走棋

int m_POS[2]; //落子的位置

int m_winpos[5][2]; //如果赢棋,则记录关键子的位置

BOOL m_bWin; //是否赢棋

BOOL m_bOver; //结束

public:

void ProcessPendingAccept(); //作为服务器端时,接受socket 连接的处理函数

void GetLocalIP(); //得到本机的IP 地址

public:

virtual void OnDraw(CDC* pDC); //绘制棋盘和棋子

virtual void OnInitialUpdate(); //初始化处理

protected:

afx_msg void OnLButtonDown(UINT nFlags, CPointpoint);

//鼠标左键按下的消息处理函数

afx_msg void INPUTMESSAGE (); //聊天输入框的消息处理

afx_msg void OnSetclient(); //菜单项"连接服务器"的消息处理

afx_msg void OnSetserver(); //菜单项"开启服务器"的消息处理

};

FiveChessView.cpp 中的核心代码如下(构造函数负责部分初始化工作):

CFiveChessView::CFiveChessView()

{

m_bIsClient=FALSE;

m_bIsInit=FALSE;

m_bOver=FALSE;

m_bWin=FALSE;

m_turn= 1; //服务器先走,黑棋

}

OnInitialUpdate 函数负责主要的初始化工作:

void CFiveChessView::OnInitialUpdate()

{

CView::OnInitialUpdate();

static bool oninitialupdatehaving=false; //只需要初始化调用一次

if(oninitialupdatehaving==false)

{

if(::AfxSocketInit()==FALSE) //winsock 初始化

{

AfxMessageBox("socket init error");

}

GetLocalIP(); //得到本机的IP 地址

CRect rect;

this->GetWindowRect(rect);

第4 章网络游戏开发219

//创建两个动态的CEdit 控件,作为输入对话框和显示对话框。

m_outputedit.Create(ES_MULTILINE|WS_CHILD |WS_VISIBLE | WS_TABSTOP |

WS_BORDER|ES_READONLY,CRect(10,rect.bottom-200,rect.right-10,rect.bottom-140), this,1);

m_inputedit.Create(ES_MULTILINE|WS_CHILD |WS_VISIBLE | WS_TABSTOP |

WS_BORDER|ES_AUTOVSCROLL,CRect(10,rect.bottom-140,rect.right-10,rect.bottom-100),this,2);

}

}

OnDraw 函数负责绘制视图:

void CFiveChessView::OnDraw(CDC* pDC)

{

CFiveChessDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

CBrush back(RGB(125,125,20)); //绘制棋盘的刷子

pDC->SelectObject((CBrush*)&back);

CRect rect;

rect.left=LEFTDIS;rect.right=LEFTDIS+(LW-1)*30;

rect.top=TOPDIS;rect.bottom=TOPDIS+(LW-1)*30;

pDC->FillRect(&rect,&back); //绘制棋盘

CPen pen(PS_SOLID,2,RGB(25,25,255)); //绘制网格线的笔

pDC->SelectObject((CPen*)&pen);

int i,j;

CPoint pos;

for(i=0;i<LW;i++) //竖直的线

{

pos.x=LEFTDIS+30*i;

pos.y=TOPDIS;

pDC->MoveTo(pos);

pos.y=TOPDIS+(LW-1)*30;

pDC->LineTo(pos);

}

for(i=0;i<LW;i++) //水平的线

{

pos.x=LEFTDIS;

pos.y=TOPDIS+30*i;

pDC->MoveTo(pos);

pos.x=LEFTDIS+(LW-1)*30;

pDC->LineTo(pos);

}

CBrush whitebrush(RGB(255,255,255)); //绘制黑棋的刷子

CBrush blackbrush(RGB(0,0,0)); //绘制白棋的刷子

for(i=0;i<LW;i++) //扫描整个棋盘

for(j=0;j<LW;j++)

{

if(m_match.chessboard[i][j]==1) //黑棋

{

pDC->SelectObject((CBrush*)&blackbrush);

pDC->Ellipse(j*30+LEFTDIS-15,i*30+TOPDIS-15,j*30+LEFTDIS+15,i*30+TOPDIS+15);

}

220 Visual C++游戏开发技术与实例

else

if(m_match.chessboard[i][j]==2) //白棋

{

pDC->SelectObject((CBrush*)&whitebrush);

pDC->Ellipse(j*30+LEFTDIS-15,i*30+TOPDIS-15,j*30+LEFTDIS+15,i*30+TOPDIS+15);

}

}

//绘制4 个星位和一个天元

CBrush starbrush(RGB(0,0,0));

pDC->SelectObject((CBrush*)&starbrush);

j=4;i=4;

if(m_match.chessboard[i][j]==0)

pDC->Ellipse(j*30+LEFTDIS-8,i*30+TOPDIS-8,j*30+LEFTDIS+8,i*30+TOPDIS+8);

j=4;i=14;

if(m_match.chessboard[i][j]==0)

pDC->Ellipse(j*30+LEFTDIS-8,i*30+TOPDIS-8,j*30+LEFTDIS+8,i*30+TOPDIS+8);

j=9;i=9;

if(m_match.chessboard[i][j]==0)

pDC->Ellipse(j*30+LEFTDIS-8,i*30+TOPDIS-8,j*30+LEFTDIS+8,i*30+TOPDIS+8);

j=14;i=4;

if(m_match.chessboard[i][j]==0)

pDC->Ellipse(j*30+LEFTDIS-8,i*30+TOPDIS-8,j*30+LEFTDIS+8,i*30+TOPDIS+8);

j=14;i=14;

if(m_match.chessboard[i][j]==0)

pDC->Ellipse(j*30+LEFTDIS-8,i*30+TOPDIS-8,j*30+LEFTDIS+8,i*30+TOPDIS+8);

//比赛已经结束,或赢或输

if(m_bOver==TRUE)

{

CBrush redbrush(RGB(255,0,0)); //用红色的刷子显示关键的5 个棋子

pDC->SelectObject((CBrush*)&redbrush);

for(int k=0;k<5;k++)

{

i=m_winpos[k][0];

j=m_winpos[k][1];

pDC->Ellipse(j*30+LEFTDIS-15,i*30+TOPDIS-15,j*30+LEFTDIS+15,i*30+TOPDIS+15);

}

}

}

菜单项“开启服务器”的消息处理函数如下:

void CFiveChessView::OnSetserver()

{

CServerDlg dlg;

第4 章网络游戏开发221

if(dlg.DoModal()==IDOK) //服务器端口设置对话框

{

m_bIsClient=FALSE; //作为游戏的服务器端

m_bIsInit=TRUE; //已经初始化

m_port=dlg.m_iPort; //得到输入的端口号

m_ListenSocket.Init(m_port,this); //开始监听端口

}

}

菜单项“连接服务器”的消息处理函数如下:

void CFiveChessView::OnSetclient()

{

CClientDlg dlg;

int ret=dlg.DoModal(); //设置服务器的IP 和端口

if(ret==2000) //用户点击了"确定"

{

m_bIsClient=TRUE; //作为游戏的客户端

m_bIsInit=TRUE; //已经初始化

m_port=dlg.m_iPort; //得到用户输入的服务器端的端口

for(int i=0;i<4;i++)

m_bIP[i]=dlg.m_bIP[i]; //得到用户输入的服务器端的IP 地址

m_ip.Format("%d.%d.%d.%d",dlg.m_bIP[0],dlg.m_bIP[1],dlg.m_bIP[2],

dlg.m_bIP[3]);

m_ClientSocket.Create(); //创建会话socket

if(m_ClientSocket.Connect(LPCSTR(m_ip),m_port))//连接到服务器端

{

m_ClientSocket.Init(this); //初始化会话socket

m_who=2; //客户端用白子。

AfxMessageBox("成功的连接到了服务器,可以开始游戏了");

}

else

{

m_ClientSocket.Close(); //连接失败

AfxMessageBox("client connectionfailed");

}

}

}

ProcessPendingAccept 是处理客户端socket 连接的函数:

void CFiveChessView::ProcessPendingAccept()

{

if(m_ListenSocket.Accept(m_ClientSocket)==FALSE)//分配一个会话socket

{

AfxMessageBox("Server Listen SocketError");

return;

}

else

{

m_who=1; //服务器端用黑棋,黑棋先行

m_ClientSocket.Init(this); //初始化会话socket

m_outputedit.SetWindowText("有人进来了");

AfxMessageBox("有人进来了");

222 Visual C++游戏开发技术与实例

}

}

INPUTMESSAGE 函数用来处理在聊天输入框中输入的对话消息:

void CFiveChessView::INPUTMESSAGE()

{

//如果网络还没有连接好则不允许输入

if(!m_bIsInit)

{

AfxMessageBox("No Connection ");

return;

}

CString in;

CMessg msg;

m_inputedit.GetWindowText(in); //得到输入框中的文字

if(in.GetLength()<1)

{

return;

}

if(in.GetAt(in.GetLength()-1)==’\n’) //输入了回车

{

in.TrimRight(" ");

m_inputedit.SetWindowText(""); //处理掉空格字符

if(in.GetLength()>2)

{

m_sMsgList+=in; //添加聊天的内容

m_outputedit.SetWindowText(m_sMsgList); //输出到聊天显示框

m_iLineCurrentPos=m_outputedit.GetLineCount();

m_outputedit.LineScroll(m_iLineCurrentPos);//滚动到当前的位置

msg.m_strText=in;

m_ClientSocket.SendMessage(&msg); //发送聊天的内容到另外一端

}

}

}

处理鼠标左键按下的消息OnLButtonDown

void CFiveChessView::OnLButtonDown(UINT nFlags,CPoint point)

{

//比赛是否已经结束

if(m_bOver==TRUE)

{

if(AfxMessageBox("想重新开始游戏吗?",MB_YESNO)==IDYES)

{

//重新开始游戏

m_bIsClient=FALSE;

m_bIsInit=FALSE;

m_turn=1;

m_match.Clear();

m_bWin=FALSE;

m_bOver=FALSE;

Invalidate(FALSE);

return;

第4 章网络游戏开发223

}

}

//轮到本机走棋

if(m_turn==m_who)

{

m_POS[0]=(point.y-TOPDIS)/30;

if((point.y-TOPDIS)%30>15)

m_POS[0]++; //得到点击的竖直位置

m_POS[1]=(point.x-LEFTDIS)/30;

if((point.x-LEFTDIS)%30>15)

m_POS[1]++; //得到点击的水平位置

if(m_POS[0]<0||m_POS[0]>=LW||m_POS[1]<0||m_POS[1]>=LW)

{

return;

}

//这个位置是否可以落子

if(m_match.CanDown(m_POS[0],m_POS[1],m_who)==TRUE)

{

Invalidate(FALSE); //重新绘制棋子和棋盘

m_turn=m_who%2+1; //轮到对方走棋

CMessg msg;

msg.m_turn=m_turn;

msg.m_x=m_POS[0];

msg.m_y=m_POS[1];

m_ClientSocket.SendMessage(&msg); //发送落子的位置

//判断是否赢棋

if(m_match.IsWin(m_who,m_winpos))

{

m_bWin=TRUE;

m_bOver=TRUE;

Invalidate(FALSE);

AfxMessageBox("赢了耶");

m_sMsgList+="赢了耶";

m_outputedit.SetWindowText(m_sMsgList);

}

}

}

else

{

m_sMsgList+="不该你走棋";

m_outputedit.SetWindowText(m_sMsgList);

}

CView::OnLButtonDown(nFlags, point);__

}