游戲功能:
游戲界面由4×4的16個方格組成,每個方格中可以存放一個數(shù)字。玩家通過操縱Basys3開發(fā)板的上下左右四個方向鍵和中央的reset鍵,控制方格中的數(shù)字移動。界面由VGA和Pmod接口oled兩種方式顯示。游戲成功時顯示笑臉,失敗時顯示骷髏。
游戲過程中,玩家每按動一次方向鍵,所有數(shù)字按照這個方向移動一次,該方向上相鄰相同的數(shù)字合并為原來的2倍,并在反方向隨機生成一個新的數(shù)字。玩家目標(biāo)是在游戲中拼出2048或更高的數(shù)字,即為游戲成功。
DIY動手指南:
Step1:材料準(zhǔn)備
硬件:
Basys3開發(fā)板
VGA連接線以及VGA顯示器
(可選)sh1106驅(qū)動的oled顯示屏一塊
軟件:
Vivado 2016.2
Step2:系統(tǒng)框架
系統(tǒng)主要由以下幾個模塊構(gòu)成,游戲主狀態(tài)機、游戲數(shù)據(jù)寄存器{S16}、移動模塊move、隨機產(chǎn)生模塊gen、隨機數(shù)生成器ran、VGA驅(qū)動模塊和oled驅(qū)動模塊。
Step3:程序設(shè)計
接下來分模塊進(jìn)行分析。
1、按鍵去抖
對每個按鍵設(shè)計了一個32位長的FIFO,按鍵值從低位進(jìn)入直至高位溢出。每過10ms,F(xiàn)IFO被完全刷新一次,只有當(dāng)32位為FFFFFFFFh時認(rèn)為輸入為1,00000000h時認(rèn)為輸入為0。
2、游戲數(shù)據(jù)寄存器
用4×4=16個4bit寄存器,存放16個格子中的數(shù)據(jù),記為{S16},以【log2對數(shù)】形式存放。即如果格子中是512,則存放9;如果格子中是128,則存放7。特例是0用0來存放。這種存放的特點是,可以節(jié)約寄存器數(shù)量,而且原本的加法128+128=256可以用加1來表示:7+1=8。寄存器位置定義如下:
3、游戲主狀態(tài)機
游戲主狀態(tài)機中,主要操縱數(shù)據(jù)寄存器{S16}的數(shù)值,通過對這個寄存器中數(shù)值的改變來實現(xiàn)游戲的進(jìn)行。
狀態(tài)機分為4個狀態(tài):檢測按鍵+移動,產(chǎn)生隨機數(shù),游戲狀態(tài)檢測,游戲失敗死循環(huán)。
其中游戲狀態(tài)檢測時,檢查當(dāng)前游戲是否已經(jīng)結(jié)束或成功,如果成功,游戲不中斷,玩家可以繼續(xù)下去,如果已經(jīng)結(jié)束,則跳入游戲失敗的死循環(huán)中,結(jié)束游戲。
4、移動邏輯組合電路
本游戲有4種移動方式,即上下左右,每次移動4條線,但本質(zhì)都是相同的,只需要一個模塊即可完成。如下圖所示
move(i3,i2,i1,i0,o3,o2,o1,o0)
我們默認(rèn)向右移動。(為什么呢?因為當(dāng)我們調(diào)用這個模塊時,可以以各種方向調(diào)用:如果向右移動,則調(diào)用move(15,14,13,12)向右,如果向左移動,則調(diào)用move(12,13,14,15)向右即可,還可以調(diào)用move{15,11,7,3}向下,調(diào)用move{1,5,9,13}向上等等。)
那么向右移動時,用f3~f0判斷這四位是否為空,通過對f3~f0的16種情況的mux來決定輸出的值,如果有相同則合并后輸出。(其實這個模塊就是一個大mux)
例如,下圖所示,左圖右移后,變?yōu)橛覉D。
再例如:2,2,4,8右移后變?yōu)?,4,4,8,再右移變?yōu)?,0,8,8再右移變?yōu)?,0,0,16
5、隨機數(shù)生成器和隨機產(chǎn)生模塊
隨機數(shù)產(chǎn)生器為隨機產(chǎn)生模塊gen服務(wù)。隨機數(shù)生成器產(chǎn)生3組隨機數(shù),分別為8bit,12bit和16bit。分別對應(yīng)2空格、3空格和4空格的情況。例如,如果右移后的圖片如下圖所示,那么黃色格子里的2就是在最左列3空格情況下隨機產(chǎn)生的一個位置。
我們還是默認(rèn)向右移動,那么最左邊的四個格子(15,11,7,3)即可交給gen,在空白出產(chǎn)生一個隨機位置的“2”。
這里需要注意的是,2048游戲中規(guī)定必須是“有效移動”后才產(chǎn)生一個數(shù)字,以杜絕有投機的玩家一直按同一個方向鍵可以作弊的情況。這一邏輯只需要對上一步中move前后做一下對比即可。
6、VGA顯示模塊
640×480分辨率,25MHz的時鐘信號。
其中掃描時,將VGA坐標(biāo)的后兩位[1:0]舍去,即可將640×480像素變?yōu)?60×120的色塊。
(注:源碼中的VGA畫圖一段和oled畫圖一段看上去很復(fù)雜,其實是畫點的方式比較奇怪,兩種畫法都是先將132x64的點陣水平劃分為8條,每條8行,再以每列為1個byte的方式畫出來。。。那么為什么要這么復(fù)雜呢?因為oled的sh1106驅(qū)動就是這么要求的,我先做了oled,后面vga就直接引用了~)
Step4:演示見視頻~