主題

【Unity 自製工具分享 附demo專案檔】 製作RPG對話系統,用tag語言處理劇情邏輯

趴趴鼠Loading | 2020-11-22 17:40:18

前言:
我對稱它為"語言"都覺得有點不適應,我知道這不算是語言,更何況我認識的有人對"語言"這名詞很講究,連html這種tag語言都覺得嚴格上來說不是語言,我講出來的時候被嗆爆,總之我就怕被罵啊XD


*巴哈創作格式一直跑掉= =  能的話希望直接看github的


先上demo 專案影片:

說明:
整個專案除了玩家、攝影機移動之外都是用這個工具進行的,(例如對話、切換場景、劇情邏輯判斷)。

一開始選擇語言,第一個場景示範切換場景。
第二個主要遊戲場景中主要的劇情邏輯有:
還沒跟兒子對話前,跟村民的對話劇情是日常的閒聊話。
第一次跟兒子對話,他會要求媽媽拿玩具給他,這時候跟小朋友的對話內容改成要求玩具,拿完玩具再跟小朋友對話則變成閒聊話2。
玩具給兒子後,他要求媽媽找5顆糖果給他,在糖果沒收集齊全前,跟兒子的對話都會說"不夠,目前只有xx顆糖果"。其中xx是動態變數。村民的對話也變成要求糖果,拿完糖果後對話變成閒聊2。

每個可對話物件都是在自己1個文本裡判斷的。
範例 兒子的文本:

其他文本檔案在

專案下載與語法說明 連結


以下簡略說明一下語法:

  • 用<story> </story> 開頭、收尾。
  • 非tag以外內容需要出現<>, 則加上 \ (跳脫符號) 在<>前。  (我regex能力的最大限了)
例如<l > \<color=red\> Red content text! \</color\> </l>

<l> </l>

"l" 代表每一個對話。

<if> </if>

達成條件才會進行裡面的內容。 temp_a、temp_b是自定義變數,下面會說怎麼設定。
範例:
<if (temp_a ==1)||(temp_a ==1)></if>        
<if (temp_a<=1) && (temp_b==2) && (temp_b\>=2) && (temp_a!=1)>       
<if(temp_a ==1)></if>

<Select> </Select>

創建選單,只能搭配<opt></opt>的tag。  set是設定變數用的,可以不加。
範例:
<select> <opt set("opt"=1) >  dialog content </opt> <opt set("opt"=2) > dialog content2 </opt> </select>

主要的tag就這些,接下來是參數。


animation

尋找場上的物件名稱為"gameobjectName"的物件,並播放動畫名稱為"clipName"的動畫,範例:
animation(gameobjectName,clipName)

audio

尋找場上的物件名稱為"gameobjectName"的物件,並設定該AudioSource的狀態。
範例:
audio(gameobjectName, stop) audio(gameobjectName, play) audio(gameobjectName, pause)

title

對話框的標題。
範例:
title ="Jack"


by

如果對話框是浮在物件上的風格,用by="物件名稱"找尋該物件。
範例:
by ="gameobjectname"

img

如果對話框是像galgame那樣固定畫面的,我預設有分左、中、右,3個image欄位。圖片要放在Resources資料夾下。 left/ middle/ right是在prefab裡面指定的。
範例:
img(left,"圖片路徑")
img(middle,'img2')
img(right,/imgfolder/img1)

set

設定變數。若是"temp_"開頭的,會放在暫存的字典裡,對話結束就清除,方便用來做一些無關緊要的選擇性對話。 反之變數會存在整個遊戲(static),但遊戲關掉就沒了,所以請記得處理RPGCore.cs 裡面的StoryRecord存讀取。可以用我這個存讀檔的腳本(字典不能存json,只能存bin)。
範例:
set(" temp_a"=1)        
set("temp_a"= "temp_b")       
set("temp_a"="temp_b" + "temp_a")

$[]

讀取自訂變數。 例如 $[temp_a]。
下面這個範例兩者是一樣的,
<select>
<opt set("temp_a"="temp_b") > set temp_a= b</opt>
<opt set("temp_a"=$[temp_c]) > set temp_a= c </opt>
</select>
但若要在對話內容裡讀取變數的值請用後者,例如:
<l> temp_a = $[temp_a]</l>   

enable / disable( obj1,obj2,obj3.....)

開啟或關閉物件。 用find找並加入紀錄中,所以一開始的物件應都設開啟狀態,透過一個開始的文本把物件關閉並記錄,之後才能再開啟。
例如在這個文本裡單純就是:玩家經過觸發,但因為沒有內容所以馬上結束。
<story disable(toy2)></story>

其他

想擴充自己的標記的話,請照著這個格式:functionName( arg1, arg2 ,arg3,arg4.......)在StoryReader.cs裡面的ReadArgs()方法裡有個標註 YOUR OWN SCRIPT FUNCTION 的地方處理。
arg在一行內可以加很多個,例如:
<l
title="Dog"        
by="Dog"       
img(right,"Img/Portrait/p1")
effect(shake)         
animation(obj1,clip_hi)        
animation(obj2,clip_ya)        
animation(obj3,Idle)        
> woof woof</l>






單純工具主要有RPGCore.cs , StoryReader.cs 和 OptionButton.cs。

  • RPGCore主要放Regex和方法,不用實作。要新增語言請找到public enum Lang。然後在Resources資料夾下新增個對應名稱的資料夾。
  • StoryReader 主要處理arg和tag解讀與和UI的互動,放在對話框prefab。用StartConversation()開始對話。
  • OptionButton 選像按鈕prefab裡面,單純回傳告知StoryReader 選擇了什麼。只有一個click方法呼叫 StoryReader.ReturnInput(gameObject.name);

  • 我多個StoryTrigger.cs是用來觸發對話的。



目前就這樣,還有些小bug和擴充的內容待加強。



133 巴幣: 122
教授加博士先生
給點小建議@@ https://github.com/Lontoone/MyUnityToolLab/issues/1
2020-11-26 18:05:28
趴趴鼠Loading
感謝! 請問這樣做是為了省下每次new一個WaitForFixedUpdate() 的功夫嗎?
2020-11-28 20:58:24
教授加博士先生
對的,那個東西都是一樣的,沒必要每一個frame去創造,除非你是要WaitForSeconds(n), n會變動才要重新創造
2020-11-28 22:47:00

更多創作