C# features

이 페이지는 C#과 Godot에서 일반적으로 사용되는 기능과 어떻게 이 둘이 함께 사용되는 지에 대한 개요입니다.

형 변환(Type conversion)과 캐스팅(casting)

C#은 정적으로 타입형 언어입니다. 그러므로 다음을 할 수 없습니다:

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

Node 인스턴스를 반환하는 GetNode() 메서드. Sprite의 경우, 원하는 파생 타입을 명시적으로 변환해야 합니다.

이를 위해, C#에서는 다양한 설정이 있습니다.

캐스트(Cast)와 타입 체크(Type Check)하기

반환된 노드를 Sprite로 캐스트 할 수 없는 경우 InvalidCastException을 발생시킵니다. 실패하지 않는다고 확신하면 as 연산자 대신에 그것을 사용할 것입니다.

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

AS 연산자 사용하기

The as operator returns null if the node cannot be cast to Sprite, and for that reason, it cannot be used with value types.

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 연산자를 사용할 수 있습니다. Sprite가 캐스트 되지 않는다면 is 연산자는 거짓을 반환하고, 그렇지 않으면 참을 반환합니다.

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

더 많은 고급 타입 체크하기의 경우, 패턴 일치에서 찾아볼 수 있습니다.

C# 시그널

완전한 C# 예제를 위해, 단계별 스크립팅(Scripting) 튜토리얼에서 시그널 다루기를 참고하세요.

C#에서 시그널 선언은 델리게이트(delegate)에서 [Signal] 속성으로 됩니다.

[Signal]
delegate void MySignal();

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

These signals can then be connected either in the editor or from code with Connect. If you want to connect a signal in the editor, you need to (re)build the project assemblies to see the new signal. This build can be manually triggered by clicking the “Build” button at the top right corner of the editor window.

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 키워드로 항상 시그널 이름을 참조할 수 있다는 것을 명심하세요 델리게이트 자체에 적용됨).

객체 배열을 전달함으로써 연결을 지을 때 값들을 묶을 수 있습니다.

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 object[] { 1 });
    minusButton.Connect("pressed", this, "ModifyValue", new object[] { -1 });
}

시그널은 매개변수를 지원하고, 모든 내장 타입의 값들과 Godot.Object에서 파생된 클래스들을 묶습니다. 결과적으로, 모든 NodeReference는 자동으로 호환될 것이지만, 맞춤 데이터 객체는 Godot.Object나 그 하위 클래스에서 확장해야 할 것입니다.

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

마침내, 시그널은 AddUserSignal로 호출하는 것으로 생성될 수 있지만, 시그널을 사용하기 전에 실행되어야 한다는 것을 명심하세요 (ConnectEmitSignal로 말이죠).

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

Preprocessor defines

Godot has a set of defines that allow you to change your C# code depending on the environment you are compiling to.

주석

If you created your project before Godot 3.2, you have to modify or regenerate your csproj file to use this feature (compare <DefineConstants> with a new 3.2+ project).

예제

For example, you can change code based on the platform:

    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
    }

Or you can detect which engine your code is in, useful for making cross-engine libraries:

    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
    }

Full list of defines

  • GODOT is always defined for Godot projects.
  • One of GODOT_64 or GODOT_32 is defined depending on if the architecture is 64-bit or 32-bit.
  • 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 :ref:OS <class_OS> singleton, but not every possible OS the method returns is an OS that Godot with Mono runs on.

When exporting, the following may also be defined depending on the export features:

  • One of GODOT_PC, GODOT_MOBILE, or GODOT_WEB depending on the platform type.
  • One of GODOT_ARM64_V8A or GODOT_ARMEABI_V7A on Android only depending on the architecture.
  • One of GODOT_ARM64 or GODOT_ARMV7 on iOS only depending on the architecture.
  • Any of GODOT_S3TC, GODOT_ETC, and GODOT_ETC2 depending on the texture compression type.
  • Any custom features added in the export menu will be capitalized and prefixed: foo -> GODOT_FOO.

To see an example project, see the OS testing demo: https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test