Unity 遊戲框架搭建 2019 (四十四、四十五) 關於知識庫的小結&獨立的方法和獨立的類

在上一篇,我們完成了一個定時功能,並且接觸了 Action 和委託、lambda 表達式這些概念。

到目前為止,我們的庫作為知識收錄這個功能來說,已經非常好用了,由於使用了 partial 關鍵字,所以重複的程式碼少了很多。而作為一個可復用的工具庫來說,勉強能夠應付。

通過 partial 關鍵字,理論上可以對已有了類,進行無限地增加示例。而我們的示例的類型呢,主要是寫可獨立使用的方法和 MenuItem 示例。什麼叫獨立使用的方法?到目前為止我們寫的所有靜態方法都是可以獨立使用的,這些方法並不需要與其他方法或對象進行協作就可以發揮本身的價值。

什麼樣的方法不能獨立使用呢?這種非常常見,比如資源的載入和卸載 Load/UnLoad, UI 的打開和關閉 ,事件的發送/接收/註冊,這種方法都不能獨立使用,而是需要和其他的方法配合使用。這種類型的方法往往都定義在一個類里,是需要進行更嚴格地設計的。不像我們目前寫的這幾個 partial 類中的方法,再增加一個方法很少要考慮之前在類中已實現的方法,我們只要保證邏輯不重複就可以了。

而在設計不可獨立使用的方法呢,要保證邏輯不能重複的同時,要更多地考慮互相如何更好地協作,從方法結構、調用順序、命名、訪問許可權、所在類這些都要進行嚴格地設計,才會得到一個合格的方法。

對開發者的要求會高很多。

我們在上一篇的定時功能,就有點這個苗頭,我們的定時方法的許可權是 public 類型的,由於實現需要用到 Coroutine 所以又定義了一個實現方法,用來實現 Coroutine 邏輯,而這個 Coroutine 邏輯不希望被子類和外部類訪問到,所以訪問許可權就設置成了 private,這樣才算是一個合格的方法。

而這個方法僅僅和合格而已,其實用筆者角度來看,問題非常多,比如要考慮 Coroutine 中斷問題,也要考慮 MonoBehaviourSimplify 這個類的使用問題,這個類現在可以直接掛到 GameObject 上,而筆者不希望用戶這樣去使用 MonoBehaviourSimpleify,而是通過繼承使用,要解決的話其實也很簡單,使用抽象類就好了。但是這樣一來,我們的上一篇文章的資訊量就會很多,並且在上一篇我們是剛剛接觸繼承這個概念,如果一篇文章就把繼承從入門到掌握再到精通都講完,那大家吸收的效果就會差很多。所以我們還是慢慢來,羅馬不是一天建成的。

總之,目前,對於讀者來說,自己寫一個示例或者收集筆者的示例,是沒啥太大的問題了。畢竟我們已經實踐了很多了。從筆者的角度來說,專欄的約定和規則已經穩定了。

從下一個示例開始呢,我們開始進行庫的專項訓練。

我們在開始本示例之前,先整理出我們當前庫中的程式碼類型。

  1. 工具方法:CommonUtil、GameObjectSimplify等。
  2. 類: MonoBehaviourSimplify。

靜態方法中的方法全部都是工具方法,都是可以獨立使用的,之所以可以獨立使用,是因為這些工具方法所在類是沒有狀態的。

什麼叫沒有狀態呢? 比如 CommonUtil,其中的方法不管怎麼使用,CommonUtil 類本身不會發生任何改變。但是像我們的 MonoBehaviourSimplify 用了一次 Show,那麼它的 gameObject 成員就會發生改變,gameObject.active 值就會是 true,狀態(數據)發生了改變。

CommonUtil 就是沒有狀態的,因為它只是靜態方法的集合。

MonoBehaviourSimplify 是有狀態的,不過僅僅是自身發生了狀態的改變,所以這個類是獨立的。而類中的方法 ShowHide 等並不是獨立的。

我們得出了結論,CommonUtil 中的靜態方法都是獨立的。MonoBehaviourSimplify 類本身是獨立的,其中的方法都不是獨立的。

有了這個結論我們能做什麼呢?

當然是去收集獨立的類了。

我們的庫從一開始收集自己的知識點,到能夠收集靜態獨立方法而到現在,可以收集獨立的類了。

所以從知識庫升級到了靜態方法庫又升級到了類庫。

當然並不是說到了類庫就不能收集靜態方法和知識了,知識和靜態方法我們同樣要收集。

那麼我們要收集什麼樣的類呢?

當然是項目經常會用到的,或者是提高我們工作/編碼效率的類呀,不只是類,任何這樣的東西我們都要收集。類庫只是暫時的,因為我們的認知目前就只能達到收集類庫的水平,要想收集更複雜的東西,我們就要提升認知,不過在提升之前,我們先把類庫掌握好。

既然要收集項目中經常會用到的或者提高我們工作/編碼效率的類,那麼我們就要思考。我們在項目中經常遇到的問題。

大多數開發者都要開發邏輯,不過說成開發邏輯還是太籠統了,我們再具體一點。把範圍縮小的什麼邏輯。UI 邏輯或者是數據存儲邏輯、或者設計某個模組、某個系統、又或者是 GamePlay 邏輯等等。這些都是我們經常要寫的邏輯。但是還是太過籠統,如果按照這個分類再分下去也沒有太大的意義。

那麼筆者呢,直接結合自己的經歷講了,筆者在最初做項目的時候,是把所有的程式碼都寫到一個文件里或者寫到一個類里,直到後來掌握了把一些可復用的程式碼提取成方法,提取了一段時間之後呢,自然就在準備寫邏輯之前會去思考能不能直接設計成方法,再這樣過了一段時間後,就考慮這些邏輯需不需要設計成方法,經過這一輪,對方法設計就掌握得很熟練了,之後再配合一些方法設計相關的書籍,使得自己寫出來的方法可以做很多事情。

類也是一樣的。不過類的設計範疇太大了,所以我們呢,就只思考獨立的類,我們只需要繼承它就能獲得功能的這種類。

我們在最初做項目的時候,往往會遇到一個腳本訪問的問題。比如掛在 A GameObject 的 A 腳本,如果想訪問 B GameObject 的 B 腳本。筆者當時使用方式呢,當然是使用連線的方式。在 A 腳本中聲明一個 public B b; 然後在 A 腳本的 Inspector 上就可以對這個變數進行賦值,這樣在最開始寫邏輯的時候真的很方便很好用,不過過了不久,用了一段時間後就發現會變得非常亂。直到連功能都不能加了為止,筆者都沒有意識到是這個問題。

A 腳本的程式碼如下:

public class A : MonoBehaviour
{
	public B b;
	
	void Start()
	{
		b.DoSomething();
	}
}

而 Unity 的這個特性,讓很多新手覺得 Unity 好強大,這些新手就有筆者一個。後來才知道,這樣做是一個坑。

那麼 A 訪問 B 腳本這樣的邏輯,在項目開發時候是使用得最多的。那麼除了連線有沒有更好的方案呢?當然有的。

筆者在之後呢很快接觸了單例模式。發現只要把每個腳本都寫成單例,那麼所有的腳本,都可以隨便訪問了,真的爽翻了,而且比連線好用了好多,使用連線的時候,public 成員變數都不知道拿來幹嘛的,而使用單例,最起碼可以使用 IDE 的可以跟蹤到程式碼定義的地方。

程式碼如下:

public class A : MonoBehaivour
{
	public static A Instance;

	void Awake()
	{
		Instance = this;
	}

	void Start()
	{
		B.Instance.DoSomething();
	}
	
	void OnDestroy()
	{
		Instance = null;
	}
}

public class B : MonoBehaivour
{
	public static B Instance;

	void Awake()
	{
		Instance = this;
	}

	void DoSomething()
	{
		// do something
	}
	
	void OnDestroy()
	{
		Instance = null;
	}
}

這種單例模式,在 Unity 中最容易實現的單例。而筆者見過身邊工作了 4 ~ 5 年工作經驗的同事也在用這種單例,不過作為過來人,要提醒各位,這種單例要謹慎使用,後邊呢有更合理的方式。

就這樣,筆者當時就用這樣的單例寫了好幾個項目,並且心裡覺得,天吶終於接觸設計模式了。設計模式真好用,真想全部學了。。。

不管怎麼樣,如何解決腳本之間互相訪問這個問題,是一個很大的問題。而使用單例其實是作為沒有主程帶的初學者都必須經歷的一個階段,所以沒有必要去噴初學者的程式碼,大家都是這樣過來的。

已經跟專欄跟到這裡的童鞋,已經是非常可貴的了。

今天的內容就這些,我們下一篇再見,我們今天沒有示例,所以不用導出。

轉載請註明地址:涼鞋的筆記:liangxiegame.com

更多內容