遊戲動起來——Inform教程(三)

遊戲動起來——Inform教程(三)

互動小說歸根結底是在講故事,人物則是故事最重要的組成部分,也是最複雜的部分。本期讓我們一起探討如何在Inform中創建可信的遊戲人物。

文|夢飛

創建NPC

  遊戲中的人物分成兩大類:玩家角色和非玩家角色。玩家角色譯自英文Player Character,簡稱PC,它是遊戲中由玩家直接控制的角色,通常是故事的主角,一般只設置1個角色(為敘述方便,下文中“玩家角色”和“PC”將混合使用)。非玩家角色譯自英文non-Player Character,簡稱NPC,它是遊戲中不受玩家控制的那一類角色,通常有很多個(下文中“非玩家角色”和“NPC”將混合使用)。NPC在遊戲中的作用有很多,比如提供信息、出售物品或參與戰鬥等。NPC既可以是友好的,也可以是敵對的,用來推進故事發展,或者為玩家提供挑戰(圖1)。

  圖1 遊戲中的各種人物角色

  一般來說PC角色無需我們手工創建,當在Inform中至少創建了一個房間並運行遊戲後,PC也就自動生成了。接上一期的實例,點擊“Go”按鈕運行遊戲,然後輸入命令“showme”,將得到一個列表(圖2)。

  圖2 Showme命令執行後的結果

  Showme是一個內部測試命令,主要用來幫助開發者查找遊戲問題,它在最終發行的遊戲中是不會出現的(也就是遊戲玩家不可能接觸到此命令)。執行Showme命令後,將會得到當前房間中所有對象的一個清單,可以看到,除了我們明確創建的“琴桌”、“香爐”和“瑤琴”之外,還多出一個person(人)類型的“yourself(你自己)”對象,這個就是代表玩家自身的玩家角色(PC)。

  PC可以自動生成,但是NPC就需要我們手動創建了。

  按照《畫壁》的故事設定,玩家(朱孝廉)因為被壁畫中的仙女吸引,才會“靈魂出竅”穿越到仙境的,所以在他醒來的臘梅小屋中應該設置一個小仙女NPC。創建方法很簡單,給小仙女取個名字“梅花仙子”(Wintersweet Fairy),聲明其為女性(Woman),出現在什麼位置(in the …),並對她的外貌進行一些描述(Description)。具體實現的代碼如下:

  Wintersweet Fairy is a woman in the Wintersweet House.“一名少女手拈梅花望著嫋嫋清煙出神,你的突然闖入讓她醒過神來。”. The description is “少女長髮垂髫,正拈花微笑,櫻唇欲動,目光如澈,神采動人。”.

  The printed name of Wintersweet Fairy is "梅花仙子".

  上面我們看到了兩種描述方法:一種是直接在聲明語句後面加雙引號括起來的描述文本,一種是通過“The description is …”語句添加的描述。現在運行此實例,看看兩種描述的區別(圖3)。

  圖3 創建遊戲NPC

  可以看到,直接引號括起來的描述,在玩家一進入房間後就自動進入“視野”中。而對少女的進一步描述,則需要玩家執行 “X”命令(此命令是examine的簡寫形式,兩者作用相同)檢查,然後才能顯示出來。

  深入Inform遊戲漢化

  上面的實例中,雖然“梅花仙子”有中文名稱,但我們執行檢查命令時,卻不得不使用她的英文名Wintersweet。如之前所述,Inform雖然能顯示中文,但是它的命令行卻只能接受英文字符的參數。要想徹底改變這一狀態,需要深入程序的源代碼(Inform是開源軟件)進行改變,目前我們還無法做到這一點,只能採取折衷的方法——在必須使用英文參數的地方,用漢語拼音代替。修改的代碼如下:

  Wintersweet Fairy is a woman in the Wintersweet House.“一名少女(ShaoNv)手拈梅花望著嫋嫋清煙出神,你的突然闖入讓她醒過神來。”. The description is “少女(ShaoNv)長髮垂髫,正拈花微笑,櫻唇欲動,目光如澈,神采動人。”.

  The printed name of Wintersweet Fairy is "梅花仙子(MeiHuaXianZi)".

  Understand "ShaoNv", "SN", "MeiHuaXianZi" and "MHXZ" as Wintersweet Fairy.

  在描述中添加漢語拼音標註,只是提示並無實質的作用,主要是為了告訴玩家執行命令時可以使用拼音名稱作為參數。而新增的“Understand … as …”語句則是告訴Inform,無論玩家輸入ShaoNv、SN、MeiHuaXianZi或者MHXZ,它們都和變量Wintersweet Fairy完全相等。此時運行遊戲執行“X”命令,無論玩家選用哪個參數,都會得到相同的結果(圖4)。

  圖4 拓展Inform的理解範圍

  注意,Understand之後as之前,是同義詞列表,需要用英文雙引號分別括起來,詞彙之間用英文逗號隔開。as之後就是真正要引用的變量名稱,它是不可以用引號括起來的。同義詞列表可多可少,根據自己的需要擬定即可。本例我們為了方便玩家輸入,提供了全拼和簡拼等多種方式(字母大小寫只是為了方便閱讀,實際運行命令時不用區分大小寫)。

  本例在修改之前,用中括號“[]”將原有的代碼括了起來,將它變成了註釋文字。這是為了方便萬一修改出錯,可以快速恢復到之前的代碼。新代碼確認沒有問題後,可以將舊有部分的代碼徹底刪除。

  通過Understand語句我們也可以將英文命令轉換成漢語拼音的方式,比如將“Examine”(即“X”命令)改寫成拼音“ChaTan”(查探),代碼如下:

  Understand “chatan [somebody]” as examining.

  添加這一行代碼後運行遊戲,此時輸入英文“examine shaonv”和輸入拼音“chatan shaonv”,效果是完全一樣的。

  上面的代碼有兩點需要注意。首先,玩家平時在玩互動小說的時候輸入的英文單詞都是動詞現在時(比如examine),而其在程序內部的表示則都是進行時的(examining),所以我們用Understand語句時引用的動詞變量一定要是進行時態。如look是looking,take是taking,等等。

  其次,互動小說中的動詞命令有三大類,一類是單獨一個動詞,如“LOOK”;還有一類是一個動詞帶一個參數,如“EXAMINE shaonv”;另外一類是一個動詞帶兩個參數,如“ASK name ABOUT something”。我們在用Understand語句關聯動詞時,如果該動詞需要參數則用中括號“[]”將參數變量括起來,就如上面的代碼所示。

  給互動遊戲添加序幕

  上一節中設置的規則最好在遊戲一開始時就係統地介紹給玩家,免得他們玩遊戲的時候不知所措。下面給出一段示範代碼:

  When play begins:

  say "[bold type]你從哪裡來?要到哪裡去⋯⋯[roman type][paragraph break]

  你在一個陌生的花園中醒來,已經完全忘記了自己是誰。你隱約感覺到自己的身世之謎就隱藏在這座迷宮一般的房間裡。你需要找出所有的記憶碎片,才能拼出你完整的過去,走出這座迷宮。[line break]

  遊戲過程中你可以輸入各種命令來觀察、檢查、聆聽周邊的環境,也可以進行拾取、打開、放置、移動、解鎖、攀爬、跳躍等操作,還可以與NPC對話以獲取信息。支持英文、中文(拼音)命令,不區分字母大小寫。[paragraph break]

  英文命令⋯⋯中文命令⋯⋯命令含義[line break]

  LOOK⋯⋯KanKan⋯⋯[bold type]看看[roman type]周邊的環境[line break]

  LISTEN⋯⋯TingTing⋯⋯[bold type]聽聽[roman type]周邊的聲音[line break]

  EXAMINE it⋯⋯ChaKan it⋯⋯[bold type]查看[roman type]對象(人或物)的狀態[line break]

  TAKE it⋯⋯ShiQv it⋯⋯[bold type]拾取[roman type]物品[line break]

  OPEN it⋯⋯DaKai it⋯⋯[bold type]打開[roman type]房門、窗戶、箱子等[line break]

  EAT it⋯⋯Chi it⋯⋯[bold type]吃[roman type]東西[line break]

  DRINK it⋯⋯He it⋯⋯[bold type]喝[roman type]東西[line break]

  TALK name ABOUT something⋯⋯TanHua⋯⋯和某人[bold type]談話[roman type][line break]

  …

  [paragraph break]"

  Understand "kankan" as looking.

  Understand "tingting" as listening.

  Understand "chakan [somebody]" as examining.

  將上述代碼放置到程序最開始的位置(自動生成的第一行代碼下面),遊戲運行後,玩家首先看到的將是遊戲故事簡介和玩法介紹(圖5)。

  圖5 給互動遊戲加入序幕

  “When play begins”是Inform中的一個特殊語句,用於定義遊戲開始時遊戲世界的初始狀態。這個語句用於設置遊戲的初始條件,例如玩家的起始位置,遊戲世界中出現的對象,以及任何其他需要設置的初始條件。這個語句還用於定義遊戲開始時應該應用的任何特殊規則,例如玩家的初始庫存或玩家可能擁有的任何特殊能力。本例中,我們只是簡單用來呈現一段序幕描述,接下來的幾條Understand語句,主要是將中英文命令對應起來,讓它們起到同等的作用。

  另外,Inform中,中括號“[]”有兩種應用場景,當它獨立使用時,括號起的是註釋作用,如同C++程序中的“//”或“/*…*/”。而當它出現在雙引號括起來的文本中的時候,則表示各種變量(替代字符)。比如上面的代碼中,“[bold type]……[roman type]”表示中間的文本要以粗體顯示,“[paragraph break]”表示段落符號,“[line break]”表示換行符。

  Inform對象的屬性與互動

  Inform7對象是存在於遊戲世界中的實體。它們可以是物理對象,如劍或椅子,也可以是抽象概念,如角色或位置。上一期我們創建的四個遊戲對象,其中臘梅小屋是Room(房間)類型,它就是屬於抽象的位置概念。另外,瑤琴是Thing(事物)類型,琴桌是Supporter(支撐)類型,香爐是Container(容器)類型,這幾個都屬於物理對象。本期我們創建的NPC則是Woman(女性)類型的抽象角色。這些不同的對象類型到底是如何組織到一起的呢?

  類概念在其他編程語言中稱之為Class(如C++和Java中),而在Inform中則稱之為Kind。Inform內置的類總共有16個,按層級樹的形式組織到一起,依次具有繼承關係,其中根類型為Object,其下分Thing、Room、Region和Direction四大類,而Thing類下面又有更多的細分子類。 

  Object作為根類,擁有最一般的屬性,比如文本屬性的打印名稱(Printed name),它下面的所有子類全都繼承了這一屬性。其實我們前面漢化的一個很重要的技巧,正是運用了這一屬性。比如我們為了讓房間顯示中文名稱,應用的代碼是:

  The printed name of the Wintersweet is "臘梅小屋".

  這一條語句就相當於C++代碼中的:

  Wintersweet.printedName = "臘梅小屋";

  每一個類在繼承上層類的屬性基礎上,還會添加自身的特色屬性,比如Room(房間)具有位置屬性,還有明亮和黑暗屬性(Lighted/Dark),如果將一個房間設置為暗屬性,那麼玩家進入這一房間後就會什麼也看不見。再比如Container(容器),可以設置打開或關閉屬性(Open/Closed),而玩家揹包(Player's holdall)作為容器的子類,同樣繼承了打開或關閉屬性。本期我們創建的NPC為Woman(女性)類別,它屬於Person(人)的子類,繼承了人這一類的性別屬性。而在Person這一類下面,還有Man(男人)和Animal(所有非人類生物)等子類(圖6)。

  圖6 Inform中的對象類的結構關係

  不同類別的對象,對玩家的行動可能會有完全不同的反應。比如玩家要執行Tell(聊天)這個動作,物理類的“琴桌”直接禁止這一操作,因為桌子是不可能傾聽人說話的。而作為人類角色的“梅花仙子”則是能夠“聽到”玩家所說的話的(只是還不熟,她可能不會理睬你):

  >tell zhuozi about my story

  You can only do that to something animate.

  (你只能對有生命的東西這麼做)

  >chakan shaonv

  少女長髮垂髫,正拈花微笑,櫻唇欲動,目光如澈,神采動人。

  >tell shaonv about my story

  This provokes no reaction.

  (你的話沒有引起任何反應)

  圖7 不同類別的事物對行動做出不同的反應

  和NPC互動對話

  上面的示例中出現了兩個問題,一個是NPC對玩家所說的話毫無反應,另外一個就是系統的自動回覆用的還是英文。這可以通過“Instead of …”語句替代Inform原有的動詞反應來解決。下面是一段示範代碼:

  Talking to is an action applying to one visible thing.

  Understand "He [someone] LiaoTian" as talking to.

  Instead of talking to Wintersweet Fairy:

  say "[one of] [line break]'姑娘,在下冒昧打擾,這廂有禮了!' 你有些不安地上前招呼。[line break]'公子是哪一位?'少女從沉思中醒來略有些吃驚,‘如何到了這裡?’[line break]按G鍵繼續⋯⋯ [or] [line break]'這個⋯⋯' 你有些尷尬,'正是在下不解之處?'[line break]'公子此話何意,你從何而來,向何處去,豈有不自知之理?' 少女露出懷疑的神情。[line break]按G鍵繼續⋯⋯[or][line break]'姑娘莫要誤解。在下此刻只覺得昏昏沉沉,確實想不起經歷了何事。' 你連忙解釋,'懇請告知,這裡是什麼所在?'[line break]少女見你不似撒謊的樣子,於是答道,'此處乃是百花園。'[line break]按G鍵繼續⋯⋯[or]當你再想問些什麼的時候,少女卻不再理睬你⋯⋯[stopping]". 

 圖8 遊戲運行過程中與NPC的對話

  上面代碼中,“Talking to is an action applying to one visible thing.”語句聲明“Talking”是一個動作以及它的應用範圍。“Understand”語句定義了動詞Talking的同義詞,“He [someone] LiaoTian”是“和…聊天”的漢語拼音。接下來的“Instead of talking to Wintersweet Fairy:say …… ”預先定義了和NPC的對話。[line break]定義了換行符,而[one of]…[or]…[or]…[or]…[stopping]等幾個參數則是將對話劃分成了幾個段落,玩家按G鍵可以翻閱對話。

  圖9 Inform語法規則示例

  帶著NPC“闖天下”

  Inform中還可以創建多種對話方式,限於篇幅不再詳述。最後我們再介紹幾個讓NPC在遊戲世界中移動的方法。

  在遊戲源文件中添加以下代碼:

  Every turn:

  let D be a random direction;

  try Wintersweet Fairy going D.

  Inform中的“Every turn”語句用於創建在每個回合開始時執行的規則。此語句可用於創建各種效果,例如更改遊戲狀態、顯示消息或執行其他操作。在這段代碼中,每經歷一個回合(玩家執行一個操作),系統便會隨機選擇一個方向(D),然後嘗試讓梅花仙子(Wintersweet Fairy)朝這個方向移動(隨機性的不一定成功)。這段代碼可以用於遊戲中模擬角色(梅花仙子)隨機探索地圖的效果。

  讓NPC在地圖中移動的方法還有很多,比如下面這段代碼:

  Every turn:

  if the location of Wintersweet Fairy is not the location of the player:

  let the way be the best route from the location of Wintersweet Fairy to the location of the player;

  try Wintersweet Fairy going the way;

  otherwise:

  say "'那麼就隨我來吧,' 少女說道,'我幫你一起尋找記憶碎片。'"

  每經歷一個回合,系統便會檢測NPC的當前位置,如果她和玩家不在同一個房間,就會調用“Try”語句將她移動過來,這樣玩家走到哪她就跟到哪了。

  發佈遊戲

  Inform教程到本期就暫告一段落了,我們介紹了互動小說基礎知識,以及Inform中游戲地圖的創建,還有如何添加遊戲對象、遊戲NPC,如何創建互動等,尤其針對Inform互動遊戲漢化的問題,提供了一些解決方案,算是拋磚引玉。

  Inform為互動小說編程提供了豐富的功能,本系列教程所涉及的內容不過是九牛一毛,有興趣的讀者可以到官方網站https://ganelson.github.io/inform-website/做進一步的瞭解。最後,我們介紹一下發布的問題,操作非常簡單,在Inform中編制好互動小說後,點擊工具欄的“Release”按鈕就可以了。最終生成的是擴展名為gblorb的程序文件,可以在Glulxe之類的解釋器軟件中打開運行,也可以上傳到支持互動小說的網站上,供世界各地的玩家們在線遊戲。

  圖10 互動小說編譯成功後的報告

原文刊登於2022 年 12月1 日出版《電腦愛好者》第 23 期

END

更多精彩,敬請期待……

Scroll to Top