2018年6月2日 星期六

「傘之下」開發記錄—20

最複雜的對話系統講完了,接下來是「最硬來」的劇情系統ww
現時的劇情系統,算是依附在對話系統下的
當對話進行到一定程度時,會把UI關掉,然後進入劇情動畫
這個「一定程度」的意思:
就是對話系統中有提到的,「對話後的動作」裡的選項(After Talk)


現時的做法,是先宣告一個 interface
然後 interface 裡再設立一個void (VoidEvent)
每當執行VoidEvent一次,就會自動執行對應動作
直接看上圖的例子,VoidEvent裡其實只有兩行
一個是 int 增加,另一個是呼叫下一個 void(會被 int 影響)
所以每按一次按鍵,就會執行不同的 void,達成一個劇情動畫流程

在每個 void裡,會寫入對應動作
比方說攝影機移動、生成特效之類的
……還有因為當初硬來,甚麼都沒設定好,所以還要控制對應的bool 參數w
大致上整個系統長這樣子,只要和對話配合好的話,台面上還是不太有問題就是##


總而言之,這是個我自己都會寫到亂掉的系統XD
要優化的話,除了把參數都事先設定好外,應該還會把經常會用到的動作寫成一個void
比方說攝影機移動:
void CameraMove(Vector3 _target) {...}
很可能會寫成這樣,到時只要呼叫這個 void,並傳送一個座標過去就行了
作業上會快速方便許多

2018年6月1日 星期五

「傘之下」開發記錄—19


上一篇傳送對話資料後,會來到下面這組腳本
這腳本用來控制整個對話UI,包括UI的淡出淡入、文字和圖像切換等
在啟動對話UI後,會先讀取名字,然後判斷他是出現在上/下對話框
先假設現在使用上對話框。如果對話框沒有顯示,則讓對話框淡入(設定上,還沒有對話出現的對話框,在起始不會出現)
淡入後,會把名字和頭像進行切換。而「對話內容」由另一個void管理(下一張圖)
當上下對話框都已出現時,「沒在說話的角色對話框」會變為暗灰色
最後,在該段對話結束後,會判斷有沒有劇情或UI

更改對話內容的void裡,同樣會先判斷這是上/下對話框,之後才更改對話內容
另一個比較特別的是:Replace \\n , \n
先說一下,\n是用來自動換行的。但輸入這個語法後,Unity好像會自動把 \n 變成 \\n
所以這時需要手動把它調回來"XD(把 \\n 變回 \n)

不過說實在,以我自己的實力不可能知道這種東西的ww
那時在查資料時剛好看到別人的教學,就拿來用一下
可惜的是現在找不回那教學,沒辦法附上連結_(:3」

能切換相關資料,亦要防止對話互相干擾
因為現在的設定是按下相關按鍵(比如Z)後,對話就會跑下去
假如沒設限制,那在「對話一」時按下Z,「對話二」也會跟著跑。到時所有對話都會亂掉

現時遊戲中的防禦機制,會和遊戲物件的Component有關,所以先解說一下:
.控制對話UI用的腳本是掛在UI上的
.每段對話分開成不同GameObject,裡面掛著對話資料(上一篇文的腳本)和觸發對話腳本(等下會講到)

那麼,我是讓所有對話在一開始時都 inactive的。當觸發對話後,對應的物件才變成active
至於怎樣才做到這點,那就是宣告一個陣列來抓取子物件(GetChild)
每當要觸發對話時,腳本都會傳送自身子物件的位置,讓子物件啟動
宣告子物件位置的程式碼,則會在下幾張圖中說到

在那之前先繼續UI操控的部分:
當全部對話結束後,會呼叫ResetImage,把相關參數都重置(為免下次啟動UI時,會有殘留的資料出現)
然後對話框便淡出


UI控制大致上是這樣,然後今天就一氣呵成的「觸發對話」也說完
當玩家碰到觸發器後,會自動開啟對話UI
上圖的最後一句,就是用來傳送子物件位置的程式碼(用Debug.Log測試過,它會回傳一個數字(int))
除此之外,在觸發器腳本上,有設置一個下拉選單,選擇是哪一種觸發方式
現時分別設置成:
.對話完後就不會再重新對話
.同樣對話完就不會再觸發,但若對話裡內含劇情動畫的話,就要選擇這方式
.對話後離開範圍,再次踏進觸發範圍時會被重新觸發

目前主要用到的就這三種模式……其實還有寫一個「按下按鍵後才觸發對話」的模式,但因為美術部分未準備好,於是先把它拿掉_(:3」

最後啦,當在離開觸發範圍後,會回傳一個「-1」給UI的防禦機制
這個「-1」在void裡已設定好,代表讓GameObject inactive
與此同時,對話中的參數也重置,以免出Bug


 ……第一次寫這麼長w
認真來說,這個系統的複雜度應該可以分3、4篇
但那樣的話感覺會變成教學文——我還沒有實力這那種東西
腳本內一堆誤人子弟的程式,被看見就不好了XD
所以我一直以來都只是講講基本架構。一來用於整理自己思緒,二來……也或多或小希望這些邏輯概念刺激到別人啦 ((自肥ww

總之,感謝看到這裡
如無意外,程式架構的東西還有三篇就完了
還在想之後要不要寫美術心得_(:3」

2018年5月31日 星期四

「傘之下」開發記錄—18

發生Boss戰前,通常會段劇情動畫,營造一種Boss要來了Σ(*゚д゚ノ)ノ的感覺
所以接下來幾篇,就是要來說遊戲中的「對話」和「劇情」系統
這兩個系統,是這遊戲裡最複雜,同時也是最不滿意的東西ww
硬來的東西根本多到不能直視

嘛總之,先來看一下遊戲的對話長怎樣:
對話框是分成上下兩個,所以要操控的東西也變成兩倍……
不止啦www
為了避免各種bug,要調整的東西說不定是2.5倍
為甚麼當初要設定這種對話視窗呢?這就要問企劃
……咦,企劃就是我耶ww((自M

要寫成這個對話UI,除了要編寫怎麼控制外,還要建立一個基本的架構,來輸入對話資料:
在對話中,要更改的資料有:
.名字
.人物頭像
.說話內容
.要使用上/下對話框
.對話結束後的行動

這五個列為一個class,再在公開面版中隨時修改
然而,因為遊戲中有時會出現對話分支,因此再以一個class包住這個class "XDD
當出現分支時,選擇哪個class的資料

架構建立起來,再由別的腳本調用後,在面版中大概長這樣:
一個不太人性化的資料輸入版面w
有時亂到自己都不太懂Orz


Anyway,有架構後,就用另一個腳本來讀取資料
在腳本啟動時,會先偵察有沒有分支對話
沒有的話直接讀取對話資料,有的話則由其他腳本控制要讀第幾個分支
然後:
當按下對應按鍵時,會呼叫ClassChange
ClassChange內,則傳送「上/下對話框、名字、頭像、對話後動作」這四筆資料,給予TalkUI腳本(控制UI的腳本,會在下篇文提及)
最後再呼叫SayChange
SayChange內,會傳送對話內容給TalkUI腳本
但在傳送之前會有一個判斷式:有沒有超出對話行數
在腳本內宣告一個 int(SayNo)……SayNumber的簡寫啦不是另一意味上的SayNo  XD
當每讀取對話一次時,這個int就+1。假如SayNo大於對話行數時,就會自動跳到ClassChange,更改其餘四項資料——畢竟超出行數的意思,就是指該名角色暫時說完話
跳到ClassChange的同時SayNo變回零,同時CharacterNo+1。CharacterNo是用來偵察「有多少段對話」用的,和SayNo異曲同工
當CharacterNo超出「對話段數」時,則完結對話,呼叫ResetNumber
ResetNumber裡,除了把SayNo和CharacterNo重置外,一堆和對話(劇情)相關的參數也重置,並關閉對話視窗
因為在進行對話時要限制玩家移動
而且進行對話時可能會出現劇情……這部分也要限制
因此出現了一堆bool參數w

說了這麼多,這部分只是「傳送資料」而已
對話UI裡的更動,還需要另一個腳本協助才能達成
這部分就留在下篇吧ww

2018年5月30日 星期三

「傘之下」開發記錄—17

上一篇結尾是在講判斷Boss動作的bool
這次會貼上那段void,所以應該會更清晰一點
(沒貼在上篇,是因為在這裡的關聯性更大)

在進行判斷之前,要先講到為甚麼Boss會中止行動
當Boss受到玩家一般攻擊時,會進行防禦
在打了第一下之後,為免Boss被攻擊多下,多次觸發程式產生Bug,所以會取消攻擊判定
接著偵察被打的方位,以免防禦錯方向
防禦時會停止其他協程,然後呼叫反擊協程

反擊開始時,會設定攻擊強度(為了把玩家推開),同時更改攻擊範圍
最後則是呼叫void,繼續Boss的行動



除了一般攻擊外,Boss會被掉落的果實砸傷(或者說,Boss只會在受到果實攻擊時才會扣HP,一般攻擊時會完全防禦)
當被果實打中,會優先播放受傷動畫
然後和防禦反擊時一樣,呼叫重新行動的void


最後這個void,其實也不是甚麼厲害的東西www
就只是根據bool的開關,判斷呼叫哪個協程而已


Boss的所有行動大概就是這樣
是有些小細節的東西沒有貼出來啦,因為那些都是沒優化過硬來的東西(黑歷史)

最後再補充一下,上面幾張圖有看到個名為NextSkill的bool
那個……是沒用處的東西"XDD
因為BossAI是整個遊戲中第3個寫的腳本,是超早期的產物
雖然中途有優化過一次,但有些東西忘了刪掉_(:3」
你就知道我當初多亂來

2018年5月21日 星期一

「傘之下」開發記錄—16

小怪AI講完,當然要來個大Boss (`・ω・´)
然後因為Boss的程式太長,還是會分個兩篇就是

首先先說說Boss的攻擊模式
牠的主要攻擊為「打地板」和「高速旋轉」,這兩套動作之間的循環
兩套動作都是用協程計時,不同時間播放不同動作,組合起來才是一整套攻擊

打地板的部分:
.先是重擊地板時(攝影機同時晃動),攻擊判定範圍會增大
.果實從樹上掉下(果實自帶「可擊飛物品」腳本,以及一個控制動畫用腳本)
.接下來準備去下一組動作
(HitFloor和Rolling的bool在文末解說)


高速旋轉的部分:(Part1)
.Boss會在空間裡左右旋轉,為了避免旋轉時超出範圍,因此一開始先宣告好一個位置,用以旋轉後固定座標
.更改攻擊範圍,同時取消旋轉期間的被攻擊判定(以免被打中出Bug)
.把Boss的碰撞器更改為觸發器,重力也改為零。會這麼做,是因為用碰撞器的話,移動途中擊中玩家時會有一個「推擠」,Boss也就不能準確地移動到位置上
.把設定都弄好後,最後就讓Boss進行位移


高速旋轉的部分:(Part2)
.在旋轉途中,Boss會有一個「跌倒」動畫,這時再往前滑行一小段(這都是用時間或數值來調整,有沒有更人性化的調整方法現在還在測試)
.滑行停止後,Boss的位置固定在設置好的座標上
.恢復碰撞器及重力的數值,以及恢復被攻擊判定
.旋轉後要面對玩家,因此會有一個Flip的效果


兩套動作大致這樣,最後來講HitFloor和Rolling的bool
在設定上,Boss被一般攻擊打中時會進行防禦反擊(另一套動作),那時會停止「旋轉」和「打地板」的動作,當反擊後才重新回到這兩套動作循環裡
那這個「重新回到循環」,就是靠bool來決定
Rolling = true的時候,反擊後會切入「旋轉」的動作再次展開攻擊。反之亦然
所以當一套動作已經完成了的時候,就會開關對應的bool,讓Boss繼續行動

2018年5月20日 星期日

「傘之下」開發記錄—15

通用參數之後就是敵方的個別AI
因為地圖上的一般敵人有兩種,所以只挑其中一隻來講
結構大致上是差不多的030

調用通用參數裡的Dis,當進入攻擊範圍時就攻擊,當距離太遠時則待機
為了避免「動作」和「移動」不同步,所以「待機」和「跑步」時都設定一下速度參數

第一張圖是基本的移動,接著第二張圖算是輔助性質的void
當Hurt時:中止其餘動作,優先播放受傷動畫。呼叫StopMove。
(然後我現在才發現圖片中的StopMove多了一個 i 字"XDD)
當StopMove:敵方強制停止移動。假若HP大於0時,呼叫MoveAgain
當MoveAgain:讓敵方重新開始移動,回到第一張圖

基本上敵方AI都會是類似的模式
理論上這些行動都能分割成通用腳本,動作之間計時也能用AnimationEvent來做
但因為這是早期腳本之一,只能強硬地寫成這鬼樣子www
(沒錯雖然這篇文的發文順序較後,但卻是最早期腳本之一XD)

假如將來繼續開發下去,應該至少把「受傷」的部分分割出來
因為說不定會出現有「霸體」的敵人,這時只要把受傷腳本移除就好
不然每只敵方都從程式碼調整的話,開發時間說不定會長個兩三倍(笑

2018年5月16日 星期三

「傘之下」開發記錄—14

除了撿取「暗之花」外,打倒敵方亦會噴出花瓣
算是半延續上一篇,這次主題是敵方AI
……這兩者其實關係不大,只是想讓文章更有連貫性才這樣寫XD ((硬來

敵方AI,主要分為兩類腳本
一個是「通用數值」,另一個是「個別行動」
這篇首先會先講「共用」的部分
每只敵人都有HP,不然打不死,所以明顯地這是個共用數值
除此之外,敵方的動畫器、碰撞器、剛體等參數亦是在這裡宣告,再在其他腳本中調用
在圖片中有看到名為Dis的參數,那是用來計算敵方和玩家的距離。畢竟每只敵人都有索敵範圍,當距離超過數值時,則執行別的動作。


上面是關於敵方自身的參數,而敵方攻擊的參數則另開一個腳本
會分開寫的原因,是因為敵方的攻擊模式不同,腳本掛上的物件亦不同
 (有的掛在攻擊觸發器中,有的掛在敵方本體身上)

攻擊強度,印象中在「玩家受傷」那篇中也輕輕提到。那是影響玩家彈飛距離的參數
這個腳本中較需要說明的是OnEnable和OnDisable的部分

依現在的程式碼,敵方在攻擊時會產生一個觸發器(SetActive),當這個觸發器碰到玩家時就會構成傷害(OnTriggerEnter)
而攻擊完畢後,SetActive = false,觸發器大小變為最小

會這樣設定,是因為自己測試時,不知為何敵方的第一擊會沒有觸發判定(可能是我設定出了問題吧_(:3」)
然後就只好讓觸發器大小重置,強制把它Reset一下


通用腳本大致上就這樣
因為這次是第一次挑戰分割腳本,所以分得很不漂亮ww
很多參數都有點硬來,有些該通用的參數也沒宣告好。總之要優化這腳本是個大工程
不過說實在的,如果沒有硬寫出來,也不懂得要怎樣優化就是XD ((經驗值up