GDScript 기초

소개

GDScript 는 내용물을 생성하는데 사용되는, 하이 레벨인 동적 언어 프로그래밍 언어입니다. Python 과 비슷한 문법을 사용합니다(블록은 들여쓰기 기반이고 많은 키워드가 유사합니다). 목적은 Godot 엔진에 최적화되고 긴밀하게 통합하도록 조직하여 콘텐츠 창작과 통합에 뛰어난 유연성을 주도록 하는 것입니다.

역사

초기에, 엔진은 Lua 스크립트 언어를 사용했습니다. Lua는 빠르지만,(폴백(fallbacks)을 사용해서) 객체 지향 시스템에 대한 바인딩 만들기는 복잡하고 느리고 엄청난 양의 코드를 사용했습니다. 이후 Python 을 사용해 보았지만, 이 또한 끼워 맞추기는 어려웠습니다.

게임에 옮기기 위해 사용한 마지막 외부 스크립트 언어는 Squirrel 이었습니다, 하지만 역시 떨어졌습니다. 이 시점에서, 커스텀 스크립트 언어가 Godot의 특정 구조를 보다 최적으로 활용할 수 있다는 것이 분명해졌습니다:

  • Godot는 노드에 스크립트를 끼워 넣습니다. 대부분의 언어는 이를 염두에 두고 설계되지 않았습니다.
  • Godot는 2D와 3D 수학에 몇 가지 내장 데이터 타입을 사용합니다. 스크립트 언어는 이를 제공하지 않으며, 바인딩 하는 것은 비효율적입니다.
  • Godot는 네트 혹은 디스크의 데이터를 들어 올리고 초기화하기 위해 스레드를 많이 사용합니다. 일반적인 언어의 스크립트 인터프리터는 이것에 친절하지 않습니다.
  • Godot는 이미 리소스를 위한 메모리 관리 모델을 갖고 있지만, 대부분의 스크립트 언어는 자신들의 것을 제공하므로, 중복되는 노력과 버그가 발생합니다.
  • 코드를 바인딩 하는 것은 항상 엉망이고 여러 오류 지점, 예측할 수 없는 버그, 그리고 일반적으로 낮은 유지 능력을 초래합니다.

이 고려 사항의 결과물이 GDScript 입니다. GDScript의 언어와 인터프리터는 Lua와 Squirrel의 바인딩 코드보다 작아졌습니다, 반면 같은 기능성을 갖습니다. 시간이 흐르면서, 내장 언어는 큰 장점이 되었습니다.

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에 대한 개요입니다. 배열이나 다른 객체에 사용할 수 있는 메서드와 같은 세부 정보는, 연결된 클래스 설명을 보시기 바랍니다.

식별자(Identifiers)

알파벳 물자로 제한하는 문자열 (a 부터 z 그리고 A 부터 Z), 숫자 (digit) (0 부터 9) 그리고 _ 는 식별자로서 권한을 지닙니다. 또한, 식별자는 숫자로 시작할 수 없습니다. 식별자는 대소문자를 구별합니다 (fooFOO 와 다릅니다).

키워드(Keywords)

다음은 언어에서 지원하는 키워드 목록입니다. 키워드는 예약된 단어(토큰)이기 때문에, 식별자로 사용될 수 없습니다. 다음 섹션에 나열된(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 클래스를 정의합니다.
extends 현재 클래스로 확장할 클래스를 정의합니다.
is 변수가 주어진 클래스를 확장하는지, 혹은 변수가 주어진 내장 타입인지 여부를 테스트합니다.
as 가능한 경우 값은 주어진 타입으로 캐스트 합니다.
self 현재 클래스 인스턴스를 참조합니다.
tool 에디터에서 스크립트를 실행합니다.
signal 시그널을 정의합니다.
func 함수를 정의합니다.
static 정적 함수를 정의합니다. 정적 멤버 변수는 허용되지 않습니다.
const 상수를 정의합니다.
enum 열거 형을 정의합니다.
var 변수를 정의합니다.
onready 스크립트가 첨부된 노드와 그것의 자식이 씬 트리의 일부인 경우 변수를 초기화합니다.
export 첨부된 리소스를 변수와 함께 저장하고 에디터에서 변수를 표시하고 수정할 수 있게 합니다.
setget 변수에 대한 setter와 getter 함수를 정의합니다.
breakpoint 디버거 중단점을 위한 에디터 도우미.
preload 클래스나 변수를 미리 불러오기(Preload)합니다. 리소스 클래스를 참고하세요.
yield 코루틴을 지원합니다. yield를 가진 코루틴(Coroutine)을 참고하세요.
assert 조건을 가정하고 실패 시 오류를 기록합니다. 디버그가 아닌 빌드에는 무시됩니다. Assert 키워드를 참고하세요.
remote 네트워킹 RPC(원격 절차 호출) 주석. high-level multiplayer docs를 참고하세요.
master 네트워킹 RPC(원격 절차 호출) 주석. high-level multiplayer docs를 참고하세요.
puppet 네트워킹 RPC(원격 절차 호출) 주석. high-level multiplayer docs를 참고하세요.
remotesync 네트워킹 RPC(원격 절차 호출) 주석. high-level multiplayer docs를 참고하세요.
mastersync 네트워킹 RPC(원격 절차 호출) 주석. high-level multiplayer docs를 참고하세요.
puppetsync 네트워킹 RPC(원격 절차 호출) 주석. high-level multiplayer docs를 참고하세요.
PI PI 상수.
TAU TAU 상수.
INF 무한대 상수. 비교에 사용됩니다.
NAN NAN (숫자 아님) 상수. 비교에 사용됩니다.

연산자(Operators)

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

연산자 설명
x[index] 구독(Subscription), 최우선 순위
x.attribute 속성 참조
is 인스턴스 타입 검사기
~ 비트 단위 NOT
-x 음수 / 단항 부정
* / %

곱하기 / 나누기 / 나머지

이 연산자들은 C++의 연산자들처럼 행동합니다. 정수로 분할 시에 소수점 부분은 반환되지 않고 잘려나가며, % 연산자는 정수형(float의 경우는 "fmod") 끼리의 연산에서만 사용할 수 있습니다.

+ 배열의 추가 / 연결
- 빼기
<< >> 비트 자리 옮김
& 비트 단위 AND
^ 비트 단위 XOR
| 비트 단위 OR
< > == != >= <= 비교
in 콘텐츠 테스트
! not 불 방식 NOT
and && 불 방식 AND
or || 불 방식 OR
if x else 3진 if/else
= += -= *= /= %= &= |= 할당, 최하위 우선 순위

상수(Literal)

상수 타입
45 기본 10 정수
0x8F51 기본 16 (hex) 정수
3.14, 58.1e-10 부동 소수점 숫자 (실수)
"안녕하세요", "안녕" 문자열
"""안녕하세요""" 여러 줄 문자열
@"Node/Label" 노드 경로(NodePath) 혹은 문자열 이름(StringName)
$NodePath get_node("NodePath") 의 속기

주석(Comment)

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

# This is a comment.

여러 줄 주석은 """ (3 행 따옴표)를 시작과 끝에 사용해서 만들 수 있습니다. 이렇게 하면 문자열이 만들어지므로 스크립트를 컴파일할 때 문자열이 제거되지 않습니다.

""" Everything on these
lines is considered
a comment. """

내장 타입(Built-in type)

내장 타입은 스택 할당됩니다. 그들은 값으로 전달됩니다. 즉, 함수로 그들을 각 할당이나 인수로 전달할 때 복사본이 만들어집니다. 유일한 예외는 Array 형식과 Dictionaries 입니다, 그들은 참조로 전달되어 공유됩니다.(PoolArray 형식은 PoolByteArray 와 다르지만, 이것도 값으로 전달되므로, 사용할 것을 결정할 때 이를 고려하십시오!)

기본 내장 타입

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

null

null 은 빈 데이터 타입으로 정보를 포함하고 있지 않고 다른 값을 할당할 수 없습니다.

bool

불 데이터 타입으로 truefalse 만을 가지고 있습니다.

int

정수 데이터 타입은 오직 정수만 가질 수 있습니다, (음수와 양수 둘 다).

float

부동 소수점 값 (실수)을 갖기 위해 사용됩니다.

String

유니 코드 형식 에 있는 문자열. 문자열은 표준 C 이스케이프 시퀀스 를 가질 수 있습니다. GDScript는 형식 문자열 일명 printf 기능 을 지원합니다.

벡터 내장 타입

Vector2

2D 벡터 타입으로 xy 필드를 가지고 있습니다. 배열로 접근될 수도 있습니다.

Rect2

2D 사각형 타입으로 두 개의 벡터 필드를 가지고 있습니다: positionsize 입니다. 또는 position+size 를 뜻하는 end 필드를 가집니다.

Vector3

3D 벡터 타입으로 x, y 그리고 z 필드를 가지고 있습니다. 배열로 접근될 수도 있습니다.

Transform2D

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

Plane

3D 평면 타입의 표준화된 형태로 normal 벡터 필드와 d 스칼라 거리를 포함합니다.

Quat

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

AABB

축이 정렬된 경계 상자로 (혹은 3D 상자) 2개의 벡터 필드를 갖습니다: positionsize 입니다. 또는 position+size 를 뜻하는 end 필드를 갖습니다.

Basis

3x3 행렬로 3D 회전과 규모에 사용됩니다. 3 개의 벡터 필드 (x, y 그리고 z)를 가지며 3D 벡터의 배열로 접근될 수도 있습니다.

Transform

3D 변형(Transform)으로 기반 필드인 basis 와 Vector3 필드인 origin 을 갖습니다.

엔진 내장 타입

Color

색상 데이터 타입으로 r, g, b, 그리고 a 필드를 갖습니다. 색조/채도/색가를 위한 h, s, 그리고 v 로 접근될 수도 있습니다.

NodePath

주로 씬 시스템에 사용되는 노드로 컴파일 된 경로. 쉽게 문자열에 할당하거나 문자열이 될 수 있습니다.

RID

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

Object

내장 타입이 아닌 것을 위한 기본 클래스.

컨테이너 내장 타입

Array

임의 객체 타입의 일반적인 열로, 다른 배열이다 딕셔너리를 포함하고 있습니다 (아래를 참고하세요). 배열의 크기는 동적으로 변경할 수 있습니다. 배열은 인덱스 0 부터 인덱스가 붙기 시작합니다. Godot 2.1부터는, 끝에서 세도록 하기 위해, Python처럼 인덱스가 음수일 수도 있습니다.

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.

데이터

변수(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 등)
  • 엔진 클래스 (노드, 리소스, 참조 등)
  • 스크립트 리소스가 포함된 상수 이름 (const MyScript = preload("res://my_script.gd")를 선언했다면 MyScript가 이에 속합니다).
  • 같은 스크립트에서 유효 범위를 준수하는 다른 클래스 (같은 유효 범위에서 class InnerClass 안에 class NestedClass를 선언했다면 InnerClass.NestedClass가 이에 속합니다)
  • class_name 키워드로 선언된 스크립트 클래스.

캐스팅(Casting)

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

값이 캐스트(cast)된 타입과 같은, 타입이나 하위 타입이라면 객체 타입 간의 캐스팅은 동일한 객체에서 수행됩니다.

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 # 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")

상수(Constant)

상수는 변수와 비슷하지만, 상수 또는 상수 식이어야만 하며 초기화 시 값이 지정되어야 합니다.

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

상수의 타입은 지정된 값에서 유추되지만, 명시적 타입 지정을 추가하는 것도 가능합니다:

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

호환하지 않은 타입의 값을 지정하면 오류가 발생합니다.

이넘(Enum)

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

이넘에 이름을 전달하면, 그 이름의 상수 딕셔너리 내에 모든 키를 넣을 것입니다.

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)

함수는 항상 class 에 속해 있습니다. 변수를 조회하는 범위 우선 순위는: 지역 → 클래스 멤버 → 전역 순입니다. 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(빈)로 타입을 설정하는 것은 함수가 어느 것도 반환하지 않음을 의미합니다. 빈 함수는 return 키워드로 일찍 반환할 수 있지만 어떤 값도 반환할 수 없습니다.

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

주석

무효가 아닌 함수는 반드시 항상 값을 반환해야 하므로, 코드가 (if/else 구조와 같은) 명령문을 뻗고 있다면, 모든 가능한 경로가 반환을 가져야 합니다. 예를 들어, if 블록 안에 return을 갖지만 그 뒤가 없는 경우, 블록이 실행되지 않으면 반환할 유효한 값을 함수가 가질 수 없기 때문에, 에디터는 오류를 발생합니다.

함수 참조하기

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)

_init과 같은 기본 함수와, _enter_tree, _exit_tree, _process, _physics_process 등과 같은 대부분의 알림은 모든 기본 클래스에서 자동으로 호출된다는 것을 기억하세요. 그래서 어떤 방법이든 함수를 오버로딩 할 때 명시적으로 함수를 호출할 필요가 있습니다.

정적 함수

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

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

명령문(statement)과 제어 흐름(control flow)

명령문은 표준이고 할당, 함수 호출, 제어 흐름 구조 등이 될 수 있습니다 (하단을 참고하세요). 명령문 분리 기호로 쓰인 ; 는 전적으로 선택 사항입니다.

if/else/elif

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

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

때때로 불 식을 기반으로 한 다른 초기 값을 지정하고 싶을 수도 있습니다. 이 경우 3진 if 식이 유용합니다:

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

while

간단한 루프는 while 문법을 사용하여 만들 수 있습니다. 루프는 break 를 사용하여 끊거나 continue 를 사용하여 진행할 수 있습니다:

while [expression]:
    statement(s)

for

배열이나 테이블 같은, 범위를 반복하려면, for 루프가 사용됩니다. 한 배열을 반복할 때, 현재 배열 요소는 루프 변수에 저장됩니다. 딕셔너리를 반복할 때, 인덱스 는 루프 변수에 저장됩니다.

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])

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.

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 를 하나의 밑줄로 변경하십시오.

제어 흐름:

패턴은 위에서 아래로 맞춰집니다. 패턴이 일치하면, 해당 블록이 실행됩니다. 그런 후, 실행은 match 명령문 아래에서 계속됩니다. fallthrough를 원한다면 continue를 사용하여 해당 블록의 실행을 멈추고 그 아래에 있는 것을 확인하게 할 수 있습니다.

6가지 패턴 타입이 있습니다:

  • 상수 패턴

    숫자와 문자열과 같은 상수

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

    변수/이넘의 상수를 대조합니다

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • 임의 문자 기호 패턴

    이 패턴은 모든 것을 대조합니다. 하나의 밑줄로 쓰여집니다.

    다른 언어의 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 tbh.")
    
  • 바인딩 패턴

    바인딩 패턴은 새 변수를 도입합니다. 임의 문자 기호 패턴과 마찬가지로, 모든 것을 대조합니다 - 그리고 값에 이름을 부여합니다. 특히 배열 패턴과 딕셔너리 패턴에 유용합니다.

    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)
    
  • 배열 패턴

    배열을 맞춰봅니다. 배열 패턴의 모든 단일 요소는 패턴 그 자체이므로 그들을 중첩할 수 있습니다.

    배열의 길이가 먼저 테스트 되고, 그것이 패턴과 같은 크기이어야 합니다, 그렇지 않으면 패턴이 일치하지 않습니다.

    개방형 배열: 마지막 하위 패턴을 .. 로 만들어서 배열이 패턴보다 더 커지도록 만들 수 있습니다

    모든 하위 패턴은 쉼표로 분리되어야 합니다.

    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")
    
  • 딕셔너리 패턴

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

    딕셔너리의 크기가 먼저 테스트 되고, 그것이 패턴과 같은 크기이어야 합니다, 그렇지 않으면 패턴이 일치하지 않습니다.

    개방형 딕셔너리: 마지막 하위 패턴을 .. 로 만들어서 딕셔너리가 패턴보다 더 커지도록 만들 수 있습니다

    모든 하위 패턴은 쉼표로 분리되어야 합니다.

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

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

    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")
    
다중 패턴:

쉼표로 분리된 다중 패턴을 지정할 수도 있습니다. 이 패턴은 바인딩을 가질 수 없습니다.

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

클래스(Class)

기본적으로, 모든 스크립트 파일은 이름 없는 클래스 입니다. 이 경우, 우리는 파일 경로를 사용하거나 상대 경로 혹은 절대 경로를 사용하여 그들을 참조할 수 있습니다. 예를 들어 스크립트의 이름이 character.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()

대신, 클래스에 이름을 지정하여 Godot 에디터에 새 타입으로 등록할 수 있습니다. 이를 위해, 'class_name' 키워드를 사용합니다. 선택적 쉼표와 이미지 경로를 추가하여 아이콘으로 사용할 수 있습니다. 클래스는 이제 에디터에서 새로운 아이콘으로 나타납니다:

# Item.gd

extends Node

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

클래스 파일 예제입니다:

# 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)

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

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

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

상속은 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 된 것)에서 함수를 호출하려면, . 를 함수 이름 앞에 붙이세요:

.basefunc(args)

이는 확장 클래스의 함수가 함수를 기본 클래스의 동일한 이름으로 대체하기 때문에 특히 유용합니다. 따라서 여전히 그들을 호출하고 싶다면, 다른 언어의 super 키워드처럼 . 을 사용할 수 있습니다:

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

클래스 생성자

클래스 인스턴스에 호출되는, 클래스 생성자의 이름은 _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)는 State.gd 에서 적절한 매개 변수를 _init 에게 전달하고 _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

내부 클래스

클래스는 내부 클래스를 포함할 수 있습니다. 내부 클래스는 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()

리소스 클래스

파일로 저장된 클래스는 resources 로 취급됩니다. 다른 클래스에서 접근하려면 반드시 불러와 있어야 합니다. 이것에는 loadpreload 함수를 사용합니다 (하단을 참고하세요). 불러온 클래스를 인스턴스 하려면 클래스 객체에 new 함수를 호출합니다:

# Load the class resource when calling load().
var my_class = 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)

클래스 멤버는 내보내질 수 있습니다. 즉, 값이 첨부된 리소스와 함께 저장됩니다 (예 the scene). 그들은 또한 속성 에디터에서 편집하는 것이 가능합니다. 내보내는 것은 export 키워드를 사용합니다:

extends Button

export var number = 5 # Value will be saved and visible in the property editor.

내보낸 변수는 상수 표현식으로 초기화 되거나 내보내기 힌트를 export 키워드에 인수의 형태로 가져야 합니다 (하단을 참고하세요).

멤버 변수 내보내기의 기본적인 이점은 에디터에서 보이고 편집할 수 있는 것입니다. 이 방법으로 아티스트와 게임 디자이너가 프로그램이 실행 방법에 영향을 주는 값을 수정할 수 있습니다. 이를 위해, 특수 내보내기 문법이 제공됩니다.

# If the exported value assigns a constant or constant expression,
# the type will be inferred and used in the editor.

export var number = 5

# Export can take a basic data type as an argument, which will be
# used in the editor.

export(int) var number

# Export can also take a resource type to use as a hint.

export(Texture) var character_face
export(PackedScene) var scene_file

# Integers and strings hint enumerated values.

# Editor will enumerate as 0, 1 and 2.
export(int, "Warrior", "Magician", "Thief") var character_class
# Editor will enumerate with string names.
export(String, "Rebecca", "Mary", "Leah") var character_name

# Named Enum Values

# Editor will enumerate as THING_1, THING_2, ANOTHER_THING.
enum NamedEnum {THING_1, THING_2, ANOTHER_THING = -1}
export (NamedEnum) var x

# Strings as Paths

# String is a path to a file.
export(String, FILE) var f
# String is a path to a directory.
export(String, DIR) var f
# String is a path to a file, custom filter provided as hint.
export(String, FILE, "*.txt") var f

# Using paths in the global filesystem is also possible,
# but only in tool scripts (see further below).

# String is a path to a PNG file in the global filesystem.
export(String, FILE, GLOBAL, "*.png") var tool_image
# String is a path to a directory in the global filesystem.
export(String, DIR, GLOBAL) var tool_dir

# The MULTILINE setting tells the editor to show a large input
# field for editing over multiple lines.
export(String, MULTILINE) var text

# Limiting editor input ranges

# Allow integer values from 0 to 20.
export(int, 20) var i
# Allow integer values from -10 to 20.
export(int, -10, 20) var j
# Allow floats from -10 to 20, with a step of 0.2.
export(float, -10, 20, 0.2) var k
# Allow values y = exp(x) where y varies between 100 and 1000
# while snapping to steps of 20. The editor will present a
# slider for easily editing the value.
export(float, EXP, 100, 1000, 20) var l

# Floats with Easing Hint

# Display a visual representation of the ease() function
# when editing.
export(float, EASE) var transition_speed

# Colors

# Color given as Red-Green-Blue value
export(Color, RGB) var col # Color is RGB.
# Color given as Red-Green-Blue-Alpha value
export(Color, RGBA) var col # Color is RGBA.

# Another node in the scene can be exported, too.

export(NodePath) var node

심지어 스크립트가 에디터에서 실행되지 않더라도, 내보낸 속성은 여전히 편집 가능합니다 (아래 "도구"를 참고하세요).

비트 플래그(bit flags) 내보내기

비트 플래그로 사용되는 정수는 한 속성에 많은 /거짓 (불) 값을 저장할 수 있습니다. 내보내기 힌트 int, FLAGS 를 사용하여, 이를 에디터에서 설정할 수 있습니다:

# Individually edit the bits of an integer.
export(int, FLAGS) var spell_elements = ELEMENT_WIND | ELEMENT_WATER

플래그를 특정 수의 명명된 플래그로 제한하는 것도 가능합니다. 문법은 열거 문법과 비슷합니다:

# Set any of the given flags from the editor.
export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0

이 예제에서, Fire 는 값 1, Water 는 값 2, Earth 는 값 4, 그리고 Wind 는 값 8에 해당합니다. 일반적으로, 상수는 이에 따라 정의되어야 합니다 (예 const ELEMENT_WIND = 8 등등).

비트 플래그를 사용하려면 비트 연산에 대한 이해가 필요합니다. 의심스럽다면, 불 변수를 대신 내보내어야 합니다.

배열 내보내기

배열을 내보낼 수 있지만 중요한 주의 사항이 있습니다: 정규 배열은 모든 클래스 인스턴스에 로컬로 만들어 지지만, 내보낸 배열은 모든 인스턴스 간에 공유됩니다. 즉, 한 인스턴스에서 배열을 편집하면 다른 인스턴스에서도 영향을 줍니다. 내보낸 배열은 이니셜라이저를 가질 수 있지만, 반드시 상수 표현식이 되어야 합니다.

# Exported array, shared between all instances.
# Default value must be a constant expression.

export var a = [1, 2, 3]

# Exported arrays can specify type (using the same hints as before).

export(Array, int) var ints = [1,2,3]
export(Array, int, "Red", "Green", "Blue") var enums = [2, 1, 0]
export(Array, Array, float) var two_dimensional = [[1, 2], [3, 4]]

# You can omit the default value, but then it would be null if not assigned.

export(Array) var b
export(Array, PackedScene) var scenes

# Typed arrays also work, only initialized empty:

export var vector3s = PoolVector3Array()
export var strings = PoolStringArray()

# Regular array, created local for every instance.
# Default value can include run-time values, but can't
# be exported.

var c = [a, 2, 3]

Setters/getters

클래스 멤버 변수가 어떤 이유로든 언제 변경되는지를 아는 것은 종종 유용합니다. 어떤 방식으로 그것의 접근을 캡슐화 하는 것이 필요할 수도 있습니다.

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

var variable = value setget setterfunc, getterfunc

외부 소스에 의해 (즉, 클래스에서의 로컬 사용이 아닌) variable의 값이 수정될 때는, 언제나 setter 함수 (위의 setterfunc)는 호출됩니다. 이것은 값이 변경되기 전에 발생합니다. setter는 새로운 값으로 무엇을 해야 할 지 결정해야 합니다. 반대로, variable이 액세스될 때, getter 함수 (위의 getterfunc)는 원하는 값을 return해야 합니다. 아래에 있는 것은 예제입니다:

var myvar 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.

setter 혹은 getter 함수 둘 중 하나는 생략될 수 있습니다:

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

Get/Setter는 입력이 유효한지 알아보기 위해, 툴 스크립트나 플러그인에서 에디터로 변수를 내보낼 때 특히 유용합니다.

말한 대로 로컬 액세스는 setter와 getter를 작동시키지 않을 것입니다. 여기에 그 예가 나와 있습니다:

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 키워드가 존재하며 파일의 맨 위에 배치해야 합니다:

tool
extends Button

func _ready():
    print("Hello")

메모리 관리

클래스가 Reference 에서 상속할 때, 더 이상 사용하지 않게 되면 인스턴스는 해제됩니다. 가비지 콜렉터 없이, 참조만 계산됩니다. 기본적으로, 상속을 정의하지 않는 모든 클래스는 참조 를 확장합니다. 바람직하지 않다면, 클래스는 Object 를 수동으로 상속해야만 하고 instance.free()를 호출해야만 합니다. 해제할 수 없는 참조 사이클을 피하기 위해, 약한 참조를 생성하는 weakref 함수가 제공됩니다.

또는, 참조를 사용하지 않을 때, is_instance_valid(instance) 가 객체가 해제되었는 지를 확인하는데 사용될 수 있습니다.

시그널

시그널은 한 객체에서 다른 객체로 들을 수 있는 신호 메시지를 전달하는 일반적인 방식입니다. signal 키워드로 클래스를 위한 커스텀 시그널을 만드세요.

# Signal with no arguments
signal your_signal_name

# Signal with two arguments
signal your_signal_name_with_args(a, b)

내장된 노드, Button이나 RigidBody의 시그널과 같은 방식으로 이 시그널도 연결할 수 있습니다.

다음은 Object.connect() 메서드를 이용해 한 스크립트에 커스텀 시그널을 만들고 이를 다른 스크립트에 연결하는 예시입니다:

# your_notifier.gd

signal data_found

var your_data = 42
# your_handler.gd

func your_handler():
   print("Your handler was called!")
# your_game.gd

func _ready():
   var notifier = your_notifier.new()
   var handler = your_handler.new()

   notifier.connect("data_found", handler, "your_handler")

GDScript는 인수를 신호와 메서드 사이의 연결에 바인딩 할 수 있습니다. 시그널이 보내질 때, 연결된 메소드를 호출하면 바인딩 된 인자가 메소드에 주어집니다. 이러한 바운드 인수는 신호 또는 메서드가 아닌 연결에만 적용되므로 각 연결마다 고유 한 바인딩이 있음을 의미합니다.

다음은 버튼을 연결에 묶어서, 버튼의 pressed 시그널과 메서드를 연결하는 예제입니다. handler는 눌린 버튼 인스턴스와 묶인 인수를 프린트 합니다.

func pressed_handler(which):
   print("A button was pressed! Button's name was:", which.get_name())

func _ready():
   for button in get_node("buttons").get_children()
      # Connect the button's 'pressed' signal to our 'pressed_handler' method
      # Bind the button to the connection so we know which button was pressed
      button.connect("pressed", self, "pressed_handler", [button])

시그널은 시그널과 인수를 뿌리는 Object.emit_signal() 메서드로 생성됩니다.

다음은 GDScript 시그널의 모든 기능을 사용하기 위해 이전 예제에서 확장된 것입니다:

# your_notifier.gd

signal data_found(data)

var your_data = 42

func _process(delta):
   if delta == your_data:
      emit_signal("data_found", data)
# your_handler.gd

func your_handler(data, obj):
   print("Your handler was called from: ", obj.get_name(), with data: ", data)
# your_game.gd

func _ready():
   var notifier = your_notifier.new()
   var handler = your_handler.new()

   notifier.connect("data_found", handler, "your_handler", [notifier])

yield를 가진 코루틴(Coroutine)

GDScript yield 내장 함수를 통해 코루틴(coroutines)을 지원합니다. yield()를 호출하면 반환 값과 같은 함수가 고정 상태가 되면서, 현재 함수에서 즉시 반환됩니다. 이 결과 resume을 호출하면 실행이 계속되고 함수가 반환하는 값을 반환합니다. 다시 시작하면 상태 객체는 무효화됩니다. 여기 예제가 있습니다:

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를 사용하는 진정한 힘은 시그널과 결합될 때 나타납니다. yield는 두 개의 매개 변수를 받아 들일 수 있는데, 객체와 시그널 입니다. 시그널을 받게되면, 실행이 다시 시작됩니다. 여기 몇 가지 예제가 있습니다:

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

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

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

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

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

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

my_func은 오직 두 버튼이 한번에 눌릴 때 계속 실행됩니다.

Onready 키워드

노드를 사용할 때, 변수에서 씬의 일부에 대한 참조를 유지하려는 것이 일반적입니다. 활성 씬 트리에 들어갈 때 씬만 구성되므로, 하위 노드는 오직 Node._ready()에 대한 호출이 있을 때만 얻을 수 있습니다.

var my_label

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

특히 노드와 외부 참조가 쌓이면서 성가시게 됩니다. 이를 위해, GDScript는 onready 라는, _ready가 호출될 때까지 멤버 변수의 초기화를 지연하는 키워드를 갖고 있습니다. 이걸로 위의 코드를 한 줄로 바꿀 수 있습니다:

onready var my_label = get_node("MyLabel")

Assert 키워드

assert 키워드는 디버그 빌드에서 정황을 확인하는 데 사용할 수 있습니다. 디버그가 아닌 빌드에서는 무시됩니다.

# Check that 'i' is 0.
assert(i == 0)