Season1:講解經典Shiru函式庫的11個案例 Season2:任天堂遊戲複刻<題目待定>
行前說明
歡迎光臨!各位任天堂玩家~
在本系列Season1的課程哩,我們會教大家如何親手以C語言製作任天堂遊戲ROM。Season1會基於大神Shiru所開發的超好用函式庫,對其所提供的11個經典範例檔進行解析。在Season2裡,我們會指定一個真實存在的任天堂遊戲作為藍本,對其進行複刻的練習。坐穩囉~讓我們快樂啟航吧!
*有關於任天堂紅白機的英文簡稱,日本版機型簡稱為FC,美版機型簡稱為NES。在本教程中將此二名詞視為等義。
軟件安裝
在Season1的開發過程中,我們需要以下軟件:
- 文字編輯器:Notepad++
- C編譯器:CC65
- FC專用圖形編輯器:YYCHR
- FC模擬器:FCEUX
- Shiru函示庫與範例
傅老師已經幫大家準備好了,請點此連結下載。
下載後請自建一個工作目錄,將所有檔案解壓進去即可使用。
首次編譯
進入<cc65_nes_examples_shiru>目錄,這個目錄涵蓋所有shiru大神所貢獻的函式庫及範例檔。
現在就讓我們來編譯shiru的第1課吧!
首先進入<cc65_nes_examples_shiru>目錄,在檔案管理員中以滑鼠左擊空白處一次,再按住SHIFT鍵並且滑鼠右擊一次,你將會看到只有老司機才知道的選單:
選擇<在此處開啟命令視窗>,即可開啟令人聞風喪膽的主控台(Console)。並且,進入的目錄恰恰好就是你剛剛所在的目錄,這實在是太方便了~~(拍手拍手)
現在鍵入下列指令,即可將shiru提供的第1課程式碼進行編譯:
> _compile example1 nrom_128_vert
*請注意:上述所謂的指令碼,是不包括最左側的大於符號的(>)。那個大於符號稱為提示字元,藉以告知用戶目前處於指令輸入狀態。
編譯完畢後我們就可以得到這輩子第一個自行編譯的ROM了!它的名稱叫做<example1.nes>(哦喔喔~~只有25KB)。
現在開啟FCEUX,將example1.nes拽進去,就可以運行啦~~~!!
源碼解說
在Shiru的第1課中,我們可以學會如何將瓦片圖集(tileset)中的瓦片,貼至螢幕上的指定位置。現在就讓我們來解析這個過程。
首先打開YYCHR,將<tileset.chr>這個檔案讀入,你就會發現任天堂FC在影像顯示上的奧妙:
原來我們看到的字,是從<tileset.chr>這個檔中,一塊一塊挖出來放到螢幕上的啊!!~~
那程式怎麼知道該去挖哪一塊作為對應的字元呢?這裡也是有招式的。
首先我們跟讀者同步一下編號的指稱方式:
- 在這個圖集中,左上角是第0片瓦片(0索引的概念),編號向右遞增。
- 首列最末為第15片,以HEX表達即為0x0F
- 以此概念來看,第二列首字元<0>,其瓦片編號為0x10;第三列中字元<A>,其瓦片編號為0x21
因此,我們只要將想列印的字元,其ASCII碼減去0x20,就可得到瓦片編號,也就知道該去抓哪一個瓦片來顯示此字元。
以notepad++開啟example1.c
有了瓦片的概念後,我們可以開始解析example1了。請以notepad++開啟<example1.c>。
首先,我們匯入了neslib.h檔頭,這讓我們得以使用shiru所設計的函式庫。
//this example code shows how to put some text in nametable //it assumes that you have ASCII-encoded font in the CHR tiles $00-$3f //it also shows how to detect PAL/NTSCvideo system #include "neslib.h"
接著,我們自訂義了一個put_str()函數,用來朝向FC的顯示記憶體添加欲顯示的字符串。
vram意指顯示記憶體(video ram),這個vram裡面有什麼,螢幕上就會在對應的地方顯示瓦片。
所以只要掌握對vram的寫入方式,就可掌握瓦片顯示(對,還有另外一種叫做圖精靈顯示)。
以下代碼中,vram_adr()可設置目前操作中的vram位置,vram_put()可朝向操作位置寫入具體的數值。
void put_str(unsigned int adr,const char *str) { vram_adr(adr); //利用shiru函式庫將vram操作位置設在adr while(1) { if(!*str) break; //將目前str指標所指向的字元值減去0x20,再放到vram中。 //注意*str++,因為帶了++,所以這行每執行一次,str就會指向下一個字元 vram_put((*str++)-0x20);//-0x20 because ASCII code 0x20 is placed in tile 0 of the CHR } }
最後讓我們來解析主函數main()。
void main(void) { //首先設置調色盤,色號是0索引,共有32色。 //在我們拿到的tileset.chr瓦片圖集中,文字瓦片都是用#1號色繪製。 //故此處我們將#1號色設為白色。白色的任天堂色盤內碼是0x30 pal_col(1,0x30);//set while color //以下7行程式利用我們自製的put_str()函式,朝向vram填寫字符串。 //NTADR_A(x,y)函式是shiru函式用來求出畫面上第x,y格瓦片的vram位置。 //任天堂的畫面一共有32*30格瓦片。 //在NTSC電視系統上,第0列會超出畫面上緣,第29列超出畫面下緣,NTSC系統這兩列是看不到的。 put_str(NTADR_A(2,2),"HELLO, WORLD!"); put_str(NTADR_A(2,6),"THIS CODE PRINTS SOME TEXT"); put_str(NTADR_A(2,7),"USING ASCII-ENCODED CHARSET"); put_str(NTADR_A(2,8),"(WITH CAPITAL LETTERS ONLY)"); put_str(NTADR_A(2,10),"TO USE CHR MORE EFFICIENTLY"); put_str(NTADR_A(2,11),"YOU'D NEED A CUSTOM ENCODING"); put_str(NTADR_A(2,12),"AND A CONVERSION TABLE"); put_str(NTADR_A(2,16),"CURRENT VIDEO MODE IS"); //使用shiru函式ppu_system()偵測目前是NTSC還是PAL電視系統。 //有興趣的人可以在FCEUX裡利用Region設置來切換電視系統 if(ppu_system()) put_str(NTADR_A(24,16),"NTSC"); else put_str(NTADR_A(24,16),"PAL"); //開啟繪圖功能,此時vram裡面有什麼東西通通會被丟上螢幕。 ppu_on_all();//enable rendering //以無限迴圈鎖死遊戲。 //未來這裡會拿來製作gameloop while(1);//do nothing, infinite loop }
簡單吧!恭喜你掌握了FC的第一課~
下節課我們將會對FC的硬件架構做個基礎說明,為大家解密FC硬件的內部運作機制。
回家作業
請試試看進行以下修改:
- 將畫面上所有文句改掉,製作一篇自我介紹。
- 更換調文字顏色
- 以YYCHR重新設計A~E的5個字元,顯示在螢幕上。
- 如何混用YYCHR與本節所學,在螢幕上顯示出自己的中文姓氏?
哇!好文!
已拜读
超赞啊!炒鸡干货开播了!
太干货了!
很赞啊!期待下一课。
牛逼啊啊啊啊!!!!