不要不加思索的繼承MonoBehaviour

 

大部分碰過濫用MonoBehaviour的狀況都是需要每偵執行邏輯的情況。跟singleton的濫用狀況類似,通常都是為了快速完成當前的功能,而沒有好好思考軟體架構。

看看下面的例子:

假設你正在寫一個動作遊戲的敵人AI,應該需要每偵運算(其實根據狀況,有時不需要每偵運算)。你想了一下,覺得直接繼承MonoBehaviour,就有Update()可以用了,而且不用花很多時間,於是就這樣做了。

1個月後,設計師加了一個道具,功能是讓特定範圍內的敵人時間停止或是減速。這時候你會碰到什麼問題?如果遊戲需要暫停功能,又會碰到什麼問題?

你可能已經想到會遭遇的困難了,不過我也相信你也想到一些解決方案。
不過其實繼承MonoBehaviour的問題其實不是單純Update()的處理問題而已。

繼承MonoBehaviour有幾個問題:
1. 要很完全的控制Update並不是那麼簡單
2. 失去了建構式
3. MonoBehaviour與Scene是強相依

當然上面這些問題有很多手段可以解決。不過誤用繼承後,在中後期進行架構變更,其實是件蠻痛苦的事情。以軟體工程的經驗,我更喜歡減少未來進行修改的痛苦,因此用下面的原則來處理這類問題。

多用組合,少用繼承

有經驗的軟體工程師都知道這個原則。大多數的情況下,使用組合在未來能夠比較順利的因應變更。當然並不是說使用繼承一定就比較糟,我們一直都說這種事情是要看設計內容與規格來決定的。

因此,如果目標是中大型專案,要寫敵人的腳本,比起直接讓Enemy繼承MonoBehaviour,我通常會把Enemy寫成單純的類別,然後讓這個類別帶有這個敵人的模型,也就是渲染用的MonoBehaviour。

剛開始進行這種想法轉換,可能真的會很不適應,不過我建議可以帶著這個原則多嘗試幾次組合,應該就能拿捏出使用組合或是繼承的時機。

繼承MonoBehaviour的原則
  1. 協助渲染的腳本,像是動畫控制、場景效果
  2. 需要物理碰撞與觸發(OnCollision與OnTrigger系列函式)
  3. 需要使用coroutine的腳本(有時可以透過額外的GameObject來解決)
  4. 其他與遊戲主要邏輯無關的支援腳本,例如:FPS顯示、debug介面

發佈留言