GDScript 기초

소개

GDScript는 콘텐츠를 만드는 데 사용되는 동적 타입 고급 프로그래밍 언어입니다. Python과 비슷한 문법을 사용합니다(블록이 들여쓰기 기반이고, 많은 키워드가 비슷합니다). 이 언어의 목적은 Godot 엔진에 최적화되고 긴밀하게 통합되어 컨텐츠 생성 및 통합을 위한 뛰어난 유연성을 제공하는 것입니다.

역사

참고

GDScript의 역사에 대한 문서는 자주 묻는 질문으로 이동되었습니다.

GDScript 예제

어떤 사람들은 구문을 살펴봄으로써 더 잘 배울 수 있습니다. 그래서 여기에 간단한 GDScript 예제가 있습니다.

# A file is a class!

# Inheritance

extends BaseClass

# (optional) class definition with a custom icon

class_name MyClass, "res://path/to/optional/icon.svg"


# Member variables

var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var typed_var: int
var inferred_type := "String"

# Constants

const ANSWER = 42
const THE_NAME = "Charly"

# Enums

enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# Built-in vector types

var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)


# Function

func some_function(param1, param2):
    var local_var = 5

    if param1 < local_var:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    var local_var2 = param1 + 3
    return local_var2


# Functions override functions with the same name on the base/parent class.
# If you still want to call them, use '.' (like 'super' in other languages).

func something(p1, p2):
    .something(p1, p2)


# Inner class

class Something:
    var a = 10


# Constructor

func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

이전에 C, C++, C#과 같은 정적 타입 언어에 대한 경험이 있지만 한 번도 동적 타입 언어를 써 본 적이 없다면, 이 튜토리얼을 읽는 것이 좋습니다: GDScript: 동적 언어 소개.

언어

여기서부터 GDScript의 개요입니다. 어떤 메서드가 배열 혹은 다른 오브젝트에 이용할 수 있는지와 같은 세부 사항은 링크된 클래스 설명을 확인해주세요.

식별자(Identifier)

알파벳 문자로 제한되는 문자열 (a부터 z, A부터 Z), 숫자 (0부터 9), _는 식별자입니다. 추가로 식별자는 숫자로 시작할 수 없습니다. 식별자는 대소문자를 구분합니다 (fooFOO와 다릅니다).

키워드(Keyword)

다음은 언어에서 지원하는 키워드 목록입니다. 키워드는 예약된 단어(토큰)이기 때문에, 식별자로 사용할 수 없습니다. 다음 섹션에 나열된 (in, not, and 혹은 or 와 같은) 연산자와 내장 타입 이름 역시 예약된 단어입니다.

키워드는 GDScript tokenizer에 정의되어 있습니다. 정체가 궁금하다면 확인해보세요.

키워드

설명

if

if/else/elif를 참고하세요.

elif

if/else/elif를 참고하세요.

else

if/else/elif를 참고하세요.

for

for를 참고하세요.

while

while을 참고하세요.

match

match를 참고하세요.

break

현재의 for 또는 while 루프 실행을 끝냅니다.

continue

즉시 for 또는 while 루프의 다음 반복으로 건너뜁니다.

pass

명령문이 문법적으로는 필요하지만 실행할 만한 코드가 마땅치 않을 때 사용됩니다. 예: 빈 함수.

return

함수에서 값을 반환합니다.

class

내부 클래스를 정의합니다.

class_name

Defines a class name and optional icon for your script.

extends

현재 클래스로 확장할 클래스를 정의합니다.

is

변수가 주어진 클래스를 확장하는지, 혹은 변수가 주어진 내장 유형인지 여부를 테스트합니다.

as

가능하다면 주어진 타입으로 값을 캐스트합니다.

self

현재 클래스 인스턴스를 참조합니다.

tool

에디터에서 스크립트를 실행합니다.

signal

시그널을 정의합니다.

func

함수를 정의합니다.

static

정적 함수를 정의합니다. 정적 멤버 변수를 허용하지 않습니다.

const

상수를 정의합니다.

enum

열거형을 정의합니다.

var

변수를 정의합니다.

onready

스크립트가 붙은 노드이고 노드의 자식이 씬 트리의 일부분인 경우, 변수를 초기화합니다.

export

변수를 리소스와 함께 저장하고 편집기에서 보고 수정할 수 있게 만듭니다.

setget

변수에 대한 setter(세터)와 getter(게터) 함수를 정의합니다.

breakpoint

디버거 브레이크포인트용 에디터 도우미.

preload

클래스나 변수를 미리 불러옵니다(Preload). 리소스로 취급되는 클래스를 참고하세요.

yield

코루틴(Coroutine)을 지원합니다. yield를 갖는 Coroutine(코루틴)을 참고하세요.

assert

조건을 가정(Assert)하고 실패 시 오류를 기록합니다. 디버그가 아닌 빌드에서는 무시됩니다. Assert 키워드를 참고하세요.

remote

네트워킹 원격 프로시저 호출(RPC: Remote Procedure Call) 어노테이션(annotation). 하이 레벨 멀티플레이어 문서를 참고하세요.

master

네트워킹 원격 프로시저 호출(RPC: Remote Procedure Call) 어노테이션(annotation). 하이 레벨 멀티플레이어 문서를 참고하세요.

puppet

네트워킹 원격 프로시저 호출(RPC: Remote Procedure Call) 어노테이션(annotation). 하이 레벨 멀티플레이어 문서를 참고하세요.

remotesync

네트워킹 원격 프로시저 호출(RPC: Remote Procedure Call) 어노테이션(annotation). 하이 레벨 멀티플레이어 문서를 참고하세요.

mastersync

네트워킹 원격 프로시저 호출(RPC: Remote Procedure Call) 어노테이션(annotation). 하이 레벨 멀티플레이어 문서를 참고하세요.

puppetsync

네트워킹 원격 프로시저 호출(RPC: Remote Procedure Call) 어노테이션(annotation). 하이 레벨 멀티플레이어 문서를 참고하세요.

PI

PI 상수.

TAU

TAU 상수.

INF

무한대 상수. 비교할 때 사용됩니다.

NAN

NAN (Not A Number, 숫자 아님) 상수. 비교할 때 사용됩니다.

연산자(Operator)

다음은 지원하는 연산자 목록과 우선 순위입니다.

연산자

설명

x[index]

구독(Subscription) (가장 높은 우선 순위)

x.attribute

속성 참조

foo()

함수 호출

is

인스턴스 유형 검사기

~

비트 단위 NOT

-x

음수 / 단항 부정

* / %

곱하기 / 나누기 / 나머지

이러한 연산자는 C++와 동일합니다. 정수 나눗셈은 소수 부분을 잘라버리고 % 연산자는 int(부동 소수점의 경우 "fmod")에만 사용할 수 있으며 형식 문자열에도 추가로 사용됩니다

+

더하기 / 배열의 연결

-

빼기

<< >>

비트 시프트

&

비트 단위 AND

^

비트 단위 XOR

|

비트 단위 OR

< > == != >= <=

비교

in

내용 테스트

! not

불리언 NOT

and &&

불리언 AND

or ||

불리언 OR

if x else

삼항 if/else

as

타입 캐스팅

= += -= *= /= %= &= |= <<= >>=

할당 (가장 낮은 우선 순위)

리터럴(Literal)

리터럴

유형

45

기본 10진법 정수

0x8f51

기본 16진법 정수

0b101010

기본 2진법 정수

3.14, 58.1e-10

부동 소수점 숫자 (실수)

"안녕하세요", "안녕"

문자열

"""안녕하세요"""

여러 줄 문자열

@"Node/Label"

노드 경로(NodePath) 혹은 문자열 이름(StringName)

$NodePath

get_node("NodePath")의 짧은 표현

정수와 부동 소수점 수는 가독성을 높이기 위해 숫자를 _로 구분할 수 있습니다. 숫자를 쓰는 다음 방법은 모두 유효합니다:

12_345_678  # Equal to 12345678.
3.141_592_7  # Equal to 3.1415927.
0x8080_0000_ffff  # Equal to 0x80800000ffff.
0b11_00_11_00  # Equal to 0b11001100.

주석(Comment)

#부터 줄 끝까지는 주석으로 간주되어 무시됩니다.

# This is a comment.

내장 타입(Built-in type)

내장 타입은 스택에 할당됩니다. 이들은 값으로 전달됩니다. 즉, 복사본은 각 할당에서, 혹은 타입이 인수로서 함수로 전달될 때 만들어집니다. 유일한 예외 사항은 배열(Array)딕셔너리(Dictionary)로, 이 때는 참조로 전달되기 때문에 공유됩니다. (PoolByteArray와 같은 풀 배열(pooled array)은 여전히 값으로 전달됩니다.)

기본 내장 타입

GDScript의 변수에는 여러 내장 타입을 할당할 수 있습니다.

null

null은 정보를 가지지 않는 빈 데이터 타입으로 어떤 값도 할당할 수 없습니다.

bool

"불리언(Boolean)"의 약자로, 오직 true 또는 false값만 가집니다.

int

"정수(Integer)"의 약자로, 모든 숫자 (양수와 음수)를 저장합니다. 64비트 값으로 저장하며, 이는 C++에서 "int64_t"와 같습니다.

float

부동 소수점 값을 사용해 소수를 포함하는 실수를 저장합니다. C++의 "double"에 해당하는 64비트 값으로 저장됩니다. 참고: 현재 Vector2, Vector3, PoolRealArray와 같은 데이터 구조는 32비트 단정도(Single-precision) "float" 값을 저장합니다.

String

유니코드 형식의 문자열입니다. 문자열에는 다음과 같은 이스케이프 시퀀스가 포함될 수 있습니다.

Escape sequence

설명

\n

줄 바꿈(라인 피드)

\t

수평 탭 문자

\r

캐리지 리턴

\a

경고(비프/벨 음)

\b

백스페이스

\f

폼 피드(페이지 나누기)

\v

수직 탭 문자

\"

큰 따옴표

\'

작은 따옴표

\\

백슬래시

\uXXXX

유니코드 코드 포인트 XXXX (16진수, 대소문자 구분 안함)

GDScript는 GDScript 형식 문자열(format strings)도 지원합니다.

벡터 내장 타입

Vector2

2D 벡터 타입으로 x, y 필드를 가집니다. 배열로 접근할 수도 있습니다.

Rect2

2D 사각형 타입으로 두 개의 벡터 필드 position, size를 가집니다. 또한 position + size값인 end 필드를 가집니다.

Vector3

3D 벡터 타입으로 x, y, z 필드를 가집니다. 배열로 접근할 수도 있습니다.

Transform2D

3x2 행렬(Matrix)로 2D 변형에 사용됩니다.

Plane

표준화된 형태의 3D 평면(Plane) 타입으로 normal 벡터와 d 스칼라 거리 필드를 가집니다.

Quat

사원수(Quaternion)는 3D 회전을 표현하기 위해 사용되는 데이터 타입입니다. 회전 값을 보간하는 용도로 사용됩니다.

AABB

축이 정렬된 경계 사각형(또는 3D 상자)로 두 개의 벡터 필드 position, size를 가집니다. 또한 position + size값인 end 필드를 가집니다.

Basis

3x3 행렬로 3D 회전과 크기 조정에 사용됩니다. 3개의 벡터 필드(x, y, z)를 가집니다. 3D 벡터의 배열로 접근할 수도 있습니다.

Transform

3D 변형(Transform)으로, Basis 필드 basis, Vector3 필드 origin을 가집니다.

엔진 내장 타입

Color

색상(Color) 데이터 타입으로 r, g, b, a 필드를 가집니다. 색조/채도/명도 값을 h, s, v로 접근할 수도 있습니다.

NodePath

노드에 대한 컴파일된 경로로, 주로 씬 시스템에서 사용됩니다. 쉽게 문자열로 할당하거나 문자열로부터 할당될 수 있습니다.

RID

리소스 ID (RID). 서버는 불투명한 데이터를 참조하기 위해 제네릭 RID를 사용합니다.

Object

내장 타입이 아닌 모든 것의 기본 클래스.

컨테이너(Container) 내장 타입

Array

다른 배열(Array)이나 딕셔너리(Dictionary)를 포함한 임의 오브젝트 타입의 일반적인 시퀀스(Sequence)입니다(아래를 참고하세요). 배열의 크기는 동적으로 조절할 수 있습니다. 배열은 인덱스 0부터 번호를 매깁니다. 번호를 음수로 하면 배열의 끝부터 셉니다.

var arr = []
arr = [1, 2, 3]
var b = arr[1] # This is 2.
var c = arr[arr.size() - 1] # This is 3.
var d = arr[-1] # Same as the previous line, but shorter.
arr[0] = "Hi!" # Replacing value 1 with "Hi!".
arr.append(4) # Array is now ["Hi!", 2, 3, 4].

GDScript 배열은 속도를 위해 선형적으로 메모리에 할당됩니다. (원소가 수만 개 이상인) 큰 배열은 메모리 단편화를 발생시킬 수 있습니다. 이것이 염려된다면 특수 타입 배열을 사용할 수 있습니다. 이 배열에는 하나의 데이터 타입만 들어갈 수 있습니다. 메모리 단편화를 방지할 뿐더러, 더 적은 메모리를 사용하지만 원자적이고 기본 배열보다 더 느리게 실행됩니다. 따라서 큰 데이터 집합을 사용할 때만 추천합니다:

Dictionary

고유 키로 참조되는 값들을 가지는 연관 컨테이너입니다.

var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
d["Hi!"] = 0
d = {
    22: "value",
    "some_key": 2,
    "other_key": [2, 3, 4],
    "more_key": "Hello"
}

Lua 스타일의 테이블 구문도 지원됩니다. Lua 스타일은 : 대신 =을 사용하고 문자열 키를 표시하기 위해 따옴표를 사용하지 않습니다(쓸 문자가 약간 줄어듭니다). 그러나 이 형식으로 작성된 키 이름은 (다른 모든 GDScript 식별자와 마찬가지로) 숫자로 시작할 수 없습니다.

var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

존재하는 딕셔너리에 키를 추가하려면, 기존 키와 같은 방식으로 접근한 뒤 할당해야 합니다:

var d = {} # Create an empty Dictionary.
d.waiting = 14 # Add String "waiting" as a key and assign the value 14 to it.
d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value.
d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it.

var test = 4
# Prints "hello" by indexing the dictionary with a dynamic key.
# This is not the same as `d.test`. The bracket syntax equivalent to
# `d.test` is `d["test"]`.
print(d[test])

참고

대괄호 구문은 사전뿐만 아니라 모든 Object의 속성에 접근하는 데 사용할 수 있습니다. 존재하지 않는 속성을 인덱싱하려고 할 때 스크립트 오류가 발생한다는 점에 유의하세요. 이를 방지하려면 Object.get()Object.set() 메서드를 대신 사용하세요.

데이터(Data)

변수(Variable)

변수는 클래스 멤버로 존재할 수도 있고 함수에 지역적으로 존재할 수도 있습니다. 이들은 var 키워드로 생성되며 초기화할 때 값을 할당받을 수도 있습니다.

var a # Data type is 'null' by default.
var b = 5
var c = 3.8
var d = b + c # Variables are always initialized in order.

변수는 타입을 지정할 수도 있습니다. 타입을 지정하면 항상 같은 타입을 가지도록 강제되며, 호환되지 않는 값을 할당하려고 하면 오류가 발생합니다.

타입은 변수 선언 시 변수 이름 뒤에 :(콜론) 기호를 사용해 지정하고 그 뒤에 타입을 지정합니다.

var my_vector2: Vector2
var my_node: Node = Sprite.new()

변수가 선언과 동시에 초기화되면 타입을 유추할 수 있으므로 타입 이름을 생략할 수 있습니다:

var my_vector2 := Vector2() # 'my_vector2' is of type 'Vector2'.
var my_node := Sprite.new() # 'my_node' is of type 'Sprite'.

타입 유추는 할당된 값에 정의된 타입이 있는 경우에만 가능하며, 그렇지 않으면 오류가 발생합니다.

올바른 타입은 다음과 같습니다:

  • 내장 타입(Array, Vector2, int, String 등).

  • 엔진 클래스 (Node, Resource, Reference 등).

  • 스크립트 리소스가 포함된 상수 이름(const MyScript = preload("res://my_script.gd")를 선언한 경우에 MyScript가 여기에 속합니다).

  • 같은 스크립트의 스코프 안에 있는 다른 클래스들(같은 스코프에서 class InnerClass 안에 class NestedClass를 선언한 경우 InnerClass.NestedClass가 여기에 속합니다).

  • class_name 키워드로 선언된 스크립트 클래스.

캐스팅(Casting)

타입이 지정된 변수에는 호환 가능한 타입의 값을 할당해야 합니다. 값을 특정 타입으로 강제 변환해야 하는 경우, 특히 객체 타입의 경우 캐스팅 연산자 as를 사용할 수 있습니다.

오브젝트 타입 간 캐스팅에서 오브젝트의 값 타입이 캐스팅할 타입과 같거나 캐스팅할 타입의 서브타입인 경우 캐스팅하면 동일한 오브젝트가 생성됩니다.

var my_node2D: Node2D
my_node2D = $Sprite as Node2D # Works since Sprite is a subtype of Node2D.

값이 캐스팅 타입의 서브타입이 아니라면 캐스팅 연산 결과값은 null이 됩니다.

var my_node2D: Node2D
my_node2D = $Button as Node2D # Results in 'null' since a Button is not a subtype of Node2D.

내장 타입의 경우 가능하다면 강제로 타입을 변환하지만, 불가능하다면 오류를 발생합니다.

var my_int: int
my_int = "123" as int # The string can be converted to int.
my_int = Vector2() as int # A Vector2 can't be converted to int, this will cause an error.

캐스팅은 씬 트리와 상호 작용할 때 더 나은 타입 안전한 변수를 만드는 데 유용합니다:

# Will infer the variable to be of type Sprite.
var my_sprite := $Character as Sprite

# Will fail if $AnimPlayer is not an AnimationPlayer, even if it has the method 'play()'.
($AnimPlayer as AnimationPlayer).play("walk")

상수

상수는 게임이 실행 중일 때 변경할 수 없는 값입니다. 상수 값은 컴파일 시간에 정해져야 합니다. const 키워드를 사용하면 상수 값에 이름을 지정할 수 있습니다. 선언된 상수에 값을 할당하려고 하면 오류가 발생합니다.

값이 변경되지 않는다면 상수를 사용하는 것이 좋습니다.

const A = 5
const B = Vector2(20, 20)
const C = 10 + 20 # Constant expression.
const D = Vector2(20, 30).x # Constant expression: 20.
const E = [1, 2, 3, 4][0] # Constant expression: 1.
const F = sin(20) # 'sin()' can be used in constant expressions.
const G = x + 20 # Invalid; this is not a constant expression!
const H = A + 20 # Constant expression: 25 (`A` is a constant).

상수의 타입은 할당되는 값에서 유추할 수 있지만 명시적으로 지정할 수도 있습니다:

const A: int = 5
const B: Vector2 = Vector2()

호환되지 않는 타입의 값을 할당하면 오류가 발생합니다.

참고

배열과 딕셔너리는 참조로 전달되기 때문에 상수는 "평평"합니다. 즉, 상수 배열이나 딕셔너리를 선언하면 나중에 수정할 수 있습니다. 그러나 다른 값으로 재할당할 수 없습니다.

열거형(Enum)

열거형은 기본적으로 상수의 축약형으로, 연속적인 정수를 일부 상수에 할당할 때 꽤 유용합니다.

열거형에 이름을 전달하면 모든 키를 해당 이름의 상수 딕셔너리 안에 넣습니다.

중요

Godot 3.1부터는, 이름있는 열거형의 키(Key)들은 전역 상수로 등록되지 않습니다. 키에 접근하려면 열거형의 이름이 앞에 있어야합니다(이름.KEY). 아래의 예제를 참고하세요.

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}
# Is the same as:
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}
# Access values with State.STATE_IDLE, etc.

함수(Function)

함수는 항상 클래스에 속합니다. 변수 조회 범위 우선 순위는 지역(local) → 클래스 멤버 → 전역 순입니다. self 변수는 항상 사용할 수 있고 클래스 멤버에 접근할 수 있는 옵션으로 제공되지만 항상 필수적이지는 않습니다(Python과 달리 함수의 첫 번째 인수로 보내면 됩니다).

func my_function(a, b):
    print(a)
    print(b)
    return a + b  # Return is optional; without it 'null' is returned.

함수는 언제든지 반환(return)할 수 있습니다. 기본 반환 값은 null입니다.

그리고 함수는 인수와 반환 값의 타입을 지정할 수 있습니다. 인수의 타입은 변수와 비슷한 방식으로 추가할 수 있습니다:

func my_function(a: int, b: String):
    pass

함수 인수가 기본 값을 가진다면 타입을 유추할 수 있습니다:

func my_function(int_arg := 42, String_arg := "string"):
    pass

함수의 반환 타입은 화살표 토큰 (->)을 사용헤 인수 목록 뒤에 지정할 수 있습니다:

func my_int_function() -> int:
    return 0

반환 타입이 있는 함수는 반드시 그에 맞는 값을 반환해야 합니다. 타입을 void로 설정하면 함수는 아무 것도 반환하면 안 됩니다. Void 함수는 return 키워드로 함수에서 일찍 반환할 수 있지만, 값을 반환하면 안 됩니다.

func void_function() -> void:
    return # Can't return a value

참고

void가 아닌 함수는 항상 값을 반환해야 하므로 코드에 분기 문이 있는 경우(예: if/else 구조) 가능한 모든 경로에서 반환해야 합니다. 예를 들어, if 블록 안에 return이 있지만 그 이후에는 없는 경우 블록이 실행되지 않으면 함수가 반환할 유효한 값을 가지지 않기 때문에 에디터에서 오류가 발생합니다.

함수 참조하기(Referencing functions)

Python과 달리, 함수는 GDScript에서 최상위 클래스 오브젝트가 아닙니다. 즉, 함수를 변수에 저장할 수 없고, 다른 함수에 인수로 전달되거나 다른 함수로부터 반환될 수 없습니다. 성능 문제 때문입니다.

실행 시간에 함수를 이름으로 참조하려면 (예: 함수를 변수로 저장하거나, 다른 함수에 인수로 전달하는 경우) call이나 funcref 도우미를 사용해야 합니다:

# Call a function by name in one step.
my_node.call("my_function", args)

# Store a function reference.
var my_func = funcref(my_node, "my_function")
# Call stored function reference.
my_func.call_func(args)

정적 함수(Static functions)

함수를 정적으로 선언할 수 있습니다. 함수가 정적이면 인스턴스 멤버 변수나 self로 접근할 수 없습니다. 주로 도우미 함수 라이브러리를 만드는 데 유용합니다:

static func sum2(a, b):
    return a + b

명령문(Statement)과 제어 흐름(Control Flow)

명령문은 표준으로 할당(Assignment), 함수 호출(Function Call), 제어 흐름 (Control Flow) 구조 등이 될 수 있습니다. 명령문 구분 기호인 ;의 사용은 자유입니다.

if/else/elif

간단한 조건은 if/else/elif 구문을 사용해 만들 수 있습니다. 조건문 주변에 괄호를 씌워도 되지만 필수는 아닙니다. 탭 기반 들여쓰기의 특성을 감안하면 else/if 대신 elif를 사용해서 들여쓰기 수준을 유지할 수 있습니다.

if [expression]:
    statement(s)
elif [expression]:
    statement(s)
else:
    statement(s)

간단한 명령문은 조건처럼 같은 줄에 적을 수 있습니다:

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

때로는 불리언(Boolean) 표현식을 기반으로 초기 값을 다르게 할당해야 하는 경우가 있습니다. 이 경우에는 삼항 표현식을 쓰면 됩니다:

var x = [value] if [expression] else [value]
y += 3 if y < 10 else -1

삼항 if 표현식은 2개 이상의 분기를 처리하기 위해 중첩될 수 있습니다. 삼항 if 표현식을 중첩할 때 가독성을 유지하기 위해 전체 표현식을 여러 줄로 감싸는 것이 좋습니다:

var count = 0

var fruit = (
        "apple" if count == 2
        else "pear" if count == 1
        else "banana" if count == 0
        else "orange"
)
print(fruit)  # banana

# Alternative syntax with backslashes instead of parentheses (for multi-line expressions).
# Less lines required, but harder to refactor.
var fruit_alt = \
        "apple" if count == 2 \
        else "pear" if count == 1 \
        else "banana" if count == 0 \
        else "orange"
print(fruit_alt)  # banana

while

간단한 루프는 while 구문으로 만들 수 있습니다. 루프는 break를 사용해서 끊거나 continue를 사용해서 계속할 수 있습니다:

while [expression]:
    statement(s)

for

배열이나 테이블과 같은 범위의 반복에는 for 루프가 사용됩니다. 배열을 반복할 때 현재 배열 요소는 루프 변수에 저장됩니다. 딕셔너리를 반복할 때 key는 루프 변수에 저장됩니다.

for x in [5, 7, 11]:
    statement # Loop iterates 3 times with 'x' as 5, then 7 and finally 11.

var dict = {"a": 0, "b": 1, "c": 2}
for i in dict:
    print(dict[i]) # Prints 0, then 1, then 2.

for i in range(3):
    statement # Similar to [0, 1, 2] but does not allocate an array.

for i in range(1, 3):
    statement # Similar to [1, 2] but does not allocate an array.

for i in range(2, 8, 2):
    statement # Similar to [2, 4, 6] but does not allocate an array.

for c in "Hello":
    print(c) # Iterate through all characters in a String, print every letter on new line.

for i in 3:
    statement # Similar to range(3)

for i in 2.2:
    statement # Similar to range(ceil(2.2))

match

match 명령문은 프로그램의 실행을 분기하는 데 사용됩니다. 다른 많은 언어에서 볼 수 있는 switch 명령문과 유사하지만 몇 가지 추가 기능을 제공합니다.

기본 문법:

match [expression]:
    [pattern](s):
        [block]
    [pattern](s):
        [block]
    [pattern](s):
        [block]

switch 명령문에 익숙한 사람들을 위한 집중 강좌:

  1. switchmatch로 바꾸세요.

  2. case를 제거하세요.

  3. 모든 break를 제거하세요. 기본적으로 break가 되지 않도록 하고 싶다면, 다른 언어의 fallthrough 키워드처럼 continue를 사용할 수 있습니다.

  4. default를 하나의 밑줄로 변경하세요.

제어 흐름(Control flow):

패턴은 위에서 아래로 매치됩니다. 패턴이 첫 번째로 일치하는 블록이 실행됩니다. 그 후, match 문 아래에서 계속 실행됩니다. continue를 사용해 현재 블록에서 실행을 중지하고 그 아래 패턴에서 추가로 패턴 일치 여부를 확인할 수 있습니다.

6 가지 패턴 유형이 있습니다:

  • 상수 패턴(Constant pattern)

    숫자, 문자열 같은 상수 원시값(Primitive):

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • 변수 패턴(Variable pattern)

    변수/열거형 내용 매치:

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • 와일드카드 패턴(Wildcard pattern)

    이 패턴은 모든 것에 매치됩니다. 하나의 밑줄로 쓸 수 있습니다.

    다른 언어의 switch 명령문의 default와 같은 의미로 쓰입니다:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        _:
            print("It's not 1 or 2. I don't care to be honest.")
    
  • 바인딩 패턴(Binding pattern)

    바인딩 패턴은 새 변수를 도입합니다. 와일드카드 패턴과 마찬가지로 모든 것에 매치됩니다. 그리고 값에 이름을 부여합니다. 이는 특히 배열 패턴과 딕셔너리 패턴에 유용합니다:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        var new_var:
            print("It's not 1 or 2, it's ", new_var)
    
  • 배열 패턴(Array pattern)

    배열을 비교합니다. 배열 패턴의 각 단일 요소도 패턴이므로, 패턴을 중첩할 수 있습니다.

    배열의 길이가 패턴과 같은지 테스트하고, 같지 않다면 패턴이 일치하지 않습니다.

    개방형 배열(Open-ended array): 배열의 마지막 서브패턴을 ..으로 만들어서 패턴보다 배열이 더 커질 수 있습니다.

    각 서브패턴은 쉼표로 구분해야 합니다.

    match x:
        []:
            print("Empty array")
        [1, 3, "test", null]:
            print("Very specific array")
        [var start, _, "test"]:
            print("First element is ", start, ", and the last is \"test\"")
        [42, ..]:
            print("Open ended array")
    
  • 딕셔너리 패턴(Dictionary pattern)

    배열 패턴과 같은 방식으로 작동합니다. 모든 키는 일정한 패턴이어야 합니다.

    딕셔너리의 길이가 패턴과 같은지 테스트하고, 같지 않다면 패턴이 일치하지 않습니다.

    개방형 딕셔너리(Open-ended dictionary): 딕셔너리의 마지막 서브패턴을 ..으로 만들어서 패턴보다 딕셔너리가 더 커질 수 있습니다.

    모든 서브패턴은 쉼표로 구분되어야 합니다.

    값을 지정하지 않으면, 키의 존재 여부만 확인됩니다.

    값 패턴과 키 패턴은 :로 분리됩니다.

    match x:
        {}:
            print("Empty dict")
        {"name": "Dennis"}:
            print("The name is Dennis")
        {"name": "Dennis", "age": var age}:
            print("Dennis is ", age, " years old.")
        {"name", "age"}:
            print("Has a name and an age, but it's not Dennis :(")
        {"key": "godotisawesome", ..}:
            print("I only checked for one entry and ignored the rest")
    
  • 다중 패턴(Multiple patterns)

    여러 패턴을 쉼표로 구분해서 지정할 수 있습니다. 이 패턴들 사이에서 바인딩은 허용되지 않습니다.

    match x:
        1, 2, 3:
            print("It's 1 - 3")
        "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")
    

클래스(Class)

기본적으로 모든 스크립트 파일은 이름 없는 클래스입니다. 이 경우 상대 경로나 절대 경로를 사용한 파일 경로를 통해서만 참조할 수 있습니다. 예를 들어 스크립트 파일의 이름을 characrer.gd로 지은 경우 다음과 같이 참조합니다:

# Inherit from 'Character.gd'.

extends "res://path/to/character.gd"

# Load character.gd and create a new node instance from it.

var Character = load("res://path/to/character.gd")
var character_node = Character.new()

Registering named classes

You can give your class a name to register it as a new type in Godot's editor. For that, you use the class_name keyword. You can optionally add a comma followed by a path to an image, to use it as an icon. Your class will then appear with its new icon in the editor:

# Item.gd

extends Node
class_name Item, "res://interface/icons/item.png"
../../../_images/class_name_editor_register_example.png

경고

스크립트가 res://addons/ 디렉토리에 있고 활성화된 에디터 플러그인의 일부인 경우 class_name이 노드를 **새 노드 만들기(Create New Node) 대화 상자에 표시합니다. 자세한 내용은 :ref:`doc_making_plugins`를 참조하세요.

클래스 파일 예제입니다:

# Saved as a file named 'character.gd'.

class_name Character


var health = 5


func print_health():
    print(health)


func print_this_script_three_times():
    print(get_script())
    print(ResourceLoader.load("res://character.gd"))
    print(Character)

참고

Godot의 클래스 구문은 간결합니다. 멤버 변수나 함수만 포함할 수 있습니다. 정적 함수를 사용할 수 있지만 정적 멤버 변수는 사용할 수 없습니다. 같은 방식으로, 엔진은 인스턴스를 생성할 때마다 변수를 초기화하며 여기에는 배열과 딕셔너리가 포함됩니다. 사용자가 알지 못하는 사이에 별도의 스레드에서 스크립트를 초기화할 수 있기 때문에 스레드 안전 정신이 적용되었습니다.

상속(Inheritance)

(파일로 저장한) 클래스는 여기서 상속될 수 있습니다:

  • 전역 클래스(Global class).

  • 다른 클래스 파일.

  • 다른 클래스 파일의 내부 클래스.

다중 상속은 허용되지 않습니다.

extends 키워드를 사용해 상속합니다:

# Inherit/extend a globally available class.
extends SomeClass

# Inherit/extend a named class file.
extends "somefile.gd"

# Inherit/extend an inner class in another file.
extends "somefile.gd".SomeInnerClass

주어진 인스턴스가 주어진 클래스로부터 상속받았는지 확인하려면, is 키워드를 사용할 수 있습니다:

# Cache the enemy class.
const Enemy = preload("enemy.gd")

# [...]

# Use 'is' to check inheritance.
if entity is Enemy:
    entity.apply_damage()

부모 클래스(즉, 현재 클래스에서 extend한 클래스)에서 함수를 호출하려면 함수 이름 앞에 .을 추가하세요:

.base_func(args)

이는 확장하는 클래스의 함수가 부모 클래스에서 같은 이름을 가진 함수를 대체하기 때문에 특히 유용합니다. 여전히 부모 클래스의 함수를 호출하고 싶다면 앞에 .를 붙일 수 있습니다(다른 언어의 super 키워드같이요):

func some_func(x):
    .some_func(x) # Calls the same function on the parent class.

참고

_init과 같은 기본 함수, _enter_tree, _exit_tree, _process, _physics_process 등과 같은 대부분의 알림은 모든 부모 클래스에서 자동으로 호출됩니다. 재정의할 때 명시적으로 호출할 필요가 없습니다.

클래스 생성자(Class Constructor)

클래스 인스턴스화 시 호출되는 클래스 생성자의 이름은 _init입니다. 앞서 언급했듯이 부모 클래스의 생성자는 클래스를 상속할 때 자동으로 호출됩니다. 따라서 보통 명시적으로 ._init()을 호출할 필요가 없습니다.

위의 .some_func 예제와 같은 일반 함수의 호출과 달리 상속받은 클래스의 생성자가 인수를 가진다면 다음과 같이 전달됩니다:

func _init(args).(parent_args):
   pass

예제를 통해서 더 쉽게 설명해 보겠습니다. 다음 시나리오를 생각해보세요:

# State.gd (inherited class)
var entity = null
var message = null


func _init(e=null):
    entity = e


func enter(m):
    message = m


# Idle.gd (inheriting class)
extends "State.gd"


func _init(e=null, m=null).(e):
    # Do something with 'e'.
    message = m

유의해야 할 몇 가지 사항이 있습니다:

  1. 상속하는 클래스(State.gd)가 인수(이 경우 e)를 가지는 _init 생성자를 정의하는 경우 상속받는 클래스(Idle.gd)는 반드시 _init을 정의해야 하고, State.gd에서 _init로 적절한 매개변수를 전달해야 합니다.

  2. idle.gd는 부모 클래스 State.gd와 다른 개수의 인수를 가질 수 있습니다.

  3. 위의 예제에서 State.gd 생성자로 전달된 eIdle.gd로 전달된 e와 같습니다.

  4. Idle.gd_init 생성자가 0개의 인수를 가지고 아무것도 하지 않더라도 부모 클래스 State.gd에 값을 전달해야 합니다. 이는 변수뿐만 아니라 기본 생성자에서도 리터럴을 전달할 수 있다는 사실을 알려줍니다. 예시:

    # Idle.gd
    
    func _init().(5):
        pass
    

내부 클래스(Inner Class)

클래스 파일은 내부 클래스를 가질 수 있습니다. 내부 클래스는 class 키워드로 정의합니다. 내부 클래스는 ClassName.new() 함수로 인스턴스화 될 수 있습니다.

# Inside a class file.

# An inner class in this class file.
class SomeInnerClass:
    var a = 5


    func print_value_of_a():
        print(a)


# This is the constructor of the class file's main class.
func _init():
    var c = SomeInnerClass.new()
    c.print_value_of_a()

리소스로 취급되는 클래스

파일로 저장된 클래스는 리소스로 취급됩니다. 다른 클래스에서 접근하려면 반드시 디스크에서 불러와야 합니다. 불러오려면 loadpreload 함수를 사용할 수 있습니다 (아래를 참고하세요). 불러온 클래스 리소스를 인스턴스화 하려면 클래스 오브젝트에 new 함수를 호출해야 합니다:

# Load the class resource when calling load().
var MyClass = load("myclass.gd")

# Preload the class only once at compile time.
const MyClass = preload("myclass.gd")


func _init():
    var a = MyClass.new()
    a.some_function()

내보내기(Export)

참고

내보내기에 관한 서술은 GDScript 내보내기(Export)로 옮겨졌습니다.

세터(Setter) / 게터(Getter)

어떤 이유에서든 클래스 멤버 변수가 언제 변경되는지 아는 것은 자주 유용합니다. 어떤 방식으로든 접근을 캡슐화하는 것이 바람직할 수도 있습니다.

이를 위해 GDScript는 setget 키워드를 사용하는 setter/getter 구문을 제공합니다. 변수 정의 바로 뒤에 사용됩니다:

var variable = value setget setterfunc, getterfunc

variable의 값이 (클래스 안이 아닌) 외부 소스에 의해 수정될 때마다 세터 함수(위의 setterfunc)가 호출됩니다. 이는 값이 변경되기 전에 발생합니다. 세터는 새 값으로 무엇을 할지 결정해야 합니다. 반대로, variable에 접근할 때 게터 함수(위의 getterfunc)는 원하는 값을 return해야 합니다. 다음은 예제입니다:

var my_var setget my_var_set, my_var_get


func my_var_set(new_value):
    my_var = new_value


func my_var_get():
    return my_var # Getter must return a value.

세터 또는 게터 함수 둘 중 하나를 생략할 수도 있습니다:

# Only a setter.
var my_var = 5 setget my_var_set
# Only a getter (note the comma).
var my_var = 5 setget ,my_var_get

세터와 게터는 입력값 유효성을 검사하기 위해 툴 스크립트 또는 플러그인에서 에디터로 변수 내보내기를 할 때 유용합니다.

말했듯이 지역적 접근은 세터와 게터를 트리거하지 않습니다. 다음은 이에 대한 예시입니다:

func _init():
    # Does not trigger setter/getter.
    my_integer = 5
    print(my_integer)

    # Does trigger setter/getter.
    self.my_integer = 5
    print(self.my_integer)

툴 모드(Tool mode)

기본적으로 스크립트는 에디터 내에서 실행되지 않으며, 오직 내보낸 속성만 에디터에서 변경할 수 있습니다. 어떤 경우에는 (에디터가 게임 코드를 실행하지 않거나 수동으로 실행하지 않는 한) 에디터 내에서 실행하는 것이 바람직합니다. 이를 위해 tool 키워드가 존재하며, 파일 맨 위에 위치해야 합니다:

tool
extends Button


func _ready():
    print("Hello")

자세한 설명은 편집기에서 코드 실행하기를 참고하세요.

경고

Tool 스크립트에서 queue_free() 또는 free()로 노드를 해제할 때 (특히 해제할 노드가 스크립트 소유자 자체일 때) 주의하세요. Tool 스크립트는 에디터에서 코드를 실행하기 때문에 잘못 사용하면 에디터가 고장날 수 있습니다.

메모리 관리

클래스가 Reference를 상속받은 경우 인스턴스는 더 이상 사용되지 않을 때 해제됩니다. 가비지 컬렉터는 존재하지 않으며 참조 카운팅만 있습니다. 기본적으로 상속을 정의하지 않는 모든 클래스는 Reference를 확장(extend)합니다. 이를 원하지 않는다면 클래스는 수동으로 Object를 상속받고 instance.free()를 호출해야 합니다. 해제할 수 없는 참조 사이클을 피하기 위해 약한 참조를 생성하는 WeakRef 함수가 제공됩니다. 다음은 예시입니다:

extends Node

var my_node_ref

func _ready():
    my_node_ref = weakref(get_node("MyNode"))

func _this_is_called_later():
    var my_node = my_node_ref.get_ref()
    if my_node:
        my_node.do_something()

또는, 참조를 사용하지 않을 때 is_instance_valid(instance)를 사용해서 오브젝트가 해제되었는지 확인할 수 있습니다.

시그널

시그널은 한 오브젝트에서 반응할 수 있는 다른 오브젝트로 메시지를 방출하는 도구입니다. 클래스에 대한 커스텀 시그널을 생성하려면 signal 키워드를 사용하세요.

extends Node


# A signal named health_depleted.
signal health_depleted

참고

시그널은 콜백 메커니즘입니다. 그리고 일반적인 프로그래밍 패턴인 옵저버(Observer)의 역할도 합니다. 자세한 정보는 Game Programming Patterns의 전자책, Observer tutorial을 참고하세요.

커스텀 시그널도 Button, RigidBody 같은 노드에 내장된 시그널을 연결하는 방법과 같은 방식으로 메서드에 연결할 수 있습니다.

아래 예제에서 Character 노드의 health_depleted 시그널을 Game 노드로 연결합니다. Character 노드가 시그널을 방출하면 Game 노드의 _on_Character_health_depleted가 호출됩니다:

# Game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.connect("health_depleted", self, "_on_Character_health_depleted")


func _on_Character_health_depleted():
    get_tree().reload_current_scene()

하나의 시그널에 원하는 만큼 많은 인수를 함께 방출할 수 있습니다.

위가 유용한 예입니다. 화면의 체력 바가 애니메이션이 적용되며 체력 변화에 반응하기를 원하지만 유저 인터페이스를 씬 트리의 플레이어와 분리하려고 한다고 가정해 보겠습니다.

Character.gd 스크립트에서 health_changed 시그널을 정의하고 Object.emit_signal()로 방출합니다. 그런 다음 씬 트리의 맨 위에 있는 Game 노드에서 Object.connect() 메서드를 사용해 시그널을 Lifebar에 연결합니다:

# Character.gd

...
signal health_changed


func take_damage(amount):
    var old_health = health
    health -= amount

    # We emit the health_changed signal every time the
    # character takes damage.
    emit_signal("health_changed", old_health, health)
...
# Lifebar.gd

# Here, we define a function to use as a callback when the
# character's health_changed signal is emitted.

...
func _on_Character_health_changed(old_value, new_value):
    if old_value > new_value:
        progress_bar.modulate = Color.red
    else:
        progress_bar.modulate = Color.green

    # Imagine that `animate` is a user-defined function that animates the
    # bar filling up or emptying itself.
    progress_bar.animate(old_value, new_value)
...

참고

시그널을 사용하려면 여러분의 클래스가 Object 클래스를 확장(extend)하거나 Object 클래스를 확장하는 Node, KinematicBody, Control등과 같은 클래스를 확장해야 합니다.

Game 노드에서는 CharacterLifebar 노드를 가져와서 시그널을 방출하는 캐릭터를 수신기인 Lifebar 노드에 연결합니다.

# Game.gd

func _ready():
    var character_node = get_node('Character')
    var lifebar_node = get_node('UserInterface/Lifebar')

    character_node.connect("health_changed", lifebar_node, "_on_Character_health_changed")

이를 통해 LifebarCharacter 노드에 연결하지 않고도 체력 변화에 반응할 수 있습니다.

시그널을 정의한 뒤에 개별적으로 괄호 안에 인수 이름을 적을 수 있습니다:

# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)

인수는 에디터의 노드(Node) 독에 표시되며 Godot는 이 인수로 콜백 함수를 생성할 수 있습니다. 시그널을 방출할 때 여전히 많은 수의 인수를 방출할 수 있으므로, 다시 말해 올바른 값을 방출하는 일은 여러분에게 달려 있습니다.

../../../_images/gdscript_basics_signals_node_tab_1.png

GDScript는 시그널과 메서드 간의 연결에 값 배열을 바인딩할 수 있습니다. 시그널이 방출되면 콜백 메서드는 바인딩된 값들을 받습니다. 이러한 바인딩된 인수는 각 연결에서 고유하며, 값들은 똑같이 유지됩니다.

방출된 시그널이 여러분이 필요한 데이터에 대한 접근을 제공하지 않는다면 이 배열의 값들로 연결에 여분의 상수 정보를 추가할 수 있습니다.

위의 예를 바탕으로 Player1이 데미지를 22 입었습니다.와 같이 각 캐릭터가 입은 데미지 로그를 화면에 표시하고 싶다고 가정해 보겠습니다. health_changed 시그널은 피해를 입은 캐릭터의 이름을 알려주지 않습니다. 따라서 게임 내 콘솔에 시그널을 연결할 때 바인딩 배열 인수에 캐릭터 이름을 추가할 수 있습니다:

# Game.gd

func _ready():
    var character_node = get_node('Character')
    var battle_log_node = get_node('UserInterface/BattleLog')

    character_node.connect("health_changed", battle_log_node, "_on_Character_health_changed", [character_node.name])

BattleLog 노드는 바인딩 배열의 각 요소를 추가 인수로 받습니다:

# BattleLog.gd

func _on_Character_health_changed(old_value, new_value, character_name):
    if not new_value <= old_value:
        return

    var damage = old_value - new_value
    label.text += character_name + " took " + str(damage) + " damage."

yield를 가진 코루틴(Coroutine)

GDScript는 내장 함수 yield를 통해 코루틴(Coroutines)을 지원합니다. yield()를 호출하면 즉시 현재 함수와 같은 함수의 현재 상태를 반환합니다. 그리고 반환된 오브젝트에서 resume을 호출하면 함수가 yield 이후부터 다시 실행되고 값을 반환합니다. 다시 시작하면 상태 오브젝트는 무효화됩니다. 여기 예제가 있습니다:

func my_func():
    print("Hello")
    yield()
    print("world")


func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print("my dear")
    y.resume()
    # 'y' resumed and is now an invalid state.

이렇게 출력됩니다:

Hello
my dear
world

yield()resume() 사이에 값을 전달할 수도 있습니다. 예를 들면:

func my_func():
    print("Hello")
    print(yield())
    return "cheers!"


func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print(y.resume("world"))
    # 'y' resumed and is now an invalid state.

이렇게 출력됩니다:

Hello
world
cheers!

여러 yield를 사용할 때 새 함수 상태를 저장해야 한다는 점을 기억하세요:

func co_func():
    for i in range(1, 5):
        print("Turn %d" % i)
        yield();


func _ready():
    var co = co_func();
    while co is GDScriptFunctionState && co.is_valid():
        co = co.resume();

코루틴 & 시그널

yield의 진정한 힘은 시그널과 결합할 때 나타납니다. yield는 오브젝트와 시그널이라는 두 개의 인수를 받을 수 있습니다. 시그널을 받으면 실행이 다시 시작됩니다. 여기 몇 가지 예제가 있습니다:

# Resume execution the next frame.
yield(get_tree(), "idle_frame")

# Resume execution when animation is done playing.
yield(get_node("AnimationPlayer"), "animation_finished")

# Wait 5 seconds, then resume execution.
yield(get_tree().create_timer(5.0), "timeout")

코루틴은 무효 상태로 전환할 때 complated 시그널을 사용합니다. 예를 들어:

func my_func():
    yield(button_func(), "completed")
    print("All buttons were pressed, hurray!")


func button_func():
    yield($Button0, "pressed")
    yield($Button1, "pressed")

my_func은 두 버튼을 모두 누르면 계속 실행됩니다.

객체가 시그널을 방출하면 시그널의 인수를 얻을 수도 있습니다:

# Wait for when any node is added to the scene tree.
var node = yield(get_tree(), "node_added")

인수가 두 개 이상인 경우 yield는 다음과 같이 인수를 포함하는 배열을 반환합니다:

signal done(input, processed)

func process_input(input):
    print("Processing initialized")
    yield(get_tree(), "idle_frame")
    print("Waiting")
    yield(get_tree(), "idle_frame")
    emit_signal("done", input, "Processed " + input)


func _ready():
    process_input("Test") # Prints: Processing initialized
    var data = yield(self, "done") # Prints: waiting
    print(data[1]) # Prints: Processed Test

함수가 yield할 수 있는지 또는 여러 번 yield할 수 있는지 여부가 확실하지 않은 경우 조건부로 completed 시그널에 yield할 수 있습니다:

func generate():
    var result = rand_range(-1.0, 1.0)

    if result < 0.0:
        yield(get_tree(), "idle_frame")

    return result


func make():
    var result = generate()

    if result is GDScriptFunctionState: # Still working.
        result = yield(result, "completed")

    return result

이렇게 하면 코루틴이 내부적으로 사용되었는지 여부에 관계없이 함수가 반환해야 하는 모든 것을 반환합니다. completed 시그널은 함수가 더 이상 yield하지 않을 때만 방출되기 때문에 while은 불필요합니다.

onready 키워드

노드를 사용할 때, 씬의 일부를 변수로 참조하는 것이 일반적입니다. 씬은 활성 씬 트리에 들어갈 때만 구성되기 때문에 하위 노드는 Node._ready() 호출이 있을 때만 가져올 수 있습니다.

var my_label


func _ready():
    my_label = get_node("MyLabel")

이 작업은 특히 노드와 외부 참조가 쌓이다 보면 번거로워질 수 있습니다. 이를 위해 GDScript에는 _ready()가 호출될 때까지 멤버 변수의 초기화를 지연시키는 onready 키워드가 있습니다. 위의 코드는 한 줄로 대체 가능합니다:

onready var my_label = get_node("MyLabel")

Assert 키워드

assert 키워드는 디버그 빌드에서 조건을 확인하는 데 사용할 수 있습니다. assert는 디버그 빌드가 아니면 무시됩니다. 즉, 인수로 전달된 표현식은 릴리스 모드로 내보낸 프로젝트에서 평가되지 않습니다. 이 때문에 assert에는 부작용이 있는 표현식이 포함되어서는 안 됩니다. 그렇지 않으면 스크립트는 프로젝트가 디버그 빌드인지 여부에 따라 매우 다르게 동작할 것입니다.

# Check that 'i' is 0. If 'i' is not 0, an assertion error will occur.
assert(i == 0)

에디터에서 프로젝트를 실행할 때 assert 오류가 발생하면 프로젝트가 일시 정지됩니다.