天天看點

(筆記)《遊戲腳本進階程式設計》——第1章 腳本程式設計概論

1.怎樣具體設計一個遊戲呢?程式員使用一個編譯器将設計文檔中的具體說明編寫成實際的功能程式,美術設計人員使用Photoshop和3ds max之類的圖像處理和生成軟體将概念藝術和架構轉化為具體的圖形,而音樂制作人員則利用一個MIDI生成器或其他的跟蹤軟體将他們腦海中的聲音轉化成音樂遊戲。那麼,問題來了,沒有工具或器械能夠将故事情節和遊戲角色也“輸入”進去。

2.解決的方法:

①讓程式員在遊戲引擎中手工編寫出所有這些編碼。以遊戲的物品為例,為描述物體,要寫一個結構體:

typedef struct _Item
{
    char * pstrName; //物品的名稱
    int iType;  //物品的具體類型
    int iPrice; //在物品店的價格
    int iPower; //具有的能量數量
}Item;
           

其中iType是物品的類型,遊戲引擎需要利用這個資料了解該物品被使用時應具有什麼功能。類似這種描述物品功能的常量應該事先定義:

const HEAL = 0;
const MAGIC_RESTORE = 1;
const ARMOR_REPAIR = 2;
const TELEPORT = 3;
           

這為選擇物品類型提供了一個非常實用的方法。如果一個物品類型是Heal,那麼它存放的就是遊戲玩家的健康值。下一個是魔力值,接着是修複裝甲,最後是在一定的遊戲條件下,遊戲玩家由一個遊戲世界快速跳到另一個遊戲世界中。

iPower的作用是無論當你準備使用這個物品實作什麼樣的處理,該物品都應該達到這個數量或者這種程度。即如果物品想要存放HP,而且iPower=32,那麼玩家就可以取出這32點生命值并使用它們。

這樣就可以定義很多物品了,物品的定義暫且忽略,不過會出現一個問題,那就是物品的平衡問題,該問題貫穿整個遊戲開發,并不是一件小事情,要不斷地進行修改,但是你在遊戲引擎中修改後會花費大量的時間進行重新編譯,更加糟糕的是,重新編譯的代碼中有99.9%的内容并沒有改變。

這種方法的弊端已經出現了:首先,當你直接将你的物品描述實作在遊戲代碼之中時,你就需要重新編譯和它相關的所有内容,浪費了大量的時間;其次,是它的組織結構。RPG的遊戲引擎問題也很複雜。

②通過将邏輯和具體實作互相分離來改善這種方法:

現在問題的關鍵是如何将遊戲代碼和遊戲内容加以分離。

用第①種方法定義物品的代碼是:

ItemArray[1].pstrName = "Magic Potion Lv 6";
ItemArray[1].iType = MAGIC_RESTORE;
ItemArray[1].iPrice = 250;
ItemArray[1].iPower = 60;
           

現在,将等号右邊的所有内容提取出來,存放到某個ASCII檔案中:

Magic Potion Lv 6
MAGIC_RESTORE
250
60
           

這兩種方法表示形式相同,唯一的差別是它所封裝的所有C/C++代碼都是被分離的,在經過以下步驟裝載到遊戲中:

(1)打開檔案并确定物品描述數組裡面的哪個索引是用來存儲相關内容的。可采取循環方式

(2)讀取第一個字元串并将其存放到變量pstrName中

(3)讀取下一行,如果本行的内容是HEAL,就将變量iType指派為HEAL,以此類推

(4)讀取下一段内容,将其由字元串類型轉化為整型并将其存放到變量iPrice中

(5)讀取下一段内容,将其由字元串類型轉化為整型并将其存放到變量iPower中

(6)重複上面的(1)~(5)步,知道所有的物品都被指派為止

這樣,每次運作程式的時候,程式将會在這個ASCII檔案中讀取資料,而不用重新加載程式代碼以修改物品資料,這就是遊戲腳本程式設計的概念。這個例子告訴我們所有類型的腳本程式設計背後的基本準則——如何避免寫死

3.簡單來說,寫死是指當你想把物品直接寫入到遊戲引擎中時所做的工作。也就是一種以一種嚴格的、固定的和難以編寫的方式來編寫程式和資料

4.第②種方法已經可以滿足大部分的需求了,但是卻無法從根本上對這些問題加以解決,是因為我們遲早要用到一些獨特而又複雜的物品,這樣的系統所能實作的功能是非常有限的。但是如果對單個物品進行編碼,比如加入if條件判斷寫入引擎,那麼又陷入了前面寫死的誤區。于是,腳本就用到了。腳本可以使你真正在遊戲引擎之外編碼,然後再載入遊戲引擎中執行

5.那麼腳本是如何運作的呢?腳本和傳統變成非常相似,唯一的差別在于運作時它們是怎樣被加載和執行的。在了解腳本運作過程之前,先了解一下普通程式是如何運作的。

用進階語言編寫的代碼最終都會轉換為低級的,可被機器識别的形式。原因是機器并不能識别這種進階語言,需要編譯器将C/C++這種語言轉化成為最低級的、位元組流形式的純代碼,即轉化成為機器碼。但機器碼難以了解,故一般被寫成更加容易了解的彙編語言形式。

一旦代碼被編譯了,一切都會變得更加迅速。編譯器将所有編譯的代碼交給一個稱為linker(連結器)的程式,它将輸入的大量的指令集封裝成一個帶有一定頭資訊的、簡潔緊湊的可執行檔案,并最終生成一個.EXE(或是其他作業系統使用的擴充名)檔案。當你運作這個可執行檔案時,作業系統将調用程式裝載器program loader(通常簡寫為loader),它負責将代碼.EXE檔案中抽取出來并将其裝載到記憶體中。然後程式裝載器将通知中央處理器記憶體中第一條需要處理的指令的位址,也稱為程式入口點(如C/C++主函數main()的入口位址)。接着,程式就可以執行了。

簡單來說,這基本上就是計算機科學背後的哲學體系:将問題和算法轉化成為進階語言代碼,将進階語言代碼轉換成為低級語言代碼,讓低級語言代碼通過CPU加以執行,進而(有望)解決問題。

那麼了解腳本的運作過程就簡單了不少。與上述過程不同的是,腳本編譯器不能将源碼轉化為任何類型CPU的機器碼,因為這些代碼不會直接運作在CPU上,而是通過虛拟機運作的。雖然虛拟機和CPU非常相似,但它們二者仍有一些不同——虛拟機是軟體而CPU是硬體。它們的基本工作是擷取下一條将要執行的指令,分析指令将要完成的功能并執行該條執行。不同的是虛拟機隻能識别它特定的那種彙編語言(位元組碼)。

虛拟機的另一個特性是,至少在遊戲腳本程式設計的内部,它通常不是孤立的。相反,它是一個特殊的“模型”,可産生其他程式或與之內建。與CPU很相似,CPU也需要和主機闆、RAM等東西內建,單純的CPU是沒有什麼用處的。與虛拟機內建的程式被稱為主應用程式,我們最終也是為這個程式編寫腳本。

是以,一個腳本系統并不僅僅隻是為自己設計一個進階的、C/C++形式的語言,它還需要創立一種低級的彙編語言,即虛拟機器碼。

除了一套運作環境,虛拟機還為運作的腳本和主應用程式提供一個通信通道,即互動界面

主應用程式會提供具有一組功能函數的運作腳本,這些功能函數被稱為API

6.腳本系統的基本類型:

時刻牢記:大型而又複雜的特征清單看起來的确很好,但是當你不需要它們的時候,它們就隻會使你的程式結構淩亂,運作緩慢

①面向過程的語言系統和面向對象的語言系統:

這些系統使用進階的、結構化的或者是面向對象的語言來編寫腳本,這些腳本随後被編譯成能夠在虛拟機内部運作的虛拟機器碼,或者不加編譯,直接由解釋器解釋執行。系統使用的虛拟機或解釋器将會與主應用程式相內建,使得主應用程式可以調用腳本并和腳本進行互動。特點是非常靈活、自由,事實上可以适用于任何一種主要的計算任務

②基于指令的語言系統:

通常會像LOGO語言一樣,非常具體,它們全部由接受一個到兩個參數的針對具體程式的指令組成。如下面一段代碼:

MovePlayer 10, 20
PlayerTalk "Something is hidden in these bushes..."
PlayAnim SEARCH_BUSHES
PlayerTalk "It's the red sword!"
GetItem RED_SWORD
           

形成這個虛拟語言的指令都是非常明确的,它們都針對于非常具體的某一種RPG遊戲,都針對于非常具體的某一種RPG遊戲,很少甚至沒有什麼靈活性。

③動态連結的子產品系統:

當一個複雜的腳本在一個虛拟機上運作時,它的運作速度明顯要比純粹的機器碼直接在CPU上運作的速度慢得多。為了避免這個問題,許多遊戲采用動态連結腳本子產品。即一些C/C++程式段,這些程式段像遊戲本身一樣被編譯成純粹的機器碼,并且在運作時被連結、載入。由于使用常見的C/C++語言編寫,運作速度特别快,而且功能強大。

動态連結子產品通過遊戲向它們提供的API接口和遊戲進行通信。通過使用這種API接口,子產品可以檢索和修改遊戲的狀态資訊,進而從外部控制遊戲。通常被稱為mods

④編譯型代碼和解釋型代碼:

編譯型代碼就是那些稱為機器碼的一系列指令,它們就是通過将那些人類可以識别的代碼轉化而成的機器可以識别的代碼。

解釋性代碼是通過解釋器的程式來運作的。解釋器是很難實作的,一方面,它們幾乎要具有編譯器所有的複雜語言分解功能函數;另一方面,它們又要足夠迅速地解決這些問題進而能夠達到實時的效果

⑤現有的腳本程式設計方法:

(1)Ruby:

是一種完全面向對象的腳本語言,側重于系統管理方面的工作。具有一系列很好的進階特性,如垃圾收集、動态庫裝載,以及多線程。解釋型語言

(2)Lua:

Lua是輕量級的強大的擴充程式設計語言,面向過程的腳本設計系統。最明顯的特征是可以用它編寫的程式來對它自身進行拓展。是以,它的核心代碼很少,通常是使用者來實作附加的特征。也能很好地和C/C++進行互動

(3)Java:

Java虛拟機,JVM,可以很容易地調用本地接口JNL和C/C++程式進行結合,因為它廣泛應用于專業級的電子商務中,是以JVM對于編譯腳本來說是最優的多線程運作環境,且這門語言本身也很靈活而且高度的面向對象

繼續閱讀