問題
- 近來在調試unity程序時發現了幾個關於初始化問題的bug,在剛進入play 模式時會報錯,提示…對象爲null,但是前進到下一幀時又一切正常,這個奇怪的錯誤讓我很疑惑。
解決過程
看到是第一幀出錯,第一反應就是想到是有對象沒有被初始化,於是開始找出錯的腳本中初始化的部分的問題,結果發現已經在構造函數和start函數中正確初始化了,按理不該出現這個問題。
然後經過幾次反覆的實驗和Debug.Log(),終於反應過來,unity會在start函數中進行對象的初始化,也就意味着,構造函數會在start函數中進行調用,以初始化必要的成員,也就是start函數中的語句是在構造函數執行後執行的,但是代碼中我們的構造函數中的相關對象的初始化是依賴於start函數中相關的初始化的,着就導致了在構造函數中進行初始化操作時,由於需要調用start函數中初始化的對象,而start中的初始化語句是在後面執行的,所有出現了值爲null的情況。
知道了問題的來源自然就容易解決了,把所有的初始化代碼全部放到構造函數中或者把所有的初始化代碼全部放到start函數中即可
深入探究
- 當初學C++時就被一直強調構造函數的重要性,這裏當然不能停留在解決眼前的問題上,所以,我稍微深入的瞭解下unity中構造函數和Start,Awake函數等的關係,最簡單的研究方式當然是。。。百度了,先貼幾個別人或者時官方文檔上的簡單介紹:
— Unity官方對構造函數的說明
— Unity官方對Start函數的說明
— Unity3D開發中 使用C# 很多文檔都有提到這個“避免使用構造函數 不要在構造函數中初始化任何變量”爲什麼?【知乎】 可以先閱讀鏈接裏面的文章有一個大概的瞭解,接下來就來研究下構造函數和Unity自帶的函數的調用關係。
先把實驗中使用的代碼貼上來
public class MessageTest : MonoBehaviour
{
static int i;
bool start;
bool FixedStart;
public void Awake()
{
Debug.Log("Awake函數" + i);
}
public void Start()
{
Debug.Log("start函數" + i);
}
private void Update()
{
if (start)
{
Debug.Log("update" + i);
start = false;
}
}
private void FixedUpdate()
{
if (FixedStart)
{
Debug.Log("fixedupdate" + i);
FixedStart = false;
}
}
public MessageTest()
{
i += 1;
start = true;
FixedStart = true;
Debug.Log("構造函數" + i);
}
}
這個代碼很簡單,就是一個簡單的腳本,使用靜態變量用於對構造函數的調用次數計數,其中靜態變量也會自動初始化爲0,另外使用兩個bool值對兩個update函數進行控制,防止一直輸出。
進入play模式,查看控制檯輸出:
其中可以看到構造函數是被調用了三次的,我嘗試通過斷點的方式查看調用堆棧。。但是VS沒有顯示,所以沒辦法,不清楚完整的調用過程,所以這裏我 大膽的猜測一下調用過程:
考慮到unity是組件式的架構,並且每個腳本都是一個組件,所以,首先我猜測,第一次的調用 是在初始化這個腳本組件的時候調用的,相當於初始化腳本
其次,第二次是初始化GameObject的時候調用的,因爲unity中所有的腳本都是掛在Gameobject上面的,即Awake函數中,而Start函數是不負責初始化腳本內容,只是相當於在腳本被激活時進行調用的一個函數
最後,在物體被銷燬後時會調用一次構造函數,爲什麼是物體銷燬後呢?因爲我在運行模式中通過添加和移除腳本發現,其中destory函數會被調用,但是並沒有調用構造函數。至於物體被銷燬時爲什麼會調用構造函數,這裏暫時沒想到是什麼原因,如果有知道的朋友歡迎留言探討。