俄羅斯方塊實(shí)驗(yàn)報告.doc
《俄羅斯方塊實(shí)驗(yàn)報告.doc》由會員分享,可在線閱讀,更多相關(guān)《俄羅斯方塊實(shí)驗(yàn)報告.doc(13頁珍藏版)》請?jiān)谘b配圖網(wǎng)上搜索。
《軟件工程與開發(fā)實(shí)踐1》 軟件設(shè)計(jì)報告 題 目 俄羅斯方塊 學(xué) 院 計(jì)算機(jī)學(xué)院 專 業(yè) 計(jì)算機(jī)科學(xué)與技術(shù) 班 級 學(xué) 號 10109345 學(xué)生姓名 其他成員 組 長 指導(dǎo)教師 孫志海 完成日期 2012年6月 1、 軟件設(shè)計(jì)概述(目的、任務(wù)、開發(fā)環(huán)境、參考資料) 俄羅斯方塊游戲?qū)儆诮?jīng)典小游戲,游戲規(guī)則簡單,但又不乏趣味。而計(jì)算的一大領(lǐng)域也是游戲,所以,成為游戲開發(fā)者,幾乎是每個編程者的夢想。經(jīng)過大一和大二的學(xué)習(xí),我們已經(jīng)掌握了編程基礎(chǔ)。為了提高我們的編程能力,我們就要不斷積累編程經(jīng)驗(yàn)。 1、 目的:復(fù)習(xí)和鞏固C/C++編程的基本思想;掌握數(shù)據(jù)結(jié)構(gòu)的核心思想;掌握C/C++中多文件的編寫;初步對了解界面的設(shè)計(jì)。 2、 任務(wù):完成一個可以運(yùn)行的游戲。 3、 開發(fā)環(huán)境:C/C++控制臺。 4、 參考資料: [1] 譚浩強(qiáng).C語言程序設(shè)計(jì)[M].北京:清華大學(xué)出版社.2004.6 [2] 孫鑫\余安萍.VC++深入詳解[M].北京:電子工業(yè)出版社.2006.6 二、可行性研究、需求分析及分工 這是一個游戲軟件,程序與用戶的交流只在游戲界面上,方塊的產(chǎn)生是隨機(jī)的。 3、 軟件設(shè)計(jì)的基本原理和采用的主要方法與技術(shù) 1、方塊類型以下7大類 █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ 每一種方塊都能夠變形,所以在游戲中如何正確打印出方塊的類型是重點(diǎn),也是難點(diǎn)。我采用的是“相對坐標(biāo)法”,具體實(shí)現(xiàn)參照“實(shí)現(xiàn)的過程與步驟”部分。 2、此游戲是簡單的二維游戲,而且區(qū)域恒定不變,所以在存儲游戲的信息時,二維數(shù)組是首選。用數(shù)組元素值模擬當(dāng)前位置有無方塊。 3、 流程圖如下 到達(dá)底部 到達(dá)底部, 游戲結(jié)束 到達(dá)底部部 結(jié)束 銷行操作 生成下一個下墜物 將新生的下墜物代替舊的“下一 個下墜物“ 將舊的“下一個下墜物”用作當(dāng)前 下墜物 銷行操作 游戲結(jié)束處理 下降一個單位 開始 4、采用的方法 在控制臺下,光標(biāo)是左到右,自上而下的,所以要要調(diào)用系統(tǒng)函數(shù)來控制光標(biāo)。同理,為了界面的美觀,也要調(diào)用系統(tǒng)函數(shù)進(jìn)行顏色控制。 5界面設(shè)置 游戲的最大特點(diǎn)就是界面的美觀,由此才能吸引玩家的興趣,因此如何讓界面盡最大限度美觀,是每個游戲程序員努力的目標(biāo)。這個程序是在VC環(huán)境下基于 C/C++控制臺的,由于VC下沒有像TC下那樣豐富的圖形庫,畫圖就要調(diào)用windows API函數(shù)。但由于我對windowsAPI理解不深,所以畫起圖來還是比較困難。 游戲不僅要求界面美觀,而且還要音樂來襯托,所以在整個程序中,盡量讓方塊的每一個動作與特殊的音樂像對應(yīng),此外,最好加上背景音樂。 4、 實(shí)現(xiàn)的過程與步驟 數(shù)據(jù)結(jié)構(gòu): 1、 方塊的存儲 如下圖所示,每一種方塊都由四個小方塊組成,可以按順序編號①、②、③、④,在方塊旋轉(zhuǎn)、輸出、擦出時,可以由第一個方塊位置加上(減去)第二個與第一個的偏移量,從而找到第二個方塊,如此可以方便遍歷四個方塊。 由于方塊屬于寬字符,故在占兩個字節(jié),輸出的時候占兩位。設(shè)①號塊的坐標(biāo)為(x,y),那么第二塊與它的偏移量的△x=2,△y=0,相對坐標(biāo)即為(2,0)。同理,③號方塊的相對坐標(biāo)為(2,1),④號方塊的坐標(biāo)為(4,1),特別的,① 號方塊的相對坐標(biāo)為(0,0),這樣一來,只要知道每一種(7大類,19種)方塊的①號方塊的坐標(biāo),就可以通過②、③、④方塊與①號方塊的偏移量而逐個輸出整個方塊。 明白了方塊的輸出,就要用一個數(shù)據(jù)結(jié)構(gòu)存儲方塊了。19種方塊都由4個小方塊組成,在打印的時候最好能用一個4次的循環(huán),這樣代碼也顯得簡單。 綜上以上兩點(diǎn),可以定義結(jié)構(gòu)體 typedef struct _VARY { int vary_x[4]; int vary_y[4]; }VARY; 存儲4個小塊之間x,y坐標(biāo)之間的相對偏移量。在定義變量的時候初始化成 VARY vary[] ={ {{0, 2, 4, 6}, {0, 0, 0, 0}}, {{0, 0, 0, 0}, {0, -1, -2, -3}}, {{0, 2, 2, 0}, {0, 0, -1, -1}}, {{0, -2, -2, -4},{0 , 0, -1, -1}}, {{0, 0, 2, 2}, {0, -1, -1, -2}}, {{0, 2, 2, 4}, {0, 0, -1, -1}}, {{0, 0, -2, -2}, {0, -1, -1, -2}}, {{0, 0, 2, 4}, {0, -1, 0, 0}}, {{0, 0, 0, 2}, {0, -1, -2, -2}}, {{0, 0, -2, -4}, {0, -1, -1, -1}}, {{0, 2, 2, 2}, {0, 0, -1, -2}}, {{0, 2, 4, 4}, {0, 0, 0, -1}}, {{0, -2, -2, -2},{0, 0, -1, -2}}, {{0, 0, 2, 4}, {0, -1, -1, -1}}, {{0, 0, 0, -2}, {0, -1, -2, -2}}, {{0 , 2, 4, 2} {0, 0, 0, -1}}, {{0, 0, 2, 0}, {0, -1, -1, -2}}, {{0, -2, 0, 2}, {0, -1, -1, -1}}, {{0, 0, -2, 0}, {0, -1, -1, -2}}, }; 這樣,7大類,共19中方塊就全部存儲了,在輸出時只要用一個4次循環(huán)就可以了。 2、控制變形 → 變形 → <1> <2> 如圖所示, <1>種方塊經(jīng)變形得到到<2>種方塊。那么如何才能根據(jù)<1>種方塊快速地找到<2>種方塊,這是關(guān)鍵。有方塊的存儲可以聯(lián)想到,在這兒能不能也用相對坐標(biāo)來存儲呢?可以的! 如圖所以,設(shè)<1>種塊的①號小塊坐標(biāo)為(x,y),則<2>種塊的①號小塊坐標(biāo)為(x+2,y-1),其相對偏移量為(2,-1)。這樣一來在當(dāng)方塊<1>變?yōu)榉綁K<2>時,只需把光標(biāo)移動到點(diǎn)(x+2,y-1)處即可,然后可以通過一個4次循環(huán),完整打印出<2>方塊。 明白了控制變形,就要設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)。但是每一類方塊通過變形產(chǎn)生的子類方塊的目是不定的,如上類方塊變形可生成兩類,但是“山”字形方塊變形,最多可產(chǎn)生4類,而“田”形的只有一類。 所以在存儲變形的相對位置時,還要將7大類方塊變形產(chǎn)生方塊的數(shù)目記下來。綜上,可以定義如下結(jié)構(gòu)體 typedef struct _CONNECTION { int sum; int connection_x[5]; int connection_y[5]; }CONNECTION; 其中,sum用來存儲該類變形產(chǎn)生的方塊數(shù),connection_x[5]和connection_y[5]用來每一個子類與大類直接的相對位置。由于每一大類變形產(chǎn)生的子類數(shù)目不定,故用一個能容下最大量是數(shù)組。 有了結(jié)構(gòu)體的定義,可以具體定義變量 CONNECTION connection[] = { {2, { -2, 2}, {0, 1}}, {1, {0}, {0}}, {2, {2, -2}, {0, 0}}, {2, { -2, 2}, {0, 0}}, {4, { -2, 0, 4, -2}, {0, 0, -1, 1}}, {4, { -4, 2, -2, 4}, {0, 0, -1, 1}}, {4, { -2, 2, 0, 0}, {0, 0, 0, 0}}, }; 這樣,在連續(xù)變形中,可以通過不斷對sum去余而得到當(dāng)錢方塊形狀。 3、 用戶界面的存儲 正如前面所說,俄羅斯方塊是個二維游戲,所以很容易想到用二維數(shù)組存儲界面信息。 用數(shù)組元素和界面建立一一對應(yīng)的關(guān)系,在游戲過程中會出現(xiàn)銷行,所以,這就要存儲當(dāng)前位置有無方塊,如果,為了界面美觀,還要存儲當(dāng)前位置方塊的顏色。綜上,可以定義一下結(jié)構(gòu)體: typedef struct _BOARD { int x; int y; int color; bool having; }BOARD; 游戲的界面大小根據(jù)開發(fā)者自己定義,這兒用滿格為15*25的區(qū)域,即定義界面面板BOARD board[15][25]; 主要函數(shù) 1、 void choice_direction(int *prev_count) 作用:實(shí)現(xiàn)上、下、左、右的選擇。 參數(shù):旋轉(zhuǎn)次數(shù)指針,用來記錄當(dāng)前方塊旋轉(zhuǎn)次數(shù)。 實(shí)現(xiàn):根據(jù)_kbhit()函數(shù)判斷當(dāng)前有無按鍵,若無,則方塊下降;若有,則用switch()判斷是哪個鍵,根據(jù)鍵值修改跟蹤方塊的參數(shù)。 2、void get_depth(int *pdepth) 作用:計(jì)算當(dāng)前方塊可以下降的高度。 參數(shù):高度值。 實(shí)現(xiàn):由于每個方塊有4個小方塊,而且每個小方塊的上下位置不一,而方塊能夠下降的高度,應(yīng)該是最小高度,所以應(yīng)該在4個值里面去最小值。 3、 void check_boundary() 作用:判斷當(dāng)前方塊是否已處于左右邊界。 實(shí)現(xiàn):同上,方塊有4個小塊,在判斷是否到達(dá)邊界時,四個小塊都要判斷。若沒有觸界,則處理左右按鍵引起的水平移動;若已處于邊界,則對左右按鍵引起的參數(shù)值的改變進(jìn)行復(fù)原。 4、 void invilate() 作用:方塊到達(dá)底部后數(shù)據(jù)記錄 實(shí)現(xiàn):根據(jù)方塊的類型、坐標(biāo),對應(yīng)對數(shù)組里面的個元素賦值,包括坐標(biāo)點(diǎn)和顏色值已經(jīng)狀態(tài)量。 5、 遇到的困難與獲得的主要成果 1、方塊的存儲和變形 這個問題我想了好久。開始的時候我想著把19種方塊分開了,但那樣出現(xiàn)的問題就是:豎直下落的時候很簡單,但是如果變形,新方塊的位置怎么確定,鑒于這個問題,最初的方法還是放棄了。 我用“相對偏移法”的靈感來自于指針和尋址。在存儲的時候,第一方塊的位置就相當(dāng)于是基“地址”,而后面的3個小塊都可以通過加上一個相對值來找到,這與指針和相對尋址不是很相似嗎? 2、 判斷有誤按鍵 開始的時候給按鍵開了一個進(jìn)程,用來判斷有誤按鍵。但是那樣也有個不足的地方,每一種進(jìn)程執(zhí)行的時間長度是隨機(jī)的,如果不對每個進(jìn)程進(jìn)行控制,就會出現(xiàn)“在輸出四個小塊的時候,值輸出了前兩個小塊,CPU就轉(zhuǎn)向判斷有誤按鍵了,而如果這時恰好有按鍵,而且是水平位移鍵,出現(xiàn)的結(jié)果就是部分小塊在左端,部分小塊在右端”的情況,很顯然,這是絕對不可以的。 之前我不知道有_kbhit()這個函數(shù),我也是在閱讀別人的代碼時發(fā)現(xiàn)這個函數(shù)的。有了這個函數(shù),就不用開線程了,也不用去控制線程了,程序簡潔多了。 3、 獲取方塊可以下落的高度 如前面函數(shù)聲明里所說,方塊下落的高度要去最小值。但是隨著水平按鍵,導(dǎo)致方塊的左右移動,方塊能夠下降的高度隨時都在變。也可以用另一個線程獲取下降高度,但不好的情況和用線程判斷按鍵類似,所以我的處理就是在每一次方塊的位置改變時,都調(diào)用獲取高度的函數(shù)。 4、 方塊的下落 方塊下落的高度有獲取高度函數(shù)確定。但是會有一種極端情況出現(xiàn),如下所示: 方塊道道第3列上方時,它能下降的高度已經(jīng)是0,但是如果在它到到第3列上方,隨后立即按了水平右移鍵,那么,它能下降的高度至少是4,而不是0。因此一味得說能下降的高度是0時,它就停止下降,顯然是不對的。下降高度是0時,下降函數(shù)就馬上執(zhí)行完了,至少是跳出了下降循環(huán),所以就要想辦法,在在下降函數(shù)執(zhí)行馬上結(jié)束時,再一次判定它的能夠下降的高度,如果高度不是0,那就再次調(diào)用下降函數(shù)(它自己),也就是遞歸,如果下降高度還是0,那么這個方塊就落到底部了。 采用這種方法可以很好的解決在水平移動時的類似問題。但是這種方法也有個不足就是,如果當(dāng)方塊已經(jīng)到底了,這時仍然按了水平移動鍵,而此時,它又滿足水平移動的要求,那么他就反復(fù)遞歸了,遞歸棧越來越深,只有累計(jì)時間溢出時,下一個方塊才能落下。 5、 多文件編寫 以前寫程序都是一main到底,多文件的。初次試著用多文件編寫,遇到的主要問題是全局變量的存放。 在多文件編寫中,變量,尤其的全局變量的定義和引用顯得不那么精簡。很多人可能會想,把全局變量放在一個*.h文件中,然后在以后的調(diào)用中只要include一下不就可以了?想想也有道理,include不就是把*.h原封不動的復(fù)制過來嗎?但是,這樣存在一個問題。加入定義了10個全局變量,而在具體一個*.c文件中我只用其中一個。如果用include把全局變量全都包含進(jìn)來。那利用率不就才是10%嗎。而且C語言常用于嵌入式,如果每個文件都include一個全局變量頭文件,那么這些變量就被分配 一次內(nèi)存,這對嵌入式來說肯定是致命的;另外,如果有很多個文件都include了全局變量的頭文件,編譯器要跟蹤每一個文件對其中一個變量的更改,這對編譯器來說也是吃不消的。 所以便有了*.c和*.h的區(qū)別。 很多人都知道聲明和定義的區(qū)別——一個分配內(nèi)存,一個不分配內(nèi)存,而且函數(shù)的聲明和定義更是顯而易見,但是對于變量的聲明和定義,就顯得有些模糊不清。由于如上所述的種種原因,C語言中全局變量放在*.c文件,而不放在*.h文件。這樣一來,*.h文件好像無用武之地了,非也。我們經(jīng)常見到#include "****.h",但有誰見過#include "***.c"嗎?所以*.h文件用來聲明,*.c文件用來實(shí)現(xiàn),在調(diào)用處用extern聲明。 1、普通變量定義成全局變量 如果是普通類型,完全可以不用*.h文件,直接在*.c文件中定義,在調(diào)用文件處用extern 聲明,因?yàn)閷τ谄胀愋?,編譯器是可以識別的。比如在一個 my.c文件中,我定義了char name[10];那么在別的文件中只要用extern char name[](由于是聲明,一位數(shù)組可以省略大小,但不建議用指針,比較指針和數(shù)組是兩回事)外部聲明就可以了,告訴編譯器這個變量我已經(jīng)定義過了,具體怎樣,你慢慢找吧。這符合常理,因?yàn)閏har是編譯器能自主識別的類型。 2、自定義結(jié)構(gòu)體類型定義成全局變量 不同于普通類型,如果不預(yù)先通知編譯器,編譯器是不會識別你自定義的類型的。這個時候,*.h文件便出現(xiàn)了。不是定義結(jié)構(gòu)類型不占內(nèi)存嗎?那好,我大結(jié)構(gòu)體的定義放在*.h文件中,這樣一來,無論你incude無數(shù)次,內(nèi)存都不會被占用的。而且這樣還有個好處,在別的文件中可以include這個*.h文件,這樣,在這個文件中,編譯器就可以識別你的自定義類型了,目的不就達(dá)到了? 假如我在global.h中定義了 typedef struct _POSITION { int x; int y; }POSITION; 那么我可以在一個global.c文件中實(shí)現(xiàn)全局變量的定義,不過要include那個*.h文件,比如 /* ***global.c ******* */ include “global.h” POSITION current; 這樣就定義了cunrrent這個變量,在別的文件中引用這個變量時,只要extern POSITION current;進(jìn)行聲明,然后就可以用了,不過這個文件也還得include "global.h" 因?yàn)槿绻话?,在這個文件中是不識別POSITION類型的。 所得:首先,在整個程序中,我決定最大的兩點(diǎn)在于那兩個結(jié)構(gòu)體的定義,我想,這才是我們學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的意義。學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu),不是讓我們寫一個堆棧或者是鏈表,而是要學(xué)會分析,將問題抽象提煉出來,根據(jù)問題自己定義一個結(jié)構(gòu)。然后,在這個程序中,好幾個系統(tǒng)函數(shù)都是我在閱讀別人代碼時看到的,由此可見交流的重要性。最后,分析問題一定要嚴(yán)謹(jǐn),在這個調(diào)試這個程序的時候,就在水平移動、落地的時候,出現(xiàn)過很多bug,我也是費(fèi)了好久才調(diào)試過來,所以,分析問題一定要嚴(yán)謹(jǐn)。 6、 測試與運(yùn)行記錄 程序測試結(jié)果良好,但不能排除bug的可能。程序第一次執(zhí)行時會出現(xiàn) <1> <2> 用于選擇保存成績的路徑,界面<1>,因?yàn)榇撕蟪绦蛟谶\(yùn)行時,都要打印當(dāng)前的最高分,所以就要把每一次的最好分存儲起來,程序下次運(yùn)行時,直接從這個文件中讀取最高分。 存儲分?jǐn)?shù)路徑選擇好后,接著會出現(xiàn)界面<2>,一個簡單的啟動畫面,只需按任意鍵即可進(jìn)入游戲。 游戲界面如下,左面為游戲主界面,右面有三行,第一行 用于顯示下一個方塊;第二行用于顯示當(dāng)前用戶的得分,已經(jīng)在這臺PC機(jī)上的最高分;第三行是游戲說明以及作者信息。 7、 結(jié)果分析與小結(jié) 在統(tǒng)計(jì)分?jǐn)?shù)時,一次銷的行越多,得到分?jǐn)?shù)越多。一次1行的1分;一次兩行的3分;一次3行的6分;一次4行得10分。在級數(shù)統(tǒng)計(jì)時, [1,20)為一級;[20,50)為二級;[50,90)為三級;[90,140)為四級;[140,+∞ )為五級。 小結(jié): 俄羅斯方塊是經(jīng)典游戲,也是每一個程序員必須編寫的程序之一。但是編程就像寫文章,不同的人有不同的思想,實(shí)現(xiàn)的算法也就不盡相同,通過編寫這個小游戲,首先要學(xué)會獨(dú)立思考,將實(shí)際問題抽象成程序。這點(diǎn)很重要比如對方塊存儲的結(jié)構(gòu)體的定義,方塊編寫前后的聯(lián)系,以及最后極端情況的調(diào)試。這些情況都和對問題的分析理解有著很大的關(guān)系,所以,要學(xué)會分析問題。 在這個程序中,有幾個函數(shù)是我初次使用,以前也不知道那些函數(shù)。C/C++提供了豐富的庫函數(shù),熟練掌握那些函數(shù),是一個優(yōu)秀程序員最基本的要求,但是我們不能去死記硬背,而且我們也不知道到底有那些庫函數(shù)。所以,我們要通過大量的實(shí)踐來熟悉這些函數(shù)。閱讀別人代碼、相互交流是一種很好的學(xué)習(xí)途徑,閱讀別人代碼,你可能會見到那些你不知道的函數(shù),相互交流,你就能知道別人對這個問題的想法,這樣一來,取長補(bǔ)短,我們進(jìn)步的就更快了。 8、 附錄(軟件配置、含注釋的的程序模塊核心代碼) 軟件配置:只能在windows下運(yùn)行。 軟件缺陷:跟別的軟件一樣,這個軟件也有缺陷,也有bug,主要表現(xiàn)在: 1、 背景音樂的播放 程序中用PlaySound()來打開一個wav音樂作為背景音樂,而方塊到達(dá)底部或者變形時又要打開另一個wav音樂。傳統(tǒng)DOS程序是單線程的,而且揚(yáng)聲器只有一個,因此,若要同時打開兩個音樂,就要用多線程操作,即執(zhí)行一個時把另一個掛起。但是播放音樂又和普通函數(shù)的調(diào)用不太一樣,普通函數(shù)掛起,只需一個延時函數(shù)即可,而音樂的播放又不同,因?yàn)橐魳芬坏┐蜷_,要么就繼續(xù)播放,要么就終止播放想保存播放的位置而下次繼續(xù)播放,對我來說有點(diǎn)難度,至今還沒解決。- 1.請仔細(xì)閱讀文檔,確保文檔完整性,對于不預(yù)覽、不比對內(nèi)容而直接下載帶來的問題本站不予受理。
- 2.下載的文檔,不會出現(xiàn)我們的網(wǎng)址水印。
- 3、該文檔所得收入(下載+內(nèi)容+預(yù)覽)歸上傳者、原創(chuàng)作者;如果您是本文檔原作者,請點(diǎn)此認(rèn)領(lǐng)!既往收益都?xì)w您。
下載文檔到電腦,查找使用更方便
9.9 積分
下載 |
- 配套講稿:
如PPT文件的首頁顯示word圖標(biāo),表示該P(yáng)PT已包含配套word講稿。雙擊word圖標(biāo)可打開word文檔。
- 特殊限制:
部分文檔作品中含有的國旗、國徽等圖片,僅作為作品整體效果示例展示,禁止商用。設(shè)計(jì)者僅對作品中獨(dú)創(chuàng)性部分享有著作權(quán)。
- 關(guān) 鍵 詞:
- 俄羅斯方塊 實(shí)驗(yàn) 報告
鏈接地址:http://m.appdesigncorp.com/p-9158741.html