> 玩家角色腳本的分工結構
用事件系統做觀察者模式,讓程式的連接變單向的
這樣核心腳本就不需要知道有哪些次要腳本在運作,而且就算關閉次要的腳本,核心還是可以正常運作
分工主要分成四種部分
核心腳本 角色動作 PlayerCharater
使用狀態機的結構執行動作,並在狀態執行和狀態切換時調用事件
public Action<CharaterState, CharaterState> OnStateChangeEvent;
public Action<CharaterState, float> OnStateEvent;
玩家狀態 PlayerStatus
玩家的狀態資訊,並在角色動作觸發擊中事件的時候輸出傷害
角色動畫 CharaterAnimation
根據角色動作觸發事件時傳遞的資料,判斷要撥放的是哪個動畫
因為我們使用Spine動畫,所以沒有用到Unity Animator
角色特效 CharaterVisualEffect
根據角色動作觸發事件時傳遞的資料,判斷要使用甚麼特效
一開始專案的時後全部都寫進一個腳本,稍微動到一點就容易連帶到其他地方出錯
如果功能開始多了就應該拆成不同腳本,分工清楚的話個別的功能要修改就會變得更容易
> 玩家動作 PlayerCharater
為了讓玩家動起來流暢,也花了不少時間研究
玩家的動作是透過狀態機控制的,大概有六種State
(Launching, Falling, Charging, Diving, Failing, Rising)
動作是根據當前狀態的持續時間(t)和上下範圍,用Lerp計算的,有稍微加一點ease(t)
但就算有緩動,整體移動看起來還是很機械
所以我用 lerp(current, next) 讓移動更流暢,然後再加了不少緩衝和過度的State
整體感覺舒服多了,對比一下差距真的很大
但缺點就是實際的速度變不好控制,但應該不會有太大誤差
> 裝備系統的條件效果 EquipConditionAction
條件效果 Condition & Action
把條件和效果分開來寫,就不需要重複寫一樣的效果或判斷,用組合的編輯起來也比較輕鬆
條件: 當玩家攻擊敵人、當玩家觸發晶片
效果: 觸發爆炸、獲得速度、啟用護盾
組合條件和效果的方式是在遊戲一開始時會讀取資料
先把效果包進一個Action裡,再把它傳進條件判斷,當條件達成時就直接Invoke效果事件
第一次這樣嘗試,花了一些時間研究出的作法
但是一個等級只能有一種效果,而且一些改動數值的效果也做不到,所以捨棄了一些設計
> 裝備系統的數據傳遞 GamePlayValuer
第一次嘗試處理那麼多的數值運算,我的作法是再遊戲開始時先把所有數值傳進一個腳本裡
每個特定的值都有一個自己的計算器,記錄所有傳進去的計算資料 (加減、比例、指定)
最後根據基礎值完成計算再發出給需要的對象使用
缺點蠻明顯的,就是只能計算一次,之後有需要要再研究不同做法
> 敵人資料的儲存方式 EnemyData
第一次使用 ScriptalbeObject儲存資料
然後把敵人行為模組化,移動、行動、死亡,這樣就可以讓同種類的敵人共用相同的行為
雖然沒有寫得很好,但也比每個敵人重新設置一次輕鬆的多
我花最多時間搞的是移動路徑,因為計算是寫在ScriptableObject裡的,所以數值儲存和傳遞是一大難題,也不能用全域變量存,因為參考是共用的
最後的解決方法是回傳移動值,用ref 傳其他參考資料
不過這種作法也只適合簡單的移動模式,比較複雜的菁英怪就不能用了,又是一大難題
然後還搞了自訂編輯器
> 粒子系統的物件池 VisualEffectObjectPools
之前在想怎麼做粒子系統的物件池,但是Particle沒辦法像我其他地方一樣只改數據共用物件
所以我換了作法,透過 ScriptalboObject 儲存物件池資料,但實體是在遊戲一開始就生好的
生的時候讓在資料夾裡的 ScriptableObject 儲存物件池的Index,這樣運作時要取出粒子就不需要把整個池掃一次
然後我用了一種特殊的寫法,讓物件池調用比較容易,一行就能指定好所有數值
VisualEffectObjectPools.EnableVisualEffect(particle).SetPosition(position).SetDirction(dirction);
其實就只需要return this 而已,這種寫法好像有名子...但我忘了
誰記得的麻煩告訴我一下 :P
這東西再調整一下就可以輸出給其他專案用ㄌ
> 實作介面 Interface
也是第一次用C#的介面功能,蠻方便的,主要是希望對像有特定功能的時候可以用
IHealthStatus、IHealthSystem、IPushinderation
直接參考我之前的筆記八,使用 Interface 實作血量系統