手動切換場景
有時候你會需要更細緻地控制場景的切換方式。Viewport 的子節點會算繪到它所產生的影像中,即使這些節點並不屬於「目前」場景也一樣。自動載入(Autoload)的節點屬於這一類,同時在執行時動態實例化並加入樹狀結構的場景也是:
var simultaneous_scene = preload("res://levels/level2.tscn").instantiate()
func _add_a_scene_manually():
# This is like autoloading the scene, only
# it happens after already loading the main scene.
get_tree().root.add_child(simultaneous_scene)
public Node simultaneousScene;
public MyClass()
{
simultaneousScene = ResourceLoader.Load<PackedScene>("res://levels/level2.tscn").Instantiate();
}
public void _AddASceneManually()
{
// This is like autoloading the scene, only
// it happens after already loading the main scene.
GetTree().Root.AddChild(simultaneousScene);
}
要完成流程,將新場景與舊場景交換時,你有多種選擇。從 Viewport 視圖中移除場景的方法有很多,這些方法在操作速度、記憶體消耗,以及資料存取與完整性等方面都各有取捨。
刪除現有場景。 SceneTree.change_scene_to_file() 與 SceneTree.change_scene_to_packed() 會立即刪除目前場景。你也可以手動刪除主場景,假設根節點名稱為「Main」,可以執行
get_node("/root/Main").free()來刪除整個場景。釋放記憶體。
優點:記憶體不再被佔用。
缺點:若要再次回到該場景,必須重新載入到記憶體,這會消耗更多時間與記憶體。如果不需要很快切回原場景,這通常不是問題。
缺點:無法再存取該場景的資料。如果短期內不需要用到這些資料,問題不大。
注意:如果需要保留即將被刪除場景中的資料,可以將其中一個或多個節點重新掛載到其他場景,甚至直接掛載到 SceneTree 上。
處理停止。
優點:沒有節點就沒有運算、物理運算或輸入處理,CPU 可專注處理新場景內容。
缺點:這些節點的運算與輸入處理都不再進行。如果不需要即時的資料更新,問題不大。
隱藏現有場景。 你可以透過更改節點的可見性或碰撞屬性,將整個節點子樹從玩家視角隱藏。
記憶體仍被佔用。
優點:如有需要,仍可存取資料。
優點:不必再移動任何節點即可保留資料。
缺點:更多資料會繼續佔用記憶體,對於像網頁或手機這種對記憶體敏感的平台可能會造成問題。
仍然持續處理。
優點:資料會持續接收處理更新,因此場景內依賴 delta time 或影格資料的內容會持續更新。
優點:節點仍然會是群組的成員(因為群組屬於 SceneTree)。
缺點:CPU 的運算量會分散到多個場景,如果負載過大可能導致畫面更新率降低。建議隨時測試效能,確保目標平台能承受這種負載。
從樹狀結構中移除現有場景。 先將現有場景的根節點指定給一個變數,然後使用 Node.remove_child(Node) 將整個場景從樹中分離。
記憶體仍被佔用(優缺點與隱藏場景方式相似)。
停止處理(優缺點與完全刪除場景相似)。
優點:這種「隱藏」變體更容易顯示/隱藏場景,只需呼叫 add_child 或 remove_child 方法,而不必追蹤場景的多次變動。這類似於其他引擎裡的遊戲物件啟用/停用功能。
缺點:與單純隱藏場景不同,如果場景內的資料依賴 delta time、輸入、群組或其他來自 SceneTree 的資料,這些內容會變得過時。
有時你可能需要同時存在多個場景,例如在執行時新增自訂單例,或在切換場景時保留資料(將場景加到根節點下)。
get_tree().root.add_child(scene)
GetTree().Root.AddChild(scene);
另一種情境是使用 SubViewportContainers 同時顯示多個場景。這非常適合在螢幕不同區域算繪不同內容(如小地圖或分割畫面的多人遊戲)。
每種選項都有其最合適的應用時機,因此你必須評估每種方式的效果,選擇最適合自己專案需求的做法。