스크립팅(Scripting)

소개

Godot 3.0 이전에는, 스크립팅 하는 법이 GDScript basics 뿐 이었습니다. 지금은, Godot는 4개를 (그래요, 4개!) 공식 언어로 갖고 있고 추가로 스크립트 언어를 추가하는 기능도 있습니다!

이것은 대단합니다, 왜냐하면 큰 유연성을 제공해주기 때문입니다. 하지만 우리가 여러가지 언어를 지원하는 것을 더 어렵게 만들기는 합니다.

Godot에서 "메인" 언어는 GDScript와 VisualScript입니다. 이들이 선택된 주된 이유는 Godot와 통합 능력이 좋기 때문입니다, 둘다 기본 에디터에서 매끄럽게 사용할 수 있습니다, 반면 C#과 C++는 별도의 IDE에서 편집해야 합니다. 당신이 정적 언어를 좋아한다면, C#과 C++을 선택하세요.

GDScript

GDScript basics 는 앞에서 말했듯, Godot에서 사용되는 메인 언어입니다. 이 언어는 다른 언어들에 비해 Godot와의 높은 통합성으로 인해 갖는 장점이 있습니다:

  • 간단하고, 우아하고, 그리고 디자인 면에서도 Lua, Python, Squirrel 등과 같은 다른 언어들과 비슷합니다.
  • 매우 빠르게 불러오고 컴파일됩니다.
  • 에디터 통합은 노드, 시그널, 그리고 작업 중인 씬과 관련된 많은 항목들의 코드 완성을 작업하는 즐거움입니다.
  • (Vectors, transforms 등과 같은) 많은 벡터 타입을 지니고 있어서, 선형 대수를 많이 사용하는데 효율적입니다.
  • 정적 언어처럼 효율적인 여러 스레드를 지원합니다 - 이것이 Lua, Squirrel 등의 가상 머신을 피하게 만든 이유입니다.
  • 가비지 컬렉터를 쓰지 않아서, 작업이 약간의 자동화를 버리는 대신 결정론적으로 처리됩니다 (어쨌든 대부분의 오브젝트들은 참조 카운트됩니다).
  • 더 많은 퍼포먼스가 필요하다면, 엔진을 전부 다시 컴파일하지 않아도 (GDNative를 통해) C++의 코드 섹션을 쉽게 최적화할 수 있습니다.

아직 프로그래밍에 경험이 없고 특히 동적 언어를 정하지 않았다면, GDScript로 시작해보세요!

VisualScript

3.0을 시작하면서, Godot는 Visual Scripting 을 제공합니다. 이것은 "블록과 연결" 언어의 일반적인 구현이지만, Godot의 작동 방식에 맞게 조정됬습니다.

Visual scripting은 비 프로그래머 뿐만 아니라 경험 있는 프로그래머도 게임 디자이너나 아티스트와 같은 사람들이 코드 일부분을 보기 쉽게 만들어 주는 멋진 도구입니다.

프로그래머에게는 이걸로 상태 시스템이나 사용자 지정 시각적 노드 워크플로우를 만들 수도 있습니다 - 예를 들면 대화 시스템이죠.

.NET / C#

마이크로소프트의 C#은 게임 개발자들 사이에서 사랑받는 언어이기에, 우리는 이걸 공식 지원합니다. C#은 많은 코드들로 작성된 완전한 언어로, 마이크로소프트의 풍부한 도움에 우리는 지원하게 되었습니다.

비록 가비지 컬랙터에 대해 알아야 하지만, 이것은 퍼포먼스와 사용의 용이성 두가지를 절충하고 있습니다.

Godot가 Mono .NET 런타임을 사용하기 때문에, 이론적으로 어떤 제 3자 .NET 라이브러리나 프레임워크라도 Godot의 스크립트에 사용될 수 있고, F#, Boo, ClojureCLR와 같은 공용 언어 인프라 호환 프로그래밍 언어 또한 가능합니다. 하지만, 실제로는 C#만 공식으로 지원하는 .NET 옵션입니다.

GDNative / C++

마침내 3.0 출시에 가장 빛나는 추가 요소: GDNative는 Godot를 다시 컴파일 하지 않아도 (심지어 재시작을 하지않아도) C++에서 스크립팅 할 수 있게 만듭니다.

모든 C++버전을 사용할 수 있으며, 내부 C API Bridge를 사용하여 생성된 공유 라이브러리의 컴파일러 브랜드와 버전을 완벽하게 혼합할 수 있습니다.

이 언어는 훌륭한 성능을 가지며, GDScript또는 VisualScript을 통해 다른 부분을 작성할 수 있으므로 게임 전체에서 사용할 필요는 없습니다. 그러나 이 API는 명확하고 사용하기 쉽습니다. 이 API의 대부분은 Godot의 실제 C++API와 유사하기 때문이죠.

GDNative 인터페이스를 통해 더 많은 언어를 사용할 수 있지만, 이 인터페이스에 대한 공식적인 지원은 없습니다.

씬 스크립팅 하기

본 튜토리얼의 나머지 부분에서는 버튼과 레이블로 구성된 GUI 씬을 설정합니다. 여기서 버튼을 누르면 라벨이 업데이트됩니다. 이를 통해 다음을 증명할 수 있습니다:

  • 스크립트를 작성하여 노드에 연결함.
  • 시그널을 통해 UI 요소들을 연결함.
  • 씬의 다른 노드에 액세스 할 수 있는 스크립트를 작성함.

계속하기 전에 GDScript basics 레퍼런스를 반드시 읽어 보시기 바랍니다. 이 언어는 간단하게 설계되었고, 참조가 짧기 때문에 개념을 파악하는 데 몇 분 정도 밖에 걸리지 않습니다.

씬 설정하기

씬 탭에서 액세스 하는 "하위 노드 추가" 대화 상자를 사용하여 (아니면 Ctrl+A 를 눌러), 다음 노드로 계층을 생성합니다:

  • 패널(Panel)
    • 라벨(Label)
    • 버튼(Button)

씬 트리는 다음과 같아야 합니다:

../../_images/scripting_scene_tree.png

2D 에디터를 사용하여 Button 과 Label을 아래 이미지와 같이 배치하고 크기를 조정합니다. 인스펙터 탭에서 텍스트를 설정할 수 있습니다.

../../_images/label_button_example.png

마지막으로, sayhello.tscn 와 같은 이름으로 씬을 저장합니다.

스크립트 추가하기

패널 노드에서 마우스 오른쪽 버튼을 클릭하고, 메뉴에서 "스크립트 붙이기"를 선택합니다:

../../_images/add_script.png

노드 스크립트 붙이기 대화 상자가 나타날 겁니다. 여기서 스크립트 언어, 클래스 명 및 기타 관련 옵션을 설정할 수 있습니다.

GDScript에서 파일 자체는 클래스를 나타내므로 클래스 이름 필드는 편집이 불가능합니다.

스크립트를 연결할 노드는 패널이므로 상속받는 위치는 "Panel"로 자동으로 채워집니다. 스크립트의 목표가 패널 노드의 기능을 확장하는 것이기 때문에, 이것이 우리가 원하는 것입니다.

마지막으로 스크립트 경로 이름을 입력하고 생성을 선택합니다:

../../_images/script_create.png

그런 다음 스크립트가 생성되어 노드에 추가됩니다. 이 아이콘은 씬 탭의 노드 옆에 있는 "스크립트 열기" 아이콘 뿐만 아니라 인스펙터 아래에 스크립트 속성에서도 볼 수 있습니다:

../../_images/script_added.png

스크립트를 편집하려면 위 이미지에 강조 표시된 두 버튼 중 하나를 선택합니다. 그러면 스크립트 에디터로 이동하고, 기본 템플릿이 나타납니다:

../../_images/script_template.png

안에는 많은 내용이 있지 않습니다. _ready() 함수는 노드와 모든 자식들이 활성화된 씬으로 들어갈 때 호출됩니다. 참고: _ready() 는 생성자가 아닙니다. 대신에 생성자는 _init() 입니다.

스크립트의 역할

스크립트는 노드에 동작을 추가합니다. 노드가 작동하는 방식뿐만 아니라 하위 노드, 상위 노드, 형제 노드 등과 상호 작용하는 방식을 제어하는 데 사용됩니다. 스크립트는 노드에게, 그 노드의 함수들을 상속해줍니다.

../../_images/brainslug.jpg

시그널 다루기

시그널은 특정한 종류의 작업이 발생할 때 "방출(emit)" 되며 스크립트 인스턴스의 모든 기능에 연결될 수 있습니다. 시그널은 대부분 GUI노드에서 사용되며, 다른 노드에도 시그널이 있고 사용자 스크립트에서 커스텀 시그널을 정의할 수도 있습니다.

이 단계에서 우리는 "pressed" 시그널을 커스텀 기능에 연결할 겁니다. 먼저 연결 형식을 지정한 후에 두 번째로 커스텀 기능을 정의합니다. Godot에서는 연결하기 위한 2가지 방법을 제공합니다: 에디터에서 제공하는 시각적 인터페이스나 코드를 통해 사용자 지정 기능에 연결할 수 있습니다.

이 튜토리얼 시리즈의 나머지 부분에서는 코드 메서드를 사용하겠습니다만, 나중에 참고하기 위한 에디터 인터페이스가 어떻게 작동하는지에 대해서는 설명하겠습니다.

씬 트리에서 버튼 노드를 고르고 "노드" 탭을 선택하세요. 그런 후, "시그널"이 선택되어 있는지 확인하세요.

../../_images/signals.png

"BaseButton" 밑에 있는 "pressed()"를 고르고 오른쪽 아래 "연결하기..." 버튼을 누르신다면, 연결 생성 대화 상자가 열립니다.

../../_images/connect_dialogue.png

왼쪽 아래에 당신이 연결을 만드는데 필요한 핵심 사항이 있습니다: 사용하길 원하는 메서드를 실행할 (NodePath로 정의된) 하나의 노드와 그 메서드의 이름입니다.

왼쪽 위 구역은 씬에 있는 노드들을 보여주며 신호를 내보내는(방출하는) 노드 이름은 빨갛게 표시됩니다. "Panel" 노드를 선택해봅시다. 노드를 선택하면, 밑에 NodePath가 자동으로 방출하는 노드부터 선택된 노드까지의 경로를 가리키기 위해 자동으로 업데이트됩니다.

기본적으로, 메서드의 이름은 방출하는 노드의 이름에 포함되어서 "_on_[EmitterNode]_[signal_name]"으로 표시됩니다 (이 경우에는 "Button"이죠). 만일 "함수 만들기" 버튼이 체크되어 있다면, 에디터는 연결을 설정하기 전에 함수를 생성할 겁니다.

그리고 이것으로 화면 인터페이스를 사용하는 법에 관한 강좌를 마칩니다. 하지만, 이것은 스크립팅 튜토리얼이므로, 학습을 위해 수동 프로세스를 알아봅시다!

이것을 달성하기 위해서, 우리는 Godot 프로그래머들이 가장 잘 사용하는 함수를 소개하겠습니다: Node.get_node()입니다. 이 함수는 스크립트를 소유한 노드라면 씬 내 어느 위치라도 경로를 사용해서 노드를 가져옵니다.

편의상, extend Panel 아래에 있는 모든 것을 지우세요. 당신이 나머지를 수동으로 채울 것입니다.

버튼과 라벨은 스크립트가 붙어있는 패널의 자식이기 때문에, 밑의 _ready() 함수를 따라 타이핑해서 버튼을 가져올 수 있습니다:

func _ready():
    get_node("Button")
public override void _Ready()
{
    GetNode("Button")
}

그런 다음 버튼을 누르면 호출되는 기능을 작성합니다:

func _on_Button_pressed():
    get_node("Label").text = "HELLO!"
public void _OnButtonPressed()
{
    GetNode<Label>("Label").Text = "HELLO!";
}

마지막으로 버튼의 "pressed" 신호를 Object.connect()를 사용하여 _ready()에 연결합니다.

func _ready():
    get_node("Button").connect("pressed", self, "_on_Button_pressed")
public override void _Ready()
{
    GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
}

최종 스크립트는 다음과 같아야 합니다:

extends Panel

func _ready():
    get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
    get_node("Label").text = "HELLO!"
using Godot;

// IMPORTANT: the name of the class MUST match the filename exactly.
// this is case sensitive!
public class sayhello : Panel
{
    public override void _Ready()
    {
        GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
    }

    public void _OnButtonPressed()
    {
        GetNode<Label>("Label").Text = "HELLO!";
    }
}

씬을 실행하고 버튼을 누르면 다음과 같은 결과를 얻을 수 있습니다:

../../_images/scripting_hello.png

어머, 안녕하세요! 첫번째 씬을 스크립팅 하신 것을 축하 드립니다.

주석

이 튜토리얼에 관한 흔한 오해는 get_node(path) 가 작동하는 방법입니다. 주어진 노드의 경우, get_node(path) 는 직계 자식을 검색합니다. 위 코드에서 버튼은 패널의 자식임을 의미합니다. 만일 버튼이 패널이 아닌 라벨의 하위 항목이라면 얻게 될 코드는 다음과 같습니다:

# Not for this case,
# but just in case.
get_node("Label/Button")
// Not for this case,
// but just in case.
GetNode("Label/Button")

또한 노드는 유형이 아닌 이름으로 참조됩니다.

주석

연결 대화 상자의 오른쪽 패널은 특정 값을 연결된 함수의 매개변수에 바인딩 하기 위한 것입니다. 다른 유형의 값을 추가하거나 제거할 수 있습니다.

코드 접근도 기본적으로 비어 있는 4번째 배열 매개 변수를 사용하여 이를 가능하게 합니다. 더 많은 정보를 원하시면 언제든지 Object.connect 방법에 대해 읽어 보시기 바랍니다.