Node/Scene structure in Godot C++ Programmer Perspective

Godot 裡的最基本元素是 Node,概念上等同 C++ Class。

當我們為一個 Node 附加 GDScript 時,就是準備在 script 裡面覆寫 (override) 原來的 member function 或增加 member variable,也就是繼承的概念。

當我們在 Godot 介面左邊的 Scene Panel 加一個 Node 時,實際上是同時宣告 (declare) 並實體化 (instance) 了一個 Class,跟 C++ 不同,在 Scene Panel 被實體化的物件名可以和 Class 名字一樣。不過只限於 Scene Panel,因為在 script 裡面是不可以用 Node 名字當變數名的。

所有的 Node 都是可以被執行的,如果要讓執行有意義,以下是比較常被覆寫 (override) 的 member function。

  • func _init()
  • func _ready()
  • func _process(delta)
  • func _physics_process(delta)
  • func _input(event)

每個 Node 裡面都可以加入多個 Child Node,可以想像 Node 有個 private member variable – node container。當 Node 的如 _ready() 或 _process(delta) 等 member 被呼叫時,Godot 會自動呼叫所有 node container 裡面的同樣的 member func。

* check Node.propagate_call() for similar mechanism

Scene 指的就是 root Node,root Node 與所有的 child Nodes 的集合叫 scene tree 或一樣叫 scene。一個 project 裡面可以有很多的 scene,指定第一個被執行的 scene (i.e. entry point) 的方法 project -> project settings -> application -> run -> Main Scene。

每個 scene 都一定要一個對應的 *.tscn 檔案,該檔案不能直接編輯,可以在左下的 FileSystem 找到。

在 Coding time 要在某個 Node 下加入 API 中定義的 Node,在 node 上右點滑鼠選 “add child node” 再選擇 Node 類型。

在 Coding time 要在某個 Node 下加入自定義的 root Node (i.e. scene) 在 node 上右點滑鼠選 “instance child scene” 再選擇 scene *.tscn。

在 Runtime (in script) 要在某個 Node 下加入 API 中定義的 Node,在 Node’s script 下寫

var child_node = Node.new()
add_child( child_node )

在 Runtime (in script) 要在某個 Node 下加入自定義的 root Node (i.e. scene) 在 Node’s script 下先宣告一個 PackedScene name

export (PackedScene) var MyNode

然後把 MyNode 這名字跟你要實體化的 scene 做連結,連結方法是到右上方的 Inspector panel 裡 Script Variable -> MyNode。連結完後再回到 script 寫

var child_node = MyNode.instance()
add_child( child_node )

從 C++ 的角度,instance() 和 new() 名字不一樣,但做得事好像沒差別? 有人對這疑惑的說法是 new() 是實體化一個 Class instance() 除了實體化 Class,還實體化了 scipt 內定義的 member variable 與 node 裡面的 child nodes,某個角度看還是有不同的。

Parent Node 要取得 Child Node

get_child( 'path' )

Coding time 加入的 Node’s path 可以參照 Scene Panel。

Runtime 加入的 Node’s path 可以在 add_child() 結束後用 get_path() 取得,不過與其記下路徑,不如把 child node reference 存下來就好。

要移除一個 Child Node,最常用的方法是 Child Node 呼叫自己的 queue_free() func,當 Child Node 自行消滅時,Parent Node 也會自動將之移除。另一個方法是 Parent Node 呼叫 remove_child(ref),不過這不會消滅 Child Node,如果 code 同時有這二種方法存在,有可能會造成 reference 指向一個已經釋放的 Object 的問題。

*參考 queue_free() 的說明