C# 功能

本頁簡介了 C# 與 Godot 常見的功能,以及該如何互相配合。

型別轉換

C# 是靜態型別語言。所以,無法這麼做:

var mySprite = GetNode("MySprite");
mySprite.SetFrame(0);

GetNode() 方法會回傳 Node 實體。必須要明確將回傳值轉換為所需要的型別,在此例中為 Sprite

為此,在 C# 中有各種選項。

型別轉換與型別檢查

使用這種方式時,若回傳的結果無法轉換為 Sprite 的話會拋出 InvalidCastException 。若非常確定不會失敗的話,可以使用這種方式來代替 as 運算子。

Sprite mySprite = (Sprite)GetNode("MySprite");
mySprite.SetFrame(0);

使用 AS 運算子

as 算子會在節點無法轉換為 Sprite 時回傳 null ,因此,無法與實值型別一起使用。

Sprite mySprite = GetNode("MySprite") as Sprite;
// Only call SetFrame() if mySprite is not null
mySprite?.SetFrame(0);

使用泛型方法

也可以使用泛型方法來透明轉換型別。

GetNode<T>() 可以在執行前就轉換節點型別。當節點無法轉換為需要的型別是,會拋出 InvalidCastException

Sprite mySprite = GetNode<Sprite>("MySprite");
mySprite.SetFrame(0);

GetNodeOrNull<T>() 會使用 as 運算子,且會在節點無法轉換為需要的型別時回傳 null

Sprite mySprite = GetNodeOrNull<Sprite>("MySprite");
// Only call SetFrame() if mySprite is not null
mySprite?.SetFrame(0);

使用 IS 運算子來進行型別檢查

若要檢查節點是否可被轉換為 Sprite,可以使用 is 運算子。is 運算子會在無法將節點轉換為 Sprite 時回傳 false,否則將回傳 true。

if (GetNode("MySprite") is Sprite)
{
    // Yup, it's a sprite!
}

有關更多型別檢查,請參考 模式比對

C# 訊號

完整的 C# 例子請參考按部就班 撰寫腳本 教學中的 處理訊號 一節。

在 C# 中,可在 Delegate 上加上 [Signal] 屬性來宣告訊號。

[Signal]
delegate void MySignal();

[Signal]
delegate void MySignalWithArguments(string foo, int bar);

接著這些訊號便可以在編輯器中或使用 ``Connect` 來在程式碼中進行連接。若像在編輯器中連接訊號,則需要建置 (或重新建置) 專案組件才能看到新的訊號。可以通過點擊編輯器視窗右上角的 [建置] 按鈕來手動觸發建置。

public void MyCallback()
{
    GD.Print("My callback!");
}

public void MyCallbackWithArguments(string foo, int bar)
{
    GD.Print("My callback with: ", foo, " and ", bar, "!");
}

public void SomeFunction()
{
    instance.Connect("MySignal", this, "MyCallback");
    instance.Connect(nameof(MySignalWithArguments), this, "MyCallbackWithArguments");
}

使用 EmitSignal 方法來送出訊號。

public void SomeFunction()
{
    EmitSignal(nameof(MySignal));
    EmitSignal("MySignalWithArguments", "hello there", 28);
}

請注意,可以使用 nameof 關鍵字來參照訊號名稱 (套用於 delegate 自身)。

It is possible to bind values when establishing a connection by passing a Godot array.

public int Value { get; private set; } = 0;

private void ModifyValue(int modifier)
{
    Value += modifier;
}

public void SomeFunction()
{
    var plusButton = (Button)GetNode("PlusButton");
    var minusButton = (Button)GetNode("MinusButton");

    plusButton.Connect("pressed", this, "ModifyValue", new Godot.Collections.Array { 1 });
    minusButton.Connect("pressed", this, "ModifyValue", new Godot.Collections.Array { -1 });
}

訊號支援參數與繫結所有 內建型別s 與繼承 Godot.Object 的值。因此,任何 NodeReference 都自動相容,但自定資料物件則需要繼承 Godot.Object 或其任一子類別。

public class DataObject : Godot.Object
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
}

最後,可以通過 AddUserSignal 來建立訊號。但請注意 AddUserSignal 應該在 (通過 ConnectEmitSignal) 使用前執行。

public void SomeFunction()
{
    AddUserSignal("MyOtherSignal");
    EmitSignal("MyOtherSignal");
}

定義前置處理器

Godot 中有一系列的定義,可以用來依據編譯環境來更改 C# 程式碼。

備註

若是在 Godot 3.2 版前建立的專案,則需要更改或重新產生 csproj 來使用本功能 (請對照 3.2+ 版專案中的 <DefineConstants> )。

範例

例如,可以依據平台來更改程式碼:

    public override void _Ready()
    {
#if GODOT_SERVER
        // Don't try to load meshes or anything, this is a server!
        LaunchServer();
#elif GODOT_32 || GODOT_MOBILE || GODOT_WEB
        // Use simple objects when running on less powerful systems.
        SpawnSimpleObjects();
#else
        SpawnComplexObjects();
#endif
    }

或者也可以依據程式碼所在引擎來偵測,對於跨引擎函式庫來說很實用:

    public void MyPlatformPrinter()
    {
#if GODOT
        GD.Print("This is Godot.");
#elif UNITY_5_3_OR_NEWER
        print("This is Unity.");
#else
        throw new InvalidWorkflowException("Only Godot and Unity are supported.");
#endif
    }

完整的定義列表

  • 在 Godot 中,永遠都有定義 GODOT

  • 依據目前架構是 64 位元還是 32 位元,會有定義 GODOT_64GODOT_32

  • One of GODOT_X11, GODOT_WINDOWS, GODOT_OSX, GODOT_ANDROID, GODOT_IOS, GODOT_HTML5, or GODOT_SERVER depending on the OS. These names may change in the future. These are created from the get_name() method of the OS singleton, but not every possible OS the method returns is an OS that Godot with Mono runs on.

匯出 時,根據匯出的功能也有下列定義:

  • 依據平台類型,會定義 GODOT_PC, GODOT_MOBILEGODOT_WEB 其中一個。

  • 在 Android 上會依據架構定義 GODOT_ARM64_V8A or GODOT_ARMEABI_V7A 其中一個。

  • 在 iOS 上會依據架構定義 GODOT_ARM64 or GODOT_ARMV7 其中一個。

  • 依據紋理貼圖壓縮類型,會定義 GODOT_S3TC, GODOT_ETCGODOT_ETC2

  • 所有從匯出選單中新增的自定功能都會被轉成大寫並加上前置詞: foo -> GODOT_FOO

若欲檢視範例專案,請參考作業系統示範專案: https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test