1. 程式人生 > >Java版AVG遊戲開發入門示例[3]——指令碼引擎的製作及應用

Java版AVG遊戲開發入門示例[3]——指令碼引擎的製作及應用

 根據wikipedia的解釋:指令碼語言(Script language,scripting language,scripting programming language),是為了縮短傳統的編寫-編譯-連結-執行(edit-compile-link-run)過程而建立的計算機程式語言。此命名起源於一個指令碼“screenplay”,每次執行都會使對話方塊逐字重複。早期的指令碼語言經常被稱為批次處理語言或工作控制語言。一個指令碼通常是解釋執行而非編譯。指令碼語言通常都有簡單、易學、易用的特性,目的就是希望能讓程式設計師快速完成程式的編寫工作。

 對於指令碼語言,想必我們這些Java程式設計師都再熟悉不過,因為我們每天都在使用的Java,從嚴格意義上講也是一種解釋型語言——畢竟class是憑藉JVM進行位元組碼解釋才得以執行,故而長期被排斥於編譯型語言的門檻之外(但是,由於Java有編譯過程,若CPU支援位元組碼那麼class就可以脫離JVM限制,說Java是純粹的解釋型語言似乎也很不妥+︿+……期待java cpu普及中……)。

 在這個講解AVG遊戲開發的系列博文中,為什麼會提到指令碼引擎的製作及應用呢?筆者將在下面加以說明。



 在遊戲開發中,我們將無可避免的遇到很多重複性工作,比如角色A-Z的建立,物品0-n的編輯,對話x->+∞的新增等等——這些並不是需要複雜的編碼,甚至僅是單純的資料錄入,但由於資料量龐大,通常也會耗費相當的時間進行設定。
 
 如果讓您來做,要怎麼辦呢?以最直觀的方式來說,可能有些人會考慮硬編碼,直接編碼似乎能省去很多的麻煩事。

 OK,或許我們完全可以採過硬編碼方式解決問題,比如建立一個對應物品的class,而後為其新增各種對應欄位。

 那麼,假設現在我們在某個同人遊戲的開發中這樣幹了,並且將遊戲對外發布。三天後,問題來了,有人反映某物品——我虛構個名字,就叫[對抗網癮專用電棍]吧。有人反映它殺傷力太低,且對[網癮]狀態無傷害加成……嗯,我們考慮後認為有道理,怎麼辦呢?一個字——改,反正就是一兩行程式碼的事情,重新編譯,打包,釋出——簡單簡單。

 這下高枕無憂了吧?可惜,出乎我們意料之外的事情又再次發生,又過了兩天,有玩家反映我們的最終BOSS[真.紅色有角.磚家叫獸地獄鬼畜模式]的大招[終極網癮擴散]會將所有我方角色全體[網癮化],並且會持續投擲[對抗網癮專用電棍]攻擊我方角色,由於改版後的[對抗網癮專用電棍]攻擊力太強,並對[網癮]狀態有極強的傷害加成,我方角色很容易便會被BOSS團滅。
   
 你問我這下怎麼辦?硬編碼的還有什麼新鮮,只能再重新編譯……使用者就是上帝,上帝讓改就改吧……但是,儘管我們非常努力,卻總有欠缺在這個遊戲中存在,如果這樣改下去,三次、四次、十次、二十次|||,次次都需要重新編譯及釋出,就是再簡單也受不了了(做Java還好,搞C/C++的很多人都經歷過守夜等編譯結果吧……),恐怕最終此遊戲只好停止釋出,爛尾了事。如果你將每個物品都直接編碼到遊戲當中,那麼毫無疑問你將會陷入到噩夢一般的處境。

 為什麼我們不能選擇更加簡單的方式來處理呢?既然每次重新編譯、重新發布程式那麼麻煩,幹什麼不將配置放在遊戲外面呢?實際上,我們完全可以建立一個文字檔案記錄下所有物品引數,而每次更新時,我們僅需修改此文字檔案便可以改變整個遊戲中的屬性設定,再也不必事無鉅細都重新編譯、重新發布整個遊戲了。

 事實上,筆者之所以耗費筆墨寫這麼一個例子,無非是為了說明指令碼應用背後的基本準則——簡化軟體製作流程,避免硬編碼。


 指令碼可以使你真正在遊戲引擎之外編寫程式碼,然後再將這些程式碼載入到遊戲引擎之中並對它加以執行。一般情況下,指令碼是按照它們自己的語言格式進行編寫的,使用何種語法完全由指令碼引擎決定,指令碼就好像是執行在遊戲程式內部的小程式,它們的工作原理和其他的普通程式一樣,你可以使用一個普通的文字編輯器來編寫它們,然後在你的主程式中執行它們。指令碼通常使用它們自己的編譯器或者直譯器,它們對遊戲主引擎並沒有任何影響——除非你希望它這麼做。

 同時,遊戲和指令碼之間通常是分離的。你可以像載入圖形、聲音,甚至早期的物體描述檔案一樣載入指令碼。但是,你並不能在顯示器上將它們顯示出來或者是通過喇叭把它們播放出來,取而代之的是,你可以執行它們,它們也可以和你的遊戲進行通訊,而遊戲也可以作出應答。

 簡單的說,如果我們將遊戲引擎和遊戲資料理解為人與積木,那麼指令碼就是用來搭建積木的圖樣;如果我們將遊戲引擎和遊戲資料理解為海洋與大陸,那麼指令碼就是通行在海洋與大陸間傳遞資源的貨輪。

 遊戲指令碼是遊戲引擎不可或缺的最佳搭檔。


 可能您會問,既然指令碼有這麼多的好處,想必製作起來很麻煩吧?

 關於這點,需要從不同的角度來加以考慮。如果我們以groovy、ruby、python、lua、angelscript這些已經成熟或接近成熟的指令碼語言為標準來看,那麼開發一種指令碼語言的確是件很複雜的事情,涉及到易用性、執行效率、安全性、資源回收、併發等等許多的“額外”問題,而且既然有groovy、ruby、python、lua、angelscript這林林種種,我們也大可不必耗費心智來重複發明輪子。

 但是,如果我們從與遊戲引擎互動,減少遊戲程式碼量的角度出發,那麼自己設計的指令碼與自己遊戲引擎間的藕合性當然會較第三方的指令碼為高,程式碼量也當然會更少,而且維護管理也更為簡便。況且交給指令碼來執行的部分,通常也不會對執行效率有太高的要求,我們與大師的差異,也僅僅是體現在決定成敗的“細節”方面,單就構建指令碼直譯器本身而言,完全算不得難事,甚至簡單到一兩個小時就可以搞定一個具有初步功能的指令碼引擎(別說指令碼,很多80後乃至90後的程式設計師都開始自己寫編譯器玩了|||……)。

 比如TJS(就是做吉里吉里那個)及RGSS (Ruby Game Scripting System)等,都是為了更加適應自身遊戲引擎而開發出來的遊戲指令碼語言。

 下面我將提供一個非常簡單的指令碼直譯器程式碼,主要功能被集中在一個Command類中。


 


 為了與遊戲相互動,此示例構建了一個順序執行的動態解釋型指令碼語言,其中每一行實際上就是一組獨立的命令,無論分支或者輸入輸出都僅在程式讀取到本行時才會活性化, 以此保證與遊戲主程式資料互動的實時性。

本例解釋的指令碼start.txt內容如下:

遊戲執行介面如下圖:

04

03

02

01

00


  ——————————————————————————————————————

 天氣很熱,寫不出什麼正經東西,由於最近[叫獸]很猖獗,隨便做點東西藉機踩踩它(昨天晚上寫的,因為太熱導致MV歸0,今天晚上才發出來|||)。

 不過這星期六、日兩天TLOH基礎部分應該也能寫完了,下週估計能發。總之是個AVG+RPG+SLG的開源遊戲,劇情部分就靠發到網上情大家幫忙新增∩ω∩,功能我會慢慢再補,所有對它的意見反饋最終都會歸結到Loonframework-Game這個框架(簡稱LFG)上去。