对战坦克大战vc++Word文件下载.docx
《对战坦克大战vc++Word文件下载.docx》由会员分享,可在线阅读,更多相关《对战坦克大战vc++Word文件下载.docx(18页珍藏版)》请在冰豆网上搜索。
InitServer())
第4章网络游戏开发277
PostQuitMessage(0);
//创建socket监听线程
CreateThread(NULL,0,AcceptThread,NULL,0,NULL);
//创建socket工作线程
CreateThread(NULL,0,WorkerThread,NULL,0,NULL);
return0;
caseWM_SETFOCUS:
SetFocus(hwndList);
caseWM_DESTROY:
TerminateServer();
}
returnDefWindowProc(hwnd,message,wParam,lParam);
WndProc在创建消息中首先调用了InitServer,以初始化服务器。
然后,它开启两个线程,一个是
socket监听线程AcceptThread,另一个是socket工作线程WorkerThread。
初始化服务器函数InitServer,定义如下:
boolInitServer()
WSADATAwsd;
sockaddr_inlocal;
//socket初始化
if(WSAStartup(MAKEWORD(2,2),&
wsd)!
=0)
returnfalse;
//创建监听socket
slisten=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(slisten==SOCKET_ERROR){
WSACleanup();
//绑定地址和端口
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_family=AF_INET;
local.sin_port=htons(sport);
if(bind(slisten,(structsockaddr*)&
local,
sizeof(local))==SOCKET_ERROR){
closesocket(slisten);
//将socket变成文件使用方式,并在上面监听socket
iocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
iocp){
278VisualC++游戏开发技术与实例
//初始化socket池和玩家信息池
olexPool.InitPool(0)||!
playerPool.InitPool(16)){
ZeroMemory(&
gTable,sizeof(GAMETABLE));
//开始监听
if(listen(slisten,SOMAXCONN)!
=0){
Notice(1,"
Serverstartsuccessfully!
"
);
returntrue;
Socket连接监听线程函数AcceptThread定义如下。
它采用轮寻方式监听连接,并将创建的会话
Socket与文件I/O进行关联。
DWORDWINAPIAcceptThread(LPVOIDpParam){
sockaddr_inclient;
intsize;
SOCKETret;
OVERLAPPEDEX*lpolex;
while(true){
size=sizeof(sockaddr_in);
ret=accept(slisten,(sockaddr*)&
client,&
size);
if(ret!
=INVALID_SOCKET)
Notice(2,"
Connect:
inet_ntoa(client.sin_addr));
lpolex=olexPool.GetUsable();
if(lpolex)
//成功接受连接
//将会话Socket和文件关联
CreateIoCompletionPort((HANDLE)ret,iocp,NULL,0);
lpolex->
socket=ret;
RecvMsg(lpolex);
else{
closesocket(ret);
第4章网络游戏开发279
else{//accepterror
ret=WSAGetLastError();
WSAErrorTrigger(ret,TEXT("
AcceptErr:
"
));
return0;
另一个线程函数WorkerThread用于和客户端进行通信,并对整个游戏进行控制。
DWORDWINAPIWorkerThread(LPVOIDpParam){
ULONG_PTRckey;
OVERLAPPED*pol;
OVERLAPPEDEX*polex;
DWORDBytesTransferred;
intret;
int*ibuf;
ret=GetQueuedCompletionStatus(iocp,&
BytesTransferred,
&
ckey,&
pol,INFINITE);
//OVERLAPPEDEX是自定义结构
polex=CONTAINING_RECORD(pol,OVERLAPPEDEX,ol);
//远程主机断开连接
if(ret==0){
intsize=sizeof(sockaddr_in);
getpeername(polex->
socket,(sockaddr*)&
Discont:
//删除所占的座位
for(inti=0;
i<
gTable.current;
i++){
if(gTable.players[i]==polex->
ppla){
if(i>
0)
gTable.players[i-1]->
next=polex->
ppla->
next;
break;
for(;
gTable.current-1;
i++)
gTable.players[i]=gTable.players[i+1];
gTable.current--;
//回收资源
playerPool.Recycle(polex->
ppla);
olexPool.Recycle(polex);
continue;
280VisualC++游戏开发技术与实例
//成功收到消息
switch(polex->
op){
caseOP_READ:
ibuf=(int*)(polex->
wbuf.buf);
switch(ibuf[0]){
//分配玩家座位表
caseNETMSGTK_ASKGROUPINFO:
Notice("
AskGroup:
ibuf[2]);
polex->
ppla=playerPool.GetUsable();
seat=gTable.current;
gTable.players[gTable.current]=polex->
ppla;
gTable.players[gTable.current]->
socket=polex->
socket;
SendMsg(NETMSGTK_ANSWERSEATINFO,polex->
socket,
gTable.current,sizeof(int));
AnswerSeat:
gTable.current);
next=NULL;
if(gTable.current>
0){
gTable.players[gTable.current-1]->
SendMsgToOther(NETMSGTK_MOREPLAYER,gTable,gTable.current,
if(++gTable.current==MAXPLAYER)
SendMsgToTable(NETMSGTK_GAMEREADY,gTable,NULL,0);
caseNETMSGTK_PLAYERREADY:
if(++gTable.counter==MAXPLAYER){
SendMsgToTable(NETMSGTK_GAMESTART,gTable,NULL,0);
//初始化奖子
gTable.food.exsit=false;
gTable.food.existnum=DEFFOODEXFRAME;
gTable.food.notexistnum=DEFFOODNOTEXFRAME;
gTable.food.counter=DEFFOODNOTEXFRAME;
gTable.counter=0;
caseNETMSGTK_CMDINFO:
if(gTable.food.counter--<
if(gTable.food.exsit){//删除
gTable.food.counter=gTable.food.notexistnum;
SendMsgToTable(NETMSGTK_CMDFOODDELETE,gTable,NULL,0);
}else{//创建
gTable.food.counter=gTable.food.existnum;
intfoodparam[3];
foodparam[0]=rand()%FOOD_MAX;
foodparam[1]=rand()%608;
foodparam[2]=rand()%608;
SendMsgToTable(NETMSGTK_CMDFOODCREATE,gTable,foodparam,sizeof(int)*3);
第4章网络游戏开发281
gTable.food.exsit=!
gTable.food.exsit;
SendMsgToOther(NETMSGTK_CMDINFO,gTable,polex->
seat,ibuf+2,
ibuf[1]);
caseNETMSGTK_TEAMVICTORY:
SendMsgToTable(NETMSGTK_TEAMVICTORY,gTable,ibuf+2,ibuf[1]);
RecvMsg(polex);
caseOP_WRITE:
4.10.2对战坦克大战的客户端程序
对战坦克大战的客户端程序界面如图4.16所示。
图4.16坦克大战客户端
注意:
如果想测试这个游戏,需要同时运行4个客户端程序。
282VisualC++游戏开发技术与实例
程序主框架首先调用InitNetwork函数用于初始化网络通信。
InitNetwork函数定义如下:
boolInitNetwork(constchar*serv_addr,unsignedintserv_port)
sockaddr_inlocal,server;
unsignedlongul=1;
//初始化socket
//创建客户端socket并绑定
c_socket=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(c_socket==SOCKET_ERROR)
c_port=NET_CLIENT_PORT_MIN;
while(c_port<
NET_CLIENT_PORT_MAX)
local.sin_port=htons(c_port);
if(bind(c_socket,(structsockaddr*)&
if(ret==WSAEADDRINUSE)
c_port++;
elsebreak;
if(c_port>
=NET_CLIENT_PORT_MAX)
server.sin_addr.s_addr=inet_addr(serv_addr);
server.sin_family=AF_INET;
server.sin_port=htons(serv_port);
//连接服务器
if(connect(c_socket,(constsockaddr*)&
server,sizeof(server))==SOCKET_ERROR)
if(ret==WSAENETDOWN||ret==WSAENETUNREACH)
ERRORMSG("
Can’treachserver.\nPleasecheckyournetworkconnection."
elseif(ret==WSAECONNREFUSED)
Theserverdoesnotwork!
elseif(ret==WSAEPROCLIM)
Toomanyusers.\nPleasetrylater."
第4章网络游戏开发283
//设置socket为非阻塞
if(ioctlsocket(c_socket,FIONBIO,&
ul)==SOCKET_ERROR)
//初始化消息列表
NetList.CreatMsgList(8,true);
//networkmessagelist
//创建消息接受线程
HANDLEhThread=CreateThread(NULL,0,MsgReceiver,NULL,0,NULL);
if(!
hThread)
InitNetwork函数中开启了一个新线程用于接受网络消息,线程函数是MsgReceiver。
在MsgReceiver
中,程序采用轮寻方式检测网络数据,函数定义如下:
DWORDWINAPIMsgReceiver(LPVOIDparam)
fd_setfdread;
timevaltval;
intret,msgsize;
charbuf_char[BUFFERSIZE];
//接受缓冲
char*mark;
CMsgElemelem;
//向服务器查询组信息
ret=0;
SendMsg(NETMSGTK_ASKGROUPINFO,&
ret,sizeof(int));
tval.tv_usec=0;
tval.tv_sec=1;
//轮寻方式检测是否有网络消息
while(true)
FD_ZERO(&
fdread);
FD_SET(c_socket,&
ret=select(0,&
fdread,NULL,NULL,&
tval);
if(ret==0||ret==SOCKET_ERROR){
//可能还未初始化
NetList.GetSize())
//接受数据
284VisualC++游戏开发技术与实例
ret=recv(c_socket,buf_char,BUFFERSIZE,0);
if(ret==SOCKET_ERROR){
NetList.Lock();
char*temp="
Connectionshutdown!
;
elem.CreateMsgElem(MSGNET_RECEIVEERROR,temp,strlen(temp)+1,MSG_NET);
NetList.Push(&
elem);
NetList.UnLock();
//把消息弹入列表中
mark=buf_char;
while(ret>
0&
elem.CreateMsgElemFromBuf(mark,msgsize,MSG_NET)){
mark+=msgsize;
ret-=msgsize;
MsgReceiver中还调用了SendMsg函数,这是向服务器发送消息的函数。
boolSendMsg(intmsg,LPVOIDparam,intsize)
intret=size+sizeof(int)*2;
char*buffer=newchar[ret];
buffer)
*(int*)buffer=msg;
*(int*)(buffer+sizeof(int))=size;
if(param&
size>
0)
memcpy(buffer+sizeof(int)*2,param,size);
ret=send(c_socket,buffer,ret,0);
delete[]buffer;
if(ret==SOCKET_ERROR)
else
在InitNetwork函数完成后,系统调用GameMain进入游戏控制循环。
在GameMain函数中,程序
首先调用MsgProcessor处理网络消息,接着根据当前的游戏状态作出不同动作。
而当游戏处于运行状
态时,程序首先对子弹进行碰撞检测相关计算,接着对坦克运动做计算,然后再对奖子做碰撞检测计
算,最后是向电脑控制的坦克做AI命令。
当这些控制完成后,程序将上面的动作统一发送到服务器
第4章网络游戏开发285
端。
GameMain的最后部分是绘制这些精灵,绘制的顺序是地图、子弹、坦克、鹰巢和草地(雪地)。
这里实现坦克游戏能够完全模仿FC(FamilyComputer)上的坦克大战,所以坦克是可以
在草地中隐藏的,这也是为什么将草地最后绘制的原因。
voidConsoleNet:
:
GameMain(){
staticintcounter=0;
staticDWORDstart_time=0,last_get;
staticDWORDframe_start=0;
DWORDend_time;
if(m_dwStatus==CONSTAT_ENDGAME)
return;
//如果网络消息队列非空,则调用MsgProcessor函数处理消息列表。
NetList.IsEmpty())
MsgProcessor(&
Net