Autoload v.s. 一般節點

Godot 提供一個功能,可以自動在專案根節點載入節點,讓你能全域存取這些節點,達到單例(Singleton)的效果:請參閱 單例(自動載入)。這些自動載入的節點在你用 SceneTree.change_scene_to_file 切換場景時不會被釋放。

在本篇指南中,我們將學習如何使用 Autoload 功能,以及可以避免使用 Autoload 的一些技巧。

音訊截斷問題

其他的引擎鼓勵使用 Manager 類別,即一種用來管理許多全域存取物件功能的單例。託節點樹與訊號的福,Godot 中提供了許多方法來避免全域狀態。

舉例來說,假設我們在做一個平台遊戲,撿到金幣時需要播放音效,可用 AudioStreamPlayer 節點。但如果 AudioStreamPlayer 正在播放時又被呼叫,新的音效會中斷原本的聲音。

有一種解決方法是撰寫一個全域且自動載入的音訊管理員類別。這個類別要產生一個 AudioStreamPlayer 節點集區,然後在遇到音效播放要求的時候照順序讓每一個 AudioStreamPlayer 來播放。假設我們管這個類別叫 Sound ,則我們在專案任何一個地方都可以通過呼叫 Sound.play("coin_pickup.ogg") 來使用。這樣一來便在短期內解決了問題,但接著會引發更多的問題:

  1. 全域狀態 :現在,有一個物件必須要負責所有物件的資料。若 Sound 類別發生錯誤,或是沒有可用的 AudioStreamPlayer,則所有呼叫 Sound 的節點都會壞掉。

  2. 全域存取 :現在所有物件都能從任何地方呼叫 Sound.play(sound_path) ,所以要找到 Bug 的原因變得更加困難了。

  3. 全域資源分配 :現在從一開始就有一個 AudioStreamPlayer 節點集區,如果數量太少的話便會遇到 Bug,而數量太多的話則會佔用過多的記憶體。

備註

有關全域存取,問題在於,在本例中,任何地方的「所有」程式碼都有可能將錯誤的資料傳給 Sound Autoload。因此,發生問題時要尋找的範圍就變成了整個專案。

如果將程式碼保留在場景中,則只有一兩個程式碼可能會與音訊有關。

相較之下,如果每個場景都在自己內部保佑必要數量的 AudioStreamPlayer 的話,則這些所有的問題便解決了:

  1. 每個場景都管理自己的狀態資訊,所以當資料由問題的時候,就只會影響到單一場景。

  2. 每個場景都只存取自己的節點。所以當現在出現 Bug 時,就很容易找出是哪個節點的錯。

  3. 每個場景都只分配到所需數量的資源。

管理共用功能或資料

另一個使用 Autoload 的理由是這樣就可以在不同的場景間重複使用相同的方法或資料。

若要用函式方式,可自訂一個新的 Node 型別,並在 GDScript 用 class_name 關鍵字,讓個別場景能直接使用該功能。

但遇到要處理資料的時候,就必須選擇:

  1. 建立一個新型的 Resource 來共享資料。

  2. 將資料儲存在每個節點都能存取的一個物件當中,比如說使用 owner 屬性來存取場景的根節點。

什麼時候該用 Autoload

GDScript 支援使用 static func 建立 static (靜態) 函式。 當與 class_name 結合使用時,(靜態函式)可以建立輔助函式程式庫,而無需建立實例來呼叫它們。 靜態函式也具有限制,它們不能引用成員變數、非靜態(non-static)函式或 self

自 Godot 4.1 起,GDScript 也支援使用 static var 宣告的「靜態」變數。這表示您現在可以在一個類別的不同實例之間共享變數,而不需要建立一個獨立的自動載入節點。

作用域更廣的系統 :若單例管理自己的資訊,且不會存取到其他物件的資料,則建立一個處理廣域資料的系統也不錯。例如,工作或對話系統。

備註

Autoload 並不完全同等與單例 (Singleton)。在 Autoload 中沒有方法可以防止再次實體化一個 Autoload 的節點。只能算是一種能不考慮遊戲節點架構以及目前執行的節點是什麼,就讓節點作為場景樹子節點自動載入的方法,例如,按 F6 鍵。

然後,就能取得自動載入的節點,例如有一個叫 Sound 的 Autoload,則可以呼叫 get_node("/root/Sound")