在項目之間有段“空項期”,上個項目剛剛完成,下個項目還沒落實,時間比較充裕。去年9月份就經(jīng)歷了這么一次短暫的“空項期”,那時偶還是一名前端工作者,C#使用起來毫不含糊,還自己整過一個類SCSF的MVP框架AngelFrame(詳見之前博客:http://m.survivalescaperooms.com/wgp13x/p/99c2adc52d8f0dff30a038841ac32872.html)。在那段“空項期”之前,有位朋友托我做個小游戲,偶也滿口的答應(yīng),只可惜之前項目太忙沒時間做,就一直耽擱了,正好有這段“空項期”,所以做了一下,現(xiàn)在回想起來,做這個小游戲的過程中還是學(xué)習(xí)到了不少東西的,因為做游戲跟做項目的常用技術(shù)不同,于是在這里總結(jié)一下,別把這么寶貴的經(jīng)驗給弄丟了。
這個小游戲的需求很簡單,就是在局域網(wǎng)的環(huán)境里能夠自組織一個飛行棋平臺,多個玩家在里面你一步我一步的玩,看誰先飛完全程。在做這個游戲之前,偶連什么是飛行棋,飛行棋怎么玩的都不懂,就先在網(wǎng)上試玩了一小把,查了查飛行棋的規(guī)則。會玩兒了,就要想想怎么做了,規(guī)則實現(xiàn)肯定要有,用戶交互是個問題。在網(wǎng)上搜索了一下現(xiàn)成的C#飛行棋實現(xiàn)例子,發(fā)現(xiàn)不是給自家女兒做的就是給自家兒子做的,單機版的,手機版的,就是沒有局域網(wǎng)版的。好吧,只有看我來創(chuàng)造了。關(guān)鍵詞:飛行棋, C#, 局域網(wǎng), 多人對戰(zhàn)摘要:很久之前就有個朋友托我做個游戲了,這個游戲的需求很簡單,就是在局域網(wǎng)的環(huán)境里能夠自組織一個飛行棋平臺,多個玩家在里面你一步我一步的玩。游戲規(guī)則有了,實現(xiàn)的難點在于自建多人對戰(zhàn)平臺,C#飛行棋實現(xiàn)例子有很多,可就是沒有局域網(wǎng)版的,下面就是我抽出時間寫的一個局域網(wǎng)多人對戰(zhàn)飛行棋,在這里總結(jié)一下。
解釋一下,打開主界面就是上面這個樣子,如果局域網(wǎng)內(nèi)同時在線的游戲客戶端多,那么在當(dāng)前在線列表中會顯示同時在線的客戶端ip和用戶名,這時你先選擇要進(jìn)行游戲的客戶端,再點擊創(chuàng)建游戲,就可以開創(chuàng)一輪新游戲了,游戲者在兩到四之間。如果你要添加跨網(wǎng)段的客戶端,那就要點擊邀請好友按鈕,填寫IP,如果它們在線就會添加到當(dāng)前在線列表中,這時你再選擇它們,點擊創(chuàng)建游戲,就能夠在開創(chuàng)的新游戲里跟他們玩了。創(chuàng)建游戲成功后,在左下角處會出現(xiàn)一個色子,游戲面板上會出現(xiàn)各類顏色的飛機,每個游戲者對應(yīng)一類顏色。由一個游戲者先擲色子,擲到6后才能夠起飛,其它的游戲者輪循著來,每個游戲者的動作都能夠被其它游戲者收到,在下方文字欄中,會作出說明。網(wǎng)絡(luò)版的游戲,要做只能做成一個Server和多個Client,每個Client的每一步都要通知Server,由Server來運行游戲邏輯,指導(dǎo)Client的運行流程;或只是多個Client,每個Client都要維護(hù)其它的Client信息,Client的運行流程都要通知其它所有的Client,每個Client都運行游戲邏輯。我這里陰差陽錯的選擇了第二種,每個Client都是平等的,沒有Server。通知用的是UDP,局域網(wǎng)的用戶交互就全靠它了,下面是網(wǎng)絡(luò)通知相關(guān)的代碼,使用單例模式,它是局域網(wǎng)游戲運行起來的核。| publicclassNetwork{GhostName;publicstaticIPAddressIpAddress;privatestaticThreadlistener;publiceventEventHandler<GameMsg>ReceivedMsg;publicstaticNetworkInstance{get{if(_instace==null)_instace=newNetwork();return_instace;}}privateNetwork(){UdpClient=newUdpClient(Port);HostName=Dns.GetHostName();foreach(IPAddressipinDns.GetHostAddresses(HostName)){if(ip.AddressFamily==AddressFamily.InterNetwork){IpAddress=ip;break;}}}//局域網(wǎng)內(nèi)廣播,在上線時調(diào)用,通知其它Client有人上線了publicvoidBroadcast(GameMsgmsg){IPEndPointipEndPoint=newIPEndPoint(IPAddress.Broadcast,Port);byte[]bytes=Tools.Serialize(msg);UdpClient.Send(bytes,bytes.Length,ipEndPoint);}//接收消息線程入口,收到消息后觸發(fā)各類事件publicvoidReceiveMsg(){IPEndPointipEndPoint=newIPEndPoint(IPAddress.Broadcast,Port);while(true){byte[]bytes=null;try{bytes=UdpClient.Receive(refipEndPoint);}catch(SocketExceptionex){return;}GameMsgmsg=(GameMsg)Tools.Deserialize(bytes);if(ReceivedMsg!=null&&!msg.IpAddress.Equals(IpAddress))ReceivedMsg(this,msg);}}//向某IP定向發(fā)送消息publicvoidSend(IPAddressip,GameMsgmsg){IPEndPointipEndPoint=newIPEndPoint(ip,Port);byte[]bytes=Tools.Serialize(msg);UdpClient.Send(bytes,bytes.Length,ipEndPoint);}//向一些IP定向發(fā)送消息publicvoidSend(IPAddress[]ips,GameMsgmsg){foreach(IPAddressipAddressinips){if(!ipAddress.Equals(IpAddress))Send(ipAddress,msg);}}} |
| [Serializable]publicclassGameMsg:EventArgs{publicMsgTypeEnumMsgType;publicIPAddressIpAddress;publicobjectMsgContent;}[Serializable]publicclassLandGameMsg//OnlineReply,Hello{publicstringHostName;publicstringUserName;}[Serializable]publicclassCreateGameMsg{publicstringCreaterHostName;publicstringCreaterUserName;publicIPAddress[]SelectedIpAddresses;publicstring[]SelectedHostNames;}...... |
| publicpartialclassMainForm:Form{publicstaticThreadListener=newThread(newThreadStart(Network.Instance.ReceiveMsg)){Name="receiveMsg",Priority=ThreadPriority.Highest};publicstaticCurrStatEnumCurrStat=CurrStatEnum.Idle;privatestaticreadonlyMsgTypeEnum[]interestMsgTypes=newMsgTypeEnum[]{MsgTypeEnum.QuitGame}; publicMainForm(){Listener.Start();Network.Instance.ReceivedMsg+=newEventHandler<GameMsg>(_network_ReceivedMsg);Network.Instance.Broadcast(newGameMsg(){MsgType=MsgTypeEnum.LandGame,IpAddress=Network.IpAddress,MsgContent=newLandGameMsg(){HostName=Network.HostName}});} privatedelegatevoidDelegate_ReceivedMsg(GameMsgmsg);void_network_ReceivedMsg(objectsender,GameMsge){Delegate_ReceivedMsgmyDelegate=newDelegate_ReceivedMsg(handleReceivedMsg);if(interestMsgTypes.Contains(e.MsgType))Invoke(myDelegate,e);}voidhandleReceivedMsg(GameMsgmsg){switch(msg.MsgType){caseMsgTypeEnum.QuitGame://收到某人退出游戲請求MessageBox.Show("有小伙伴要求退出游戲");ucUsersInGame_QuitGame(null,null);break;}}} |
| publicpartialclassInviteOthers:Form{privatestaticreadonlyMsgTypeEnum[]interestMsgTypes=newMsgTypeEnum[]{MsgTypeEnum.OnlineReply};publicList<GameMsg>OnlineReplys=newList<GameMsg>(); void_network_ReceivedMsg(objectsender,GameMsgmsg){if(interestMsgTypes.Contains(msg.MsgType)){switch(msg.MsgType){caseMsgTypeEnum.OnlineReply: OnlineReplys.Add(msg);break;}}} } |
| ///監(jiān)控鼠標(biāo)和窗口位置privatevoidtimer1_Tick(objectsender,EventArgse){intmouse_x=Cursor.Position.X,mouse_y=Cursor.Position.Y;intwindow_x=this.Location.X,window_y=this.Location.Y;intwindow_width=this.Size.Width,window_height=this.Size.Height;if(isHiding==false&&window_y==0){if(window_x-mouse_x>10||mouse_x-window_x-window_width>10||mouse_y-window_y-window_height>10){timer1.Enabled=false;timer2.Enabled=true;}}if(isHiding==true&&mouse_y<=1&&mouse_x>window_x&&mouse_x<window_x+window_width){timer1.Enabled=false;timer3.Enabled=true;}}///隱藏界面privatevoidtimer2_Tick(objectsender,EventArgse){intwindow_height=this.Size.Height;startY+=window_height/8;if(startY<window_height){this.Location=newPoint(this.Location.X,-startY);}else{this.Location=newPoint(this.Location.X,1-window_height);isHiding=true;timer2.Enabled=false;timer1.Enabled=true;}}///顯示界面privatevoidtimer3_Tick(objectsender,EventArgse){intwindow_height=this.Size.Height;startY-=window_height/8;if(startY>0){this.Location=newPoint(this.Location.X,-startY);}else{this.Location=newPoint(this.Location.X,0);isHiding=false;timer3.Enabled=false;timer1.Enabled=true;}} |
|
新聞熱點
疑難解答