單機對戰(zhàn)游戲畢業(yè)設(shè)計_第1頁
已閱讀1頁,還剩17頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、<p><b>  目錄</b></p><p><b>  摘要1</b></p><p><b>  關(guān)鍵字1</b></p><p><b>  1 前言1</b></p><p>  1.1程序開發(fā)背景1</p>&

2、lt;p>  1.2 程序開發(fā)目標(biāo)1</p><p>  1.3編碼設(shè)計標(biāo)準與規(guī)范2</p><p>  1.4程序開發(fā)的實際意義2</p><p>  1.5 對“單擊對戰(zhàn)”的解釋3</p><p>  2 相關(guān)軟件及技術(shù)介紹3</p><p>  2.1 開發(fā)平臺與工具選擇3</p>

3、<p>  2.1.1 .NET簡介3</p><p>  2.1.2 Microsoft Visual Studio 2008簡介3</p><p>  2.2 相關(guān)技術(shù)簡介3</p><p>  2.2.1 使用UDP協(xié)議的套接字技術(shù)3</p><p>  2.2.2 使用TCP協(xié)議的套接字技術(shù)3</p>

4、<p>  2.2.3 Socket方法和相關(guān)類介紹4</p><p>  2.2.4 多線程的使用4</p><p>  2.2.5 事件的使用4</p><p>  2.2.6 反射調(diào)用一個類的私有成員4</p><p>  3 可行性分析與需求分析4</p><p>  3.1 可行性分析

5、4</p><p>  3.2 需求分析5</p><p>  3.2.1 功能需求5</p><p>  4 單擊對戰(zhàn)游戲設(shè)計5</p><p>  4.1 系統(tǒng)設(shè)計思想5</p><p>  4.2 系統(tǒng)總體設(shè)計5</p><p>  5 主要功能詳細設(shè)計6</p>

6、<p>  5.1 在線用戶列表的獲得6</p><p>  5.1.1 設(shè)計思路6</p><p>  5.1.2 實現(xiàn)過程6</p><p>  5.2 進入對戰(zhàn)窗體的友好用戶體驗9</p><p>  5.2.1 設(shè)計思路9</p><p>  5.2.2關(guān)鍵代碼9</p>

7、<p>  5.3 五子棋對戰(zhàn)窗體功能12</p><p>  5.3.1 設(shè)計思路12</p><p>  5.3.2 關(guān)鍵代碼12</p><p><b>  6系統(tǒng)測試15</b></p><p>  6.1測試分析報告15</p><p>  6.2.1測試方式簡介

8、15</p><p>  6.2改進意見15</p><p><b>  7結(jié)束語15</b></p><p><b>  致謝15</b></p><p><b>  參考文獻15</b></p><p>  單擊對戰(zhàn)游戲的設(shè)計與實現(xiàn)</

9、p><p>  摘要:系統(tǒng)是基于.NET平臺開發(fā)的一個P2P結(jié)構(gòu)的單擊對戰(zhàn)五子棋游戲。基于P2P、套接字、事件、設(shè)計模式、多線程、反射等技術(shù),使用c#語言和UDP協(xié)議的套接字進行廣播,不但可以實現(xiàn)在局域網(wǎng)內(nèi)獲得所有在線用戶IP列表、狀態(tài)的功能,而且通過使用TCP協(xié)議的套接字可以實現(xiàn)多個用戶之間的連接、游戲、聊天、并閃動對方屏幕的功能。以計算機技術(shù)和網(wǎng)絡(luò)技術(shù)為核心的現(xiàn)代網(wǎng)絡(luò)技術(shù)已在現(xiàn)實生活和生產(chǎn)中得以廣泛的使用,休閑類

10、網(wǎng)絡(luò)游戲集趣味性、娛樂性、互動性和益智性于一體,已經(jīng)成為多數(shù)人群的休閑方式。</p><p>  關(guān)鍵字:P2P;UDP廣播;TCP協(xié)議;設(shè)計模式;事件</p><p><b>  1 前言</b></p><p><b>  1.1程序開發(fā)背景</b></p><p>  隨著計算機信息技術(shù)的發(fā)展

11、,網(wǎng)絡(luò)游戲已成為我們生活中的重要組成部分,但由于廣域網(wǎng)游戲占用的資源較多、易受到病毒等缺點,所以局域網(wǎng)游戲被游戲愛好者廣泛使用。無論是對于初步探索網(wǎng)絡(luò)編程的編程愛好者還是有志于在物聯(lián)網(wǎng)方面發(fā)展的程序員,編寫簡單的局域網(wǎng)游戲都是一個很好開始。</p><p>  網(wǎng)絡(luò)游戲在市面上已經(jīng)出現(xiàn)多年,許多游戲?qū)?zhàn)平臺也已相繼出現(xiàn),例如我們熟知的浩方、QQ對戰(zhàn)平臺和街機游戲的ArcadeLive平臺等等,雖說它們有很大的不同

12、,但有一點是可以肯定的:不管是這些游戲還是對戰(zhàn)平臺,剛開始都是需要在局域網(wǎng)內(nèi)調(diào)試的。然而,出于盈利的目的,開發(fā)商一般不會將對戰(zhàn)平臺的局域網(wǎng)內(nèi)的版本流于市面,更不會公開自己的原代碼。當(dāng)然通過一些人士的分析和透露,我們知道浩方是通過把tcp/ip協(xié)議轉(zhuǎn)換為ipx協(xié)議。虛擬局域網(wǎng),為大家提供了一個作戰(zhàn)平臺,但游戲開始后,玩家之間就是p2p的關(guān)系了,游戲速度和平臺無關(guān)。但具體的實現(xiàn)肯定是很困難的,里面至少涉及hook,P2P,VPN,修改數(shù)據(jù)包

13、,java或C++的網(wǎng)絡(luò)編程,進程通信,數(shù)據(jù)庫的操作等等。當(dāng)然一些大型游戲里面內(nèi)置了網(wǎng)絡(luò)連接模塊,在局域網(wǎng)內(nèi)是不需要再通過平臺完成數(shù)據(jù)通信的。但對于一些單機游戲,要想完成局域網(wǎng)內(nèi)的對戰(zhàn),對戰(zhàn)平臺就不可或缺了。這方面ArcadeLive是比較有代表性的,它的聯(lián)網(wǎng)功能可能是靠"kaillera"來實現(xiàn)的,要想實現(xiàn)聯(lián)網(wǎng)玩游戲,你必須有一個kaillera的客戶端和一個服務(wù)器端的程序,客戶端現(xiàn)在大多在模擬器中自</p&

14、gt;<p>  1.2 程序開發(fā)目標(biāo)</p><p>  擬開發(fā)基于p2p具有運用時自動加載在線用戶IP列表、雙擊某個IP或頭像自動進入五子棋游戲,并且用戶體驗很好的程序。</p><p>  該程序無需服務(wù)器端,也就是說每個程序?qū)嵗仁欠?wù)端也是客戶端,也就是常被我們提及的p2p。運行程序時通過廣播特定的信息使所有在線的用戶獲得自己的IP然后添加到自己的用戶列表上,收到此

15、特定信息的用戶回復(fù)另一信息,然后就可以實現(xiàn)所有的人獲得在線用戶IP的功能。游戲是逐漸被世人拋棄的起源于中國古代的傳統(tǒng)黑白棋種之一的五子棋,雖然它看似無聊,但對于練習(xí)編程技術(shù)還是很有意義的。本程序中的五子棋使用TCP協(xié)議的套接字進行通訊,廣泛應(yīng)用了事件機制,接收到我們自定義的信息后就會根據(jù)以至的信息頭作出相關(guān)的反映。眾所周之,網(wǎng)絡(luò)游戲會因為種種原因出現(xiàn)延遲,使用戶持續(xù)等待甚至出現(xiàn)卡死的情況,本程序盡最大的努力提高用戶體驗的舒適性,即盡量不

16、發(fā)生卡的情況。</p><p>  1.3編碼設(shè)計標(biāo)準與規(guī)范</p><p>  為了在編碼過程中,能夠快速地、有效地閱讀,我們有必要為對象約定一個規(guī)范的編碼命名約定。</p><p><b>  對象命名約定</b></p><p>  下面為在編碼過程中會用到的對象統(tǒng)一的命名,如下表1所示:</p>&

17、lt;p><b>  表1對象統(tǒng)一的命名</b></p><p><b>  結(jié)構(gòu)化編碼約定</b></p><p>  編碼風(fēng)格參照下例進行:</p><p>  bool startListen = false;</p><p>  public bool CanAgin, CanAgin

18、s, CanDown, IFirst;</p><p>  private Socket socketListerner;</p><p>  private Thread mainThread;</p><p>  public ClientRole clientConnectionRole = new ClientRole();</p><p

19、>  在程序中,對一些比較難懂,或是屬性不熟悉的地方加上必要的文字說明,其次,程序要有層次,使得讀程序時更易讀懂。</p><p>  1.4程序開發(fā)的實際意義</p><p>  對于數(shù)據(jù)的通信,我們比較熟悉的有Tcp/Ip協(xié)議、UDP協(xié)議等等。以UDP協(xié)議為例,UDP從應(yīng)用程序接過報文后,附上目的端口號、源端口號和其他兩個小字段后就直接將結(jié)果段遞交給網(wǎng)絡(luò)層。網(wǎng)絡(luò)層將該段封裝到數(shù)據(jù)

20、報中后,盡力而為地將數(shù)據(jù)包傳遞給接收端主機。如果數(shù)據(jù)報到達接收端主機,UPD將根據(jù)IP地址和兩個端口號將段中的數(shù)據(jù)交給相應(yīng)的進程。本程序中的用戶列表的維護就是利用使用UDP協(xié)議的Socket發(fā)送廣播進行獲得。然而鑒于UDP協(xié)議的無連接特點,丟失數(shù)據(jù)包的可能性也很大的,所以當(dāng)進入游戲窗體后,真正負責(zé)通訊的采用了TCP協(xié)議的套接字進行通訊,也就是說,UDP協(xié)議那個套接字只負責(zé)維護列表。本程序中的游戲是人氣很旺的網(wǎng)絡(luò)五子棋,選擇它的原因很簡單

21、:簡單直接,不會上癮,算法簡單,易于實現(xiàn)。</p><p>  本程序的可擴展性強,程序員可以根據(jù)自己的要求將基于TCP的Socket也提到一個類中,然后通過存放到一個以遠程端點為Key相應(yīng)連接套接字為Value的Dictionary中,以實現(xiàn)和多人對戰(zhàn)的要求。當(dāng)然也可以利用反射將游戲程序編譯成一個類,然后放到一個特定文件夾下,當(dāng)主窗體運行時,自動加載。除此之外,對于網(wǎng)絡(luò)編程的初學(xué)者也是很有幫助的。</p&

22、gt;<p>  1.5 對“單擊對戰(zhàn)”的解釋</p><p>  本程序采用p2p的架構(gòu),即集服務(wù)端和客戶端的功能于一體,整個設(shè)計采用事件驅(qū)動,所以非常方便實現(xiàn)。之所以叫做“單機對戰(zhàn)游戲的設(shè)計與實現(xiàn)”,主要是內(nèi)嵌的游戲是五子棋游戲,它在用戶體驗上的感覺基本就是單擊鼠標(biāo),沒有太多別的負責(zé)的操作,很是適合小朋友或是老年人玩。</p><p>  2 相關(guān)軟件及技術(shù)介紹</

23、p><p>  2.1 開發(fā)平臺與工具選擇</p><p>  2.1.1 .NET簡介 .NET的編程模型將開發(fā)語言和運行時平臺分離,實現(xiàn)了獨立與語言的組件技術(shù),通過不同的運行平臺,.NET應(yīng)用可以被擴展到PC、PDA、手機和嵌入式設(shè)備上。.NET運行平臺稱為.NET Framework,是.NET平臺的基礎(chǔ)框架,它創(chuàng)造了一個完全可操控的安全的和特性豐富的應(yīng)用執(zhí)行環(huán)境,這不但使得應(yīng)用程

24、序的開發(fā)與發(fā)布更加簡單,并且實現(xiàn)了眾多語言的無縫連接。</p><p>  2.1.2 Microsoft Visual Studio 2008簡介 為了開發(fā).NET的應(yīng)用,微軟推出了強大的.NET開發(fā)工具Visual Studio .NET(以下簡稱VS)作為微軟.NET戰(zhàn)略的重要組成部分。VS包含C#、Visual Basic .NET、Visual c++.NET等多個開發(fā)語言,同時還為第三方語言工具

25、商提供了接口,只要支持CLS(Common Language Specification)的語言都可以集成到VS環(huán)境中。</p><p>  VS充分發(fā)揮了CLR的潛力,為開發(fā)者提供了一個統(tǒng)一的集成開發(fā)環(huán)境和調(diào)試器。由于采用統(tǒng)一的類型定義和共享類庫,各語言不僅在運行時,而且在設(shè)計就可以實現(xiàn)對象級 交互。VS不僅提供了Web Services開發(fā)工具,而且提供從Web Services開發(fā)到發(fā)布、注冊、整合的全過程

26、支持。為創(chuàng)建和部署Web Services,.Net平臺采用了一系列的互聯(lián)網(wǎng)協(xié)議,如XML、SOAP、WSDL、UDDL等用于Web Services的部署、請求和響應(yīng)。</p><p>  軟件人員最關(guān)心的還是開發(fā)效率的提高,VS人性化的界面和眾多工具將成倍提高開發(fā)效率。無論針對傳統(tǒng)的Windows桌面開發(fā),Web Services,開發(fā)人員都不必在為每種不用的應(yīng)用重寫全部代碼,不必為不同的客戶設(shè)備定制不同的界

27、面。</p><p>  2.2 相關(guān)技術(shù)簡介</p><p>  2.2.1 使用UDP協(xié)議的套接字技術(shù) 數(shù)據(jù)報式Socket(DATAGRAM)是一種無連接的Socket,對應(yīng)于無連接的UDP服務(wù)應(yīng)用.不安全(丟失,順序混亂,在接收端要分析重排及要求重發(fā)),但效率高。</p><p>  使用UDP協(xié)議的套接字可以用來向許多系統(tǒng)支持的網(wǎng)絡(luò)發(fā)送廣播數(shù)據(jù)包。要

28、實現(xiàn)這種功能,網(wǎng)絡(luò)本身必須支持廣播功能,因為系統(tǒng)軟件并不提供對廣播功能的任何模擬。廣播信息將會給網(wǎng)絡(luò)造成極重的負擔(dān),因為它們要求網(wǎng)絡(luò)上的每臺主機都為它們服務(wù),所以發(fā)送廣播數(shù)據(jù)包的能力被限制于那些用顯式標(biāo)記了允許廣播的套接字中。廣播通常是為了如下兩個原因而使用的:1. 一個應(yīng)用程序希望在本地網(wǎng)絡(luò)中找到一個資源,而應(yīng)用程序?qū)υ撡Y源的地址又沒有任何先驗的知識。2. 一些重要的功能,例如路由要求把它們的信息發(fā)送給所有可以找到的本網(wǎng)段的主機。被廣

29、播信息的目的地址取決于這一信息將在何種網(wǎng)絡(luò)上廣播。本程序中利用基于UDP協(xié)議的套接字向廣播地址255.255.255.255發(fā)送廣播的方式以獲得用戶列表。</p><p>  2.2.2 使用TCP協(xié)議的套接字技術(shù) 本程序中的游戲部分使用的是的TCP協(xié)議的套接字進行通訊。眾所周之流式Socket(STREAM)是一種面向連接的Socket,針對于面向連接的TCP服務(wù)應(yīng)用,雖然效率低但由于其安全特性被廣泛使用

30、。某一個棋子的位置很可能關(guān)系到游戲的勝敗,所以使用TCP協(xié)議的套接字就可以避免丟包等問題。</p><p>  2.2.3 Socket方法和相關(guān)類介紹</p><p>  --------------------方法------------------------------</p><p>  Socket (): 創(chuàng)建一個Socket</p>&

31、lt;p>  Bind(): 綁定一個本地的IP和端口號(IPEndPoint)</p><p>  Listen(): 讓Socket偵聽傳入的連接嘗試,并指定偵聽隊列容量</p><p>  Connect(): 初始化與另一個Socket的連接</p><p>  Accept(): 接收連接并返回一個新的socket</p><p&

32、gt;  Send(): 輸出數(shù)據(jù)到Socket</p><p>  Receive(): 從Socket中讀取數(shù)據(jù)</p><p>  Close(): 關(guān)閉Socket (銷毀連接)</p><p>  ------------------相關(guān)類--------------------------</p><p>  IPAddress類

33、:包含了一個IP地址</p><p>  IPEndPoint類:包含了一對IP地址和端口號</p><p>  2.2.4 多線程的使用 應(yīng)用多線程技術(shù)最大的誤區(qū)在于沒有分清適合的情況就盲目地使用多線程。除非運行一個多處理器計算機,否則在CPU密集的任務(wù)中使用兩個線程不能節(jié)省多少時間,理解這一點很重要。在單處理器計算機上,讓兩個線程同時進行100萬次運算所花的時間與讓一個線程進行2

34、00萬次運算是相同的,甚至使用兩個線程所用的的時間更長,因為要處理另一個線程操作系統(tǒng)必須用一定的事件切換線程,即分配時間片。使用線程帶來的負面因素是必須額外考慮線程的并發(fā)、同步等線程安全問題,從而使得程序更加復(fù)雜而難以維護。然而,有些場合則使用多線程技術(shù)非常合適,網(wǎng)絡(luò)應(yīng)用開發(fā)就是其經(jīng)常出現(xiàn)的場合之一。當(dāng)一個線程等待從網(wǎng)絡(luò)中獲取數(shù)據(jù),同時其他線程可以繼續(xù)處理其他的任務(wù),此時可以節(jié)省很多時間。本程序中主要就是在針對Accept和Receiv

35、e這兩個會阻塞的方法使用多線程。</p><p>  2.2.5 事件的使用 由于不知道什么時候會有人發(fā)送信息給自己,所以程序中應(yīng)用事件來處理對方的消息,即監(jiān)聽對方發(fā)送的消息,如果接受到的消息和自己要處理的一致,則執(zhí)行注冊在其上的方法。例如,A上線的時候,就會向廣播地址發(fā)送“Logon”消息,已經(jīng)在線上的B、C收到此消息后就行執(zhí)行自定義的OnGetList事件,然后此事件調(diào)用注冊在自己身上的GetListM

36、ethod方法,從而實現(xiàn)將A的IP地址加到自己的用戶列表上的功能。</p><p>  2.2.6 反射調(diào)用一個類的私有成員 當(dāng)用戶進入對戰(zhàn)窗體后,主窗體的用戶列表不可用,即一次只能和一個用戶進行游戲,(這只是我個人規(guī)定)。當(dāng)用戶退出對戰(zhàn)窗體后,必須要求主窗體的用戶列表重新可用,但我們注意到,窗體類的TreeView類型字段默認是private,也就是其它類不可訪問的,當(dāng)然我們可以將其改成internal或

37、更加訪問級別使外界可以訪問。但我們有更專業(yè)的手段:利用反射技術(shù)。.NET的一個反編譯工具Reflector就是利用發(fā)射實現(xiàn)的。</p><p>  3 可行性分析與需求分析</p><p><b>  3.1 可行性分析</b></p><p>  在很早以前五子棋軟件就已經(jīng)被開發(fā)出來了,當(dāng)時的五子棋軟件僅提供了“人機模式”與“雙人模式”?!叭?/p>

38、機模式”是指,人與計算機進行對弈,計算機按照事先編寫好的算法程序來進行下棋;“雙人模式”是指,兩個人通過交替使用同一臺計算機來進行下棋。無論是選擇哪一種模式,所有的操作都必須在同一臺計算機上來往完成,棋局無法在異地計算機之間來進行,故而也叫單機版五子棋軟件。</p><p><b>  3.2 需求分析</b></p><p>  軟件的需求分析是軟件生存期中重要的一

39、步,也是決定性的一步。只有通過需求分析才能把軟件功能和性能的總體概念描述為具體的軟件需求規(guī)格說明,從而奠定軟件開發(fā)的基礎(chǔ)。在此階段,了解用戶要求本軟件必須滿足的所有功能和限制,以及用戶對軟件功能和性能的要求,弄清用戶想要軟件“做什么”,準確地表達用戶的要求。</p><p>  3.2.1 功能需求</p><p>  運行游戲自動獲得所有在線用戶列表并且自己的IP加到所有其他在上用戶玩家

40、列表上的功能。</p><p>  顯示請求對戰(zhàn)提示框。</p><p>  進入對戰(zhàn)窗體,其他用戶無法再與其游戲。</p><p>  顯示玩家退出提示框。</p><p>  能通過網(wǎng)絡(luò)進行下棋。</p><p>  符合五子棋的基本規(guī)則。</p><p>  提供玩家之間的聊天功能。<

41、;/p><p>  提供玩家之間閃屏功能。</p><p>  一個用戶退出程序,所有在線用戶列表中自動清除退出用戶對應(yīng)的IP。</p><p>  用戶界面需求界面友好、親切。</p><p><b>  界面簡潔、不花哨。</b></p><p>  操作界面直觀、不繁瑣。</p>

42、<p><b>  用戶體驗良好。</b></p><p>  4 單擊對戰(zhàn)游戲設(shè)計</p><p>  現(xiàn)有的網(wǎng)絡(luò)編程模式主要分成兩類:一種是基于C/S(Client/Server客戶機/服務(wù)器)模式,另一種是B/S (Browser/Server,瀏覽器/服務(wù)器)模式;C/S 程序具有好的交互性,功能強大, 但是客戶端必須安裝客戶端軟件,服務(wù)器端也要運行

43、,限制了其應(yīng)用;B/S 模式下要求客戶端具有瀏覽器,但瀏覽器在安全方面有一些限制,交互性與功能有一些限制。由于這兩種模式的種種弊端,單擊對戰(zhàn)游戲采用了p2p網(wǎng)絡(luò)技術(shù)來設(shè)計與實現(xiàn),網(wǎng)絡(luò)內(nèi)部使用UDP和TCP協(xié)議的Socket 通過傳輸層提供的服務(wù)進行通訊。 </p><p>  4.1 系統(tǒng)設(shè)計思想</p><p>  主窗體運行時開啟是一個基于UDP協(xié)議的套接字用戶獲得所在網(wǎng)段所用玩家的I

44、P地址,雙擊一個IP進入對戰(zhàn)窗體。同時對方收到請求對戰(zhàn)的提示框。</p><p>  對戰(zhàn)窗體通過一個socket 監(jiān)聽其他用戶的連接的連接,一旦有人連接連接,為該用戶建立連接并啟動一個特定的客戶socket,利用該連接不斷從客戶讀取數(shù)據(jù),雙方的交互。</p><p>  4.2 系統(tǒng)總體設(shè)計</p><p><b>  系統(tǒng)流程圖:</b>&

45、lt;/p><p><b>  圖1 系統(tǒng)流程圖</b></p><p>  5 主要功能詳細設(shè)計</p><p>  5.1 在線用戶列表的獲得</p><p>  5.1.1 設(shè)計思路 用戶列表即在線用戶的IP地址。由于本程序使用的端口號為1817,所以一般情況下只需要知道本網(wǎng)段內(nèi)所有運行了本程序的主機的IP就能

46、實現(xiàn)要求。選擇廣播是最簡單且效率最高的手段,接下來考慮的就是應(yīng)當(dāng)使用什么協(xié)議的Socket進行廣播。因為只是獲得列表是需要連接且要求效率較高,UDP顯然在這里最適合。</p><p>  5.1.2 實現(xiàn)過程</p><p><b>  界面設(shè)計</b></p><p>  新建一個空解決方案,命名為”GobangDemo”,在解決方案上右擊添

47、加一個“新建項目”,在出現(xiàn)的對話框上選擇“Windows 窗體應(yīng)用程序”命名為“Gobang1”。</p><p>  將默認生成的窗體刪除,添加一個“Windows 窗體”并命名為FrmMain,將此窗體的FormBorderStyle屬性設(shè)為none。</p><p>  拖入一個Panel控件,命名為pnlWrap,將它的Dock屬性設(shè)為Full。然后依次再拖入三個Panle,分別命

48、名為pnlTop、pnlMain、pnlBelow,然后分別設(shè)置它們對應(yīng)的BackgroundImage的值。</p><p>  拖入一個PictureBox控件,命名為picClose,然后設(shè)置Image屬性以顯示它默認的圖片。拖入一個ImageList控件,使用默認名字imageList1,在其Images屬性中添加一個頭像圖片。拖入一個ContextMenuStrip控件,使用默認命名contextMen

49、uStrip1,然后添加“顯示主界面”和“退出”兩個子菜單。拖入一個NotifyIcon控件,使用默認命名notifyIcon1,設(shè)置Visable屬性為true,ContextMenuStrip屬性為contextMenuStrip1。</p><p>  拖入一個TreeView控件,命名為tvPlayer,設(shè)值ImageList屬性為imageList1,ImageIndex屬性為0。</p>

50、<p>  小結(jié):其實要實現(xiàn)一個漂亮的界面并不是什么復(fù)雜的問題,只要一張很好看的圖片就可以,但一個好的編程習(xí)慣那就是將所有的控件都放到Panel中,所以就要將圖片切成三段,然后設(shè)置成對應(yīng)容器的背景顏色。下面分別介紹各控件的具體作用:notifyIcon1用于將程序圖標(biāo)顯示系統(tǒng)托盤,picClose用于最小化到系統(tǒng)托盤,tvPlayer顯示所有在線用戶IP,imageList1里的圖片作為用戶列表中的頭像,contextMen

51、uStrip1作為notifyIcon1的菜單。</p><p><b>  關(guān)鍵代碼</b></p><p>  GetUserList類中發(fā)送UDP廣播的方法SendToAll</p><p>  public void SendToAll(string state)</p><p><b>  {<

52、/b></p><p>  IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse ("255. 255. 255.255") , port);</p><p>  Byte[] sendBytes = Encoding.ASCII.GetBytes(state);</p><p><

53、b>  try</b></p><p><b>  {</b></p><p>  udpGetList.Send(sendBytes, sendBytes.Length, endPoint);</p><p><b>  }</b></p><p><b>  ……&l

54、t;/b></p><p><b>  }</b></p><p>  GetUserList類中處理信息的方法GetList中接收來自局域網(wǎng)的信息:</p><p>  IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, port);</p><p&

55、gt;  while (true)</p><p><b>  {</b></p><p>  Byte[] receiveBytes = udpGetList.Receive(ref RemoteIpEndPoint);</p><p>  string returnData = Encoding.ASCII.GetString(receiv

56、eBytes);</p><p>  //接下來判斷收到的信息,然后作出相應(yīng)的處理</p><p><b>  ……</b></p><p><b>  }</b></p><p>  處理“Logon”消息:</p><p>  GetListEventArgs getLi

57、st = new GetListEventArgs();</p><p>  getList.IP = RemoteIpEndPoint.Address.ToString();</p><p>  getList.Msg = returnData;</p><p>  OnGetList(this, getList);</p><p>  I

58、PEndPoint endPoint = new IPEndPoint(IPAddress.Parse(getList.IP), port);</p><p>  Byte[] sendBytes = Encoding.ASCII.GetBytes("Reply");</p><p>  udpGetList.Send(sendBytes, sendBytes.Leng

59、th, endPoint);</p><p>  處理“Reply”消息:</p><p>  GetListEventArgs getList = new GetListEventArgs();</p><p>  getList.IP = RemoteIpEndPoint.Address.ToString();</p><p>  if

60、(getList.IP != IPAddress.Loopback.ToString())</p><p><b>  {</b></p><p>  getList.Msg = returnData;</p><p>  OnGetList(this, getList);</p><p><b>  }<

61、;/b></p><p><b>  使用到的事件</b></p><p>  public event EventHandler<GetListEventArgs> OnGetList;</p><p>  其中的GetListEventArgs是一個繼承自EventArgs的自定義類:</p><p&g

62、t;  public class GetListEventArgs : EventArgs</p><p><b>  {</b></p><p>  public string Msg; //獲得發(fā)過來的信息</p><p>  public string IP; //獲得對方IP,用于顯示的主界面的用戶列表上</p>&l

63、t;p><b>  }</b></p><p>  窗體類FrmMain的中的</p><p>  TreeNode node = new TreeNode();</p><p>  node.Text = e.IP;</p><p>  node.Tag = (object)e.IP;</p>&l

64、t;p>  foreach (TreeNode n in tvPlayer.Nodes)</p><p><b>  {</b></p><p>  if (n.Text == node.Text || n.Text.Contains(node.Text))</p><p><b>  {</b></p>

65、<p><b>  return;</b></p><p><b>  }</b></p><p><b>  }</b></p><p>  tvPlayer.Nodes.Add(node);</p><p>  小結(jié):在p2p理念下,使用廣播獲得用戶列表是目前

66、為止我發(fā)現(xiàn)的最高效的實現(xiàn)手段??梢钥吹街恍柘驈V播地址發(fā)送“Logon”信息,在線的所有用戶都會接收到此信息,然后執(zhí)行相應(yīng)的代碼,其中最重要的就是將發(fā)送此信息的用戶的IP加到自己的tvPlayer中。</p><p><b>  圖2 主界面</b></p><p>  5.2 進入對戰(zhàn)窗體的友好用戶體驗</p><p>  5.2.1 設(shè)計

67、思路 IP為117.67.187.188的用戶雙擊想要對戰(zhàn)用戶的IP(假設(shè)是117.67.187.180),這次操作共發(fā)送兩類信息: 180收到“Fight”消息,所有在線用戶收到“InRoom”消息。180進入對戰(zhàn)窗體,此時他的“開始”按鈕不可用。于是180的用戶將看到請求對戰(zhàn)的窗體,并且180發(fā)送“Thinking”廣播;所用用戶的用戶列表上180將添加[fighting]標(biāo)記,180將添加[thinking]標(biāo)記,此時這兩個

68、用戶暫時都不能再被別人請求對戰(zhàn)。如果180點擊對戰(zhàn)窗體的“同意”則進入對戰(zhàn)窗體,此時180將收到“117.67.187.180同意了你的請求”的提示框,點擊確定,開始按鈕可用;如果180點擊拒絕,180將收到“117.67.187.180同意了你的請求”的提示框,180的用戶列表再次可用(利用反射訪問似有成員)。對戰(zhàn)雙方進入對戰(zhàn)窗體后就開始了各自的服務(wù)端角色。</p><p><b>  5.2.2關(guān)鍵

69、代碼</b></p><p>  雙擊117.67.187.180節(jié)點的操作</p><p>  if(!tvPlayer.SelectedNode.Text.Contains("fighting") && !tvPlayer.SelectedNode.Text.Contains("thinking"))</p>

70、;<p><b>  {</b></p><p>  GetWindow();</p><p>  frmFight.IpTag = tvPlayer.SelectedNode.Tag.ToString();</p><p>  frmFight.getUserList = this.getUserList;</p>

71、<p>  frmFight.frmMain = this; </p><p>  frmFight.SetFrmFightPicStartUnabled();</p><p>  getUserList.SendToAll("InRoom"); </p><p>  getUserList.SendToSelected(tvPlay

72、er.SelectedNode.Tag.ToString(), "Fight");</p><p>  tvPlayer.Enabled = false;</p><p>  frmFight.Show();</p><p><b>  }</b></p><p><b>  else<

73、;/b></p><p><b>  {</b></p><p>  MessageBox.Show("哥游戲中,請勿打擾!");</p><p><b>  }</b></p><p>  117.67.187.180收到“Fight”后的操作</p>&l

74、t;p>  getUserList.SendToAll("Thinking"); </p><p>  FrmCue frmCue = new FrmCue();</p><p>  tvPlayer.Enabled = false;</p><p>  frmCue.frmMain = this;</p><p>

75、  frmCue.Tag = e.IP;</p><p>  frmCue.lblIP.Text = e.IP;</p><p>  frmCue.getUserList = this.getUserList;</p><p>  frmCue.Show();</p><p>  所有在線用戶收到“InRoom”后的操作</p>

76、<p>  foreach (TreeNode node in tvPlayer.Nodes)</p><p><b>  {</b></p><p>  if (node.Text.Contains(e.IP))</p><p><b>  {</b></p><p>  node.T

77、ext = e.IP + "[fighting]";</p><p><b>  }</b></p><p><b>  }</b></p><p>  117.67.187.180點擊提示框的“同意”按鈕</p><p>  private void lblAgree_Clic

78、k(object sender, EventArgs e)</p><p><b>  {</b></p><p>  IPAddress[] myIp = Dns.GetHostAddresses(Dns.GetHostName());</p><p>  getUserList.SendToSelected(lblIP.Text.Trim(

79、), "Agree");</p><p>  if (frmFight == null || frmFight.IsDisposed)</p><p><b>  {</b></p><p>  if (myIp[0].ToString() == this.Tag.ToString())</p><p&g

80、t;<b>  {</b></p><p>  this.Close();</p><p><b>  return;</b></p><p><b>  }</b></p><p>  frmFight = FrmFight.GetInstance();</p>

81、<p>  frmFight.Ready();</p><p>  } </p><p>  frmFight.IpTag = this.Tag.ToString(); </p><p>  frmFight.getUserList = this.getUserList;</p><p>

82、;  frmFight.frmMain = this.frmMain;</p><p>  //getUserList.SendToAll("");</p><p>  getUserList.SendToAll("InRoom");</p><p>  frmFight.Show();</p><p>

83、;  this.Close();</p><p><b>  }</b></p><p>  117.67.187.180點擊提示框的“拒絕”按鈕</p><p>  private void lblIgnore_Click(object sender, EventArgs e)</p><p><b>  {

84、</b></p><p>  getUserList.SendToSelected(lblIP.Text.Trim(), "Refuse");</p><p>  getUserList.SendToAll("Free");</p><p>  SetFrmMainTreeViewEnabedIsTrue();&l

85、t;/p><p>  this.Close();</p><p><b>  } </b></p><p>  利用反射在frmCue中訪問frnMain中的似有方法</p><p>  private void SetFrmMainTreeViewEnabedIsTrue()</p><p>

86、;<b>  {</b></p><p>  Type typeFrmMain = frmMain.GetType();</p><p>  MethodInfo methodNiDongDe = typeFrmMain. GetMethod(" SetTreeViewEnableIsTrue",BindingFlags.NonPublic | B

87、indingFlags.Instance);</p><p>  methodNiDongDe.Invoke(frmMain, null);</p><p><b>  }</b></p><p>  進入對戰(zhàn)窗體那一刻開始各自服務(wù)端的角色</p><p>  IPEndPoint ep = new IPEndPoint

88、(IPAddress.Any, mainPort );</p><p>  socketListerner = new Socket(AddressFamily.InterNetwork,</p><p>  SocketType.Stream, ProtocolType.Tcp);</p><p>  socketListerner.Bind(ep);</p

89、><p>  socketListerner.Listen(12);</p><p>  startListen = true;</p><p>  while (true)</p><p><b>  {</b></p><p>  Socket socketServer;</p>&

90、lt;p>  ClientConnection conn;</p><p><b>  try</b></p><p><b>  {</b></p><p>  socketServer = socketListerner.Accept();</p><p>  conn = new Cli

91、entConnection(socketServer); </p><p>  dicConnections.Add(socketServer.RemoteEndPoint.ToString(), conn);</p><p>  RemoteTag = socketServer.RemoteEndPoint.ToString();</p><

92、;p><b>  }</b></p><p><b>  catch</b></p><p><b>  {</b></p><p><b>  break;</b></p><p><b>  }</b></p>

93、<p>  dicConnections[socketServer.RemoteEndPoint.ToString()].OnAddChess += new EventHandler<AddChessEventArgs>(this.AddChess);</p><p>  …… dicConnections[socketServer.RemoteEndPoint.To

94、String()].StartReceive();</p><p><b>  }</b></p><p>  小結(jié):這段功能基本還是利用事件驅(qū)動,本程序中應(yīng)用的是微軟大力推薦的EventHandler委托,它有兩種使用方式:一種是直接在定義一種此類型的委托,另一種使用它對應(yīng)的泛型委托EventHandler<TEventArgs>。個人特別喜歡第二種用法

95、。如果說使用事件是很高級的技術(shù),那么反射則是高級技術(shù)中的王者。DotNet最為人熟知的反編譯工具Reflector就是利用反射做出來的。通過反射我們可以獲得一個類或一個類實例的結(jié)構(gòu),也就是說,只要屬于程序集,我們就可以通過反射看到源代碼。</p><p>  圖3 180看到的對戰(zhàn)請求窗體</p><p>  圖4 180點擊同意后188看到的提示</p><p&g

96、t;  圖5 180點擊拒絕后188看到的提示</p><p>  5.3 五子棋對戰(zhàn)窗體功能</p><p>  5.3.1 設(shè)計思路 點擊開始按鈕后,開啟各自的客戶端角色。棋盤是通過GDI畫上去的,并且每個圖片都注冊一個單擊事件。單擊棋盤一個位置,對應(yīng)位置顯示對應(yīng)顏色的棋子圖片,發(fā)送并且判斷是否自己是否贏了</p><p>  5.3.2 關(guān)鍵代碼<

97、;/p><p>  開始客戶端角色的關(guān)鍵代碼</p><p>  public void ConnectServer(string ip, int port)</p><p><b>  {</b></p><p>  socketClientRole=new Socket (AddressFamily.InterNetwo

98、rk, Socket Type.Stream, ProtocolType.Tcp); </p><p>  IPEndPoint ep = new IPEndPoint(IPAddress.Parse(ip), port);</p><p>  socketClientRole.Connect(ep);</p><p>  clientConnected = tru

99、e;</p><p><b>  }</b></p><p>  棋盤初始化(注冊事件)</p><p>  for (int i = 0; i < 15; i++)//棋盤</p><p><b>  {</b></p><p>  for (int j = 0; j

100、 < 15; j++)</p><p><b>  {</b></p><p>  PictureBox pictureBox1 = new PictureBox();</p><p>  pictureBox1.Location = new System.Drawing.Point(i * 29 + 150, j * 29 + 70);

101、</p><p>  pictureBox1.Name = "pictureBox" + (i + j * 15).ToString();</p><p>  pictureBox1.Size = new System.Drawing.Size(30, 30);</p><p>  pictureBox1.Image = imageList1.I

102、mages[2];</p><p>  pictureBox1.TabStop = false;</p><p>  forChess[i + j * 15] = 521; </p><p>  pictureBox1.Click += new EventHandler(ClickChessboard);</p><p>  this.pnl

103、Main.Controls.Add(pictureBox1);</p><p><b>  }</b></p><p><b>  }</b></p><p>  單擊棋盤圖片發(fā)送的信息代碼</p><p>  public void ClickChessboard(object sender, E

104、ventArgs e)</p><p><b>  {</b></p><p>  if (CanDown == true)</p><p><b>  {</b></p><p>  if (IFirst == true)</p><p><b>  {</

105、b></p><p>  PictureBox pictureBox1 = (PictureBox)sender;</p><p>  pictureBox1.Enabled = false;</p><p>  int s = Int32.Parse(pictureBox1.Name.Substring(10));</p><p> 

106、 ClientRole clientRole = clientConnectionRole;</p><p><b>  BBow++;</b></p><p>  if (BBow % 2 == 1)//black 奇數(shù)為黑</p><p><b>  {</b></p><p>  lblBla

107、ck.Visible = false;</p><p>  lblWhite.Visible = true;</p><p>  pictureBox1.Image = imageList1.Images[0];</p><p>  clientRole.SendMessage("Down#" + s.ToString() + "|&q

108、uot; + "0" + "@" + BBow.ToString());</p><p>  forChess[s] = 0;</p><p><b>  }</b></p><p>  else //white</p><p><b>  {</b><

109、/p><p>  lblBlack.Visible = true;</p><p>  lblWhite.Visible = false;</p><p>  pictureBox1.Image = imageList1.Images[1];</p><p>  clientRole.SendMessage("Down#" +

110、 s.ToString() + "|" + "1" + "@" + BBow.ToString());</p><p>  forChess[s] = 1;</p><p>  } </p><p>  Low.ChessJudge(ref forChess, ref

111、BCount, this);</p><p>  IFirst = false;</p><p><b>  }</b></p><p><b>  else</b></p><p><b>  {</b></p><p>  MessageBox.Sh

112、ow("對方還沒下棋!!");</p><p><b>  }</b></p><p><b>  }</b></p><p><b>  else</b></p><p><b>  {</b></p><p>

113、;  MessageBox.Show("請先按準備和開始按鈕!");</p><p><b>  }</b></p><p><b>  }</b></p><p><b>  單例者模式</b></p><p>  private static FrmFi

114、ght frmFight = new FrmFight();</p><p>  public static FrmFight GetInstance()</p><p><b>  {</b></p><p>  if (frmFight.IsDisposed)</p><p><b>  {</b&g

115、t;</p><p>  frmFight = new FrmFight();</p><p><b>  }</b></p><p>  return frmFight;</p><p><b>  }</b></p><p> ?。?). 五子棋規(guī)則示例“從左往右橫向”

116、 </p><p>  for (int k = 0; k < 225; k++)</p><p><b>  {</b></p><p>  if (a[k] == 0)</p><p><b>  {</b></p><p><b>  BCount++;

117、</b></p><p>  for (int p = 1; p < 5; p++)</p><p><b>  {</b></p><p>  if (k + p < ((k / 15) + 1) * x)</p><p><b>  {</b></p>&l

118、t;p>  if (a[k + p] == 0)</p><p><b>  {</b></p><p><b>  BCount++;</b></p><p><b>  }</b></p><p>  else if (a[k + p] == 1)</p>

119、<p><b>  {</b></p><p>  BCount = 0;</p><p><b>  }</b></p><p><b>  }</b></p><p><b>  }</b></p><p>  i

120、f (BCount == 5)</p><p><b>  {</b></p><p>  frmFight.BlackWin();</p><p><b>  return;</b></p><p><b>  }</b></p><p><b&

121、gt;  else</b></p><p><b>  {</b></p><p>  BCount = 0;</p><p><b>  }</b></p><p><b>  }</b></p><p><b>  }</

122、b></p><p><b>  6系統(tǒng)測試</b></p><p><b>  6.1測試分析報告</b></p><p>  系統(tǒng)測試是保證軟件質(zhì)量的關(guān)鍵,也是在成功開發(fā)軟件系統(tǒng)的重要保證之一。它貫穿于軟件系統(tǒng)開發(fā)的整個過程中,具有多種系統(tǒng)測試方式和測試對象。</p><p>  6.2.

123、1測試方式簡介</p><p>  單元測試:對每個模塊進行單獨測試,最大程度的減少模塊內(nèi)部所有錯誤。</p><p>  組合測試采:用自頂向下測試方法對通過單元測試的模塊進行系統(tǒng)化的方法裝配與測試軟件系統(tǒng)。</p><p>  系統(tǒng)測試:系統(tǒng)集成后,對其整體性進行測試,對各模塊之間的數(shù)據(jù)通信和數(shù)據(jù)共享進行測試,保證系統(tǒng)運行的正確性。</p><

124、;p><b>  6.2改進意見</b></p><p>  由于時間倉促與測試不方便,對戰(zhàn)時一次只能與一位玩家對戰(zhàn),并且在網(wǎng)絡(luò)上發(fā)送的信息都采用明文發(fā)送,這其實是很不安全的。若要實現(xiàn)一次能與多個玩家對戰(zhàn)其實很簡單,只需像本程序中UDP的套接字那樣封裝到一個類中,然后當(dāng)監(jiān)聽到某個網(wǎng)絡(luò)端點的連接后,將Accept()產(chǎn)生的套接字放到一個以此網(wǎng)絡(luò)端點為Key的Dictionary中。信息的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論