跨語言腳本撰寫¶
在 Godot 中可以依照需求來混合使用不同的腳本語言。這表示,單一專案內可以同時以 C# 與 GDScript 來定義節點。本頁將介紹如何讓兩個以不同語言撰寫的節點該如何互動。
下列兩個腳本會在本頁中都會用來作為參考。
extends Node
var str1 : String = "foo"
var str2 : String setget ,get_str2
func get_str2() -> String:
return "foofoo"
func print_node_name(node : Node) -> void:
print(node.get_name())
func print_array(arr : Array) -> void:
for element in arr:
print(element)
func print_n_times(msg : String, n : int) -> void:
for i in range(n):
print(msg)
public class MyCSharpNode : Node
{
public String str1 = "bar";
public String str2 { get { return "barbar"; } }
public void PrintNodeName(Node node)
{
GD.Print(node.GetName());
}
public void PrintArray(String[] arr)
{
foreach (String element in arr)
{
GD.Print(element);
}
}
public void PrintNTimes(String msg, int n)
{
for (int i = 0; i < n; ++i)
{
GD.Print(msg);
}
}
}
實體化節點¶
若不使用場景樹中的節點,則可以直接從程式碼中實體化節點。
在 GDScript 中實體化 C# 節點¶
從 GDScript 中使用 C# 並不需花費額外功夫。載入後 (請參見 以類別當作資源) 便可以使用 new() 來實體化腳本。
var my_csharp_script = load("res://path_to_cs_file.cs")
var my_csharp_node = my_csharp_script.new()
print(my_csharp_node.str2) # barbar
警告
建立 .cs
腳本後,請記得 Godot 類別與 .cs
檔案本身的名字。若與檔名相同的類別不存在與檔案中,則會看到這個錯誤:Invalid call. Nonexistent function `new` in base
。
舉例來說,MyCoolNode.cs 應該包含名為 MyCoolNode 的類別。
你也需要檢查 .cs
檔案是否在專案中的 .csproj
檔案中參照。否則也會出現相同的錯誤。
在 C# 中實體化 GDScript 節點¶
在 C# 這邊,所有東西的運作方式都一樣。載入後,GDScript 便可以使用 GDScript.New() 來實體化。
GDScript MyGDScript = (GDScript) GD.Load("res://path_to_gd_file.gd");
Object myGDScriptNode = (Godot.Object) MyGDScript.New(); // This is a Godot.Object
存取欄位¶
在 GDScript 中存取 C# 欄位¶
要從 GDScript 存取 C# 欄位很簡單,沒什麼好擔心的。
print(my_csharp_node.str1) # bar
my_csharp_node.str1 = "BAR"
print(my_csharp_node.str1) # BAR
print(my_csharp_node.str2) # barbar
# my_csharp_node.str2 = "BARBAR" # This line will hang and crash
請注意,不管欄位是定義為 Property 還是 Attribute,只要沒定義 Setter,賦值時就會導致當機。
在 C# 中存取 GDScript 欄位¶
由於 C# 是靜態型別,從 C# 中存取 GDScript 就稍微有點複雜,我們需要使用 Object.Get() 與 Object.Set() 。第一個參數即為欲存取欄位的名稱。
GD.Print(myGDScriptNode.Get("str1")); // foo
myGDScriptNode.Set("str1", "FOO");
GD.Print(myGDScriptNode.Get("str1")); // FOO
GD.Print(myGDScriptNode.Get("str2")); // foofoo
// myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything
請記得當設定欄位值時,使用的型別應該是 GDScript 那段能認得的型別。特別是在使用如 doc_gdscript` 中說明的內建型別或擴充了 Object 的類別時。
呼叫方法¶
在 GDScript 中呼叫 C# 方法¶
同樣地,從 GDScript 中呼叫 C# 方法應該很簡單。封送處理程序會儘可能地將引數的型別轉換為符合函式簽章的型別。若無法自動轉換,則會看到這個錯誤: Invalid call. Nonexistent function `FunctionName`
。
my_csharp_node.PrintNodeName(self) # myGDScriptNode
# my_csharp_node.PrintNodeName() # This line will fail.
my_csharp_node.PrintNTimes("Hello there!", 2) # Hello there! Hello there!
my_csharp_node.PrintArray(["a", "b", "c"]) # a, b, c
my_csharp_node.PrintArray([1, 2, 3]) # 1, 2, 3
在 C# 中呼叫 GDScript 方法¶
從 C# 中呼叫 GDScript 方法則需要使用 Object.Call() 。第一個引數為欲呼叫的函式名稱,接下來的引數則會被傳入該方法中。
myGDScriptNode.Call("print_node_name", this); // my_csharp_node
// myGDScriptNode.Call("print_node_name"); // This line will fail silently and won't error out.
myGDScriptNode.Call("print_n_times", "Hello there!", 2); // Hello there! Hello there!
// When dealing with functions taking a single array as arguments, we need to be careful.
// If we don't cast it into an object, the engine will treat each element of the array as a separate argument and the call will fail.
String[] arr = new String[] { "a", "b", "c" };
// myGDScriptNode.Call("print_array", arr); // This line will fail silently and won't error out.
myGDScriptNode.Call("print_array", (object)arr); // a, b, c
myGDScriptNode.Call("print_array", (object)new int[] { 1, 2, 3 }); // 1, 2, 3
// Note how the type of each array entry does not matter as long as it can be handled by the marshaller
警告
如上所示,若呼叫方法的第一個引數為陣列,則需要將其轉換型別為 object
。否則,該陣列的每個元素都會被當作各個單一引數,進而不符合函式簽章。
繼承¶
GDScript 檔案不能繼承 C# 腳本。同樣地,C# 腳本也不能繼承 GDScript 檔案。由於要實作這個功能會很複雜,此一限制應該也會延續到未來的版本中。 請參考 這個 GitHub Issue (英文) 來瞭解詳情。