GDScript 기초

소개

GDScript is a high-level, dynamically typed programming language used to create content. It uses a syntax similar to Python (blocks are indent-based and many keywords are similar). Its goal is to be optimized for and tightly integrated with Godot Engine, allowing great flexibility for content creation and integration.

역사

In the early days, the engine used the Lua scripting language. Lua is fast, but creating bindings to an object oriented system (by using fallbacks) was complex and slow and took an enormous amount of code. After some experiments with Python, it also proved difficult to embed.

게임에 옮기기 위해 사용한 마지막 외부 스크립트 언어는 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 (highest priority)
x.attribute Attribute reference
is Instance type checker
~ 비트 단위 NOT
-x Negative / Unary negation
* / %

곱하기 / 나누기 / 나머지

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

+ Addition / Concatenation of arrays
- 빼기
<< >> Bit shifting
& 비트 단위 AND
^ 비트 단위 XOR
| 비트 단위 OR
< > == != >= <= 비교
in Content test
! not 불 방식 NOT
and && 불 방식 AND
or || 불 방식 OR
if x else 삼항 if/else
= += -= *= /= %= &= |= Assignment (lowest priority)

리터럴(Literal)

리터럴 타입
45 기본 10진법 정수
0x8F51 Base 16 (hexadecimal) integer
0b101010 Base 2 (binary) integer
3.14, 58.1e-10 Floating-point number (real)
"안녕하세요", "안녕" 문자열
"""안녕하세요""" 여러 줄 문자열
@"Node/Label" class_NodePath or StringName
$NodePath get_node("NodePath")의 속기

주석(Comment)

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

# This is a comment.

내장 타입(Built-in type)

Built-in types are stack-allocated. They are passed as values. This means a copy is created on each assignment or when passing them as arguments to functions. The only exceptions are Arrays and Dictionaries, which are passed by reference so they are shared. (Pooled arrays such as PoolByteArray are still passed as values.)

기본 내장 타입

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

null

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

bool

Short for "boolean", it can only contain true or false.

int

Short for "integer", it stores whole numbers (positive and negative). It is stored as a 64-bit value, equivalent to "int64_t" in C++.

float

Stores real numbers, including decimals, using floating-point values. It is stored as a 64-bit value, equivalent to "double" in C++. Note: Currently, data structures such as Vector2, Vector3, and PoolRealArray store 32-bit single-precision "float" values.

String

A sequence of characters in Unicode format. Strings can contain standard C escape sequences. GDScript also supports GDScript 형식 문자열(format strings).

벡터 내장 타입

Vector2

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

Rect2

2D Rectangle type containing two vectors fields: position and size. Also contains an end field which is position + size.

Vector3

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

Transform2D

3×2 matrix used for 2D transforms.

Plane

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

Quat

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

AABB

Axis-aligned bounding box (or 3D box) contains 2 vectors fields: position and size. Also contains an end field which is position + size.

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

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

컨테이너(Container) 내장 타입

Array(배열)

Generic sequence of arbitrary object types, including other arrays or dictionaries (see below). The array can resize dynamically. Arrays are indexed starting from index 0. Negative indices count from the end.

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 arrays are allocated linearly in memory for speed. Large arrays (more than tens of thousands of elements) may however cause memory fragmentation. If this is a concern, special types of arrays are available. These only accept a single data type. They avoid memory fragmentation and use less memory, but are atomic and tend to run slower than generic arrays. They are therefore only recommended to use for large data sets:

  • PoolByteArray: 바이트의 배열 (0부터 255까지의 정수).
  • PoolIntArray: 정수의 배열.
  • PoolRealArray: 실수의 배열.
  • PoolStringArray: 문자열의 배열.
  • PoolVector2Array: Vector2 객체의 배열.
  • PoolVector3Array: Vector3 객체의 배열.
  • PoolColorArray: Color 객체의 배열.

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'

타입 함축은 오직 지정된 값이 정의된 타입을 갖고 있어야만 가능합니다, 그렇지 않으면 오류가 발생합니다.

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

  • Built-in types (Array, Vector2, int, String, etc.).
  • Engine classes (Node, Resource, Reference, etc.).
  • 스크립트 리소스가 포함된 상수 이름 (const MyScript = preload("res://my_script.gd")를 선언했다면 MyScript가 이에 속합니다).
  • Other classes in the same script, respecting scope (InnerClass.NestedClass if you declared class NestedClass inside the class InnerClass in the same scope).
  • 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 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

Casting is also useful to have better type-safe variables when interacting with the scene tree:

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

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

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

중요

In Godot 3.1 and later, keys in a named enum are not registered as global constants. They should be accessed prefixed by the enum's name (Name.KEY); see an example below.

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.

함수

함수는 항상 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을 갖지만 그 뒤가 없는 경우, 블록이 실행되지 않는 경우, 반환할 올바른 값을 함수가 가질 수 없기 때문에 편집기는 오류를 발생합니다.

Referencing functions

Contrary to Python, functions are not first-class objects in GDScript. This means they cannot be stored in variables, passed as an argument to another function or be returned from other functions. This is for performance reasons.

To reference a function by name at run-time, (e.g. to store it in a variable, or pass it to another function as an argument) one must use the call or funcref helpers:

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

정적 함수

A function can be declared static. When a function is static, it has no access to the instance member variables or self. This is mainly useful to make libraries of helper functions:

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

Sometimes, you might want to assign a different initial value based on a boolean expression. In this case, ternary-if expressions come in handy:

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]) # 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 명령문과 유사하지만 몇 가지 추가 기능을 제공합니다.

Basic syntax:

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

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

  1. Replace switch with match.
  2. Remove case.
  3. 모든 break를 제거하세요. 기본적으로 break가 되지 않도록 하고 싶다면 fallthrough처럼 continue를 사용하실 수 있습니다.
  4. default를 하나의 밑줄로 변경하세요.

제어 흐름:

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

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

  • Constant pattern

    Constant primitives, like numbers and strings:

    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

    Matches the contents of a variable/enum:

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Wildcard pattern

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

    It can be used as the equivalent of the default in a switch statement in other languages:

    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

    A binding pattern introduces a new variable. Like the wildcard pattern, it matches everything - and also gives that value a name. It's especially useful in array and dictionary patterns:

    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

    Matches an array. Every single element of the array pattern is a pattern itself, so you can nest them.

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

    Open-ended array: An array can be bigger than the pattern by making the last subpattern ...

    Every subpattern has to be comma-separated.

    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: A dictionary can be bigger than the pattern by making the last subpattern ...

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

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

    A value pattern is separated from the key pattern with a :.

    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

    You can also specify multiple patterns separated by a comma. These patterns aren't allowed to have any bindings in them.

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

클래스(Class)

By default, all script files are unnamed classes. In this case, you can only reference them using the file's path, using either a relative or an absolute path. For example, if you name a script file 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()

Instead, 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 add an optional 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

클래스 파일 예제입니다:

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

A class (stored as a file) can inherit from:

  • A global class.
  • Another class file.
  • 다른 클래스 파일 안의 내부 클래스.

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

Inheritance uses the extends keyword:

# 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

To check if a given instance inherits from a given class, the is keyword can be used:

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

# [...]

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

To call a function in a parent class (i.e. one extend-ed in your current class), prepend . to the function name:

.base_func(args)

This is especially useful because functions in extending classes replace functions with the same name in their parent classes. If you still want to call them, you can prefix them with . (like the super keyword in other languages):

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

주석

Default functions like _init, and most notifications such as _enter_tree, _exit_tree, _process, _physics_process, etc. are called in all parent classes automatically. There is no need to call them explicitly when overloading them.

클래스 생성자

The class constructor, called on class instantiation, is named _init. As mentioned earlier, the constructors of parent classes are called automatically when inheriting a class. So, there is usually no need to call ._init() explicitly.

Unlike the call of a regular function, like in the above example with .some_func, if the constructor from the inherited class takes arguments, they are passed like this:

func _init(args).(parent_args):
   pass

This is better explained through examples. Consider this scenario:

# 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. If the inherited class (State.gd) defines a _init constructor that takes arguments (e in this case), then the inheriting class (Idle.gd) must define _init as well and pass appropriate parameters to _init from State.gd.

  2. Idle.gd can have a different number of arguments than the parent class State.gd.

  3. In the example above, e passed to the State.gd constructor is the same e passed in to Idle.gd.

  4. If Idle.gd's _init constructor takes 0 arguments, it still needs to pass some value to the State.gd parent class, even if it does nothing. This brings us to the fact that you can pass literals in the base constructor as well, not just variables. eg.:

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

주석

Documentation about exports has been moved to GDScript exports.

Setters/getters

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

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

var variable = value setget setterfunc, getterfunc

Whenever the value of variable is modified by an external source (i.e. not from local usage in the class), the setter function (setterfunc above) will be called. This happens before the value is changed. The setter must decide what to do with the new value. Vice versa, when variable is accessed, the getter function (getterfunc above) must return the desired value. Below is an example:

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.

Either of the setter or getter functions can be omitted:

# 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

Setters and getters are useful when exporting variables to the editor in tool scripts or plugins, for validating input.

말한 대로 지역 접근는 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)

툴 모드

By default, scripts don't run inside the editor and only the exported properties can be changed. In some cases, it is desired that they do run inside the editor (as long as they don't execute game code or manually avoid doing so). For this, the tool keyword exists and must be placed at the top of the file:

tool
extends Button

func _ready():
    print("Hello")

경고

Be cautious when freeing nodes with queue_free() or free() in a tool script (especially the script's owner itself). As tool scripts run their code in the editor, misusing them may lead to crashing the editor.

메모리 관리

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

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

시그널(Signal)

Signals are a tool to emit messages from an object that other objects can react to. To create custom signals for a class, use the signal keyword.

extends Node

# A signal named health_depleted
signal health_depleted

주석

Signals are a Callback mechanism. They also fill the role of Observers, a common programming pattern. For more information, read the Observer tutorial in the Game Programming Patterns ebook.

You can connect these signals to methods the same way you connect built-in signals of nodes like class_Button or class_RigidBody.

In the example below, we connect the health_depleted signal from a Character node to a Game node. When the Character node emits the signal, the game node's _on_Character_health_depleted is called:

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

You can emit as many arguments as you want along with a signal.

Here is an example where this is useful. Let's say we want a life bar on screen to react to health changes with an animation, but we want to keep the user interface separate from the player in our scene tree.

In our Character.gd script, we define a health_changed signal and emit it with Object.emit_signal(), and from a Game node higher up our scene tree, we connect it to the Lifebar using the Object.connect() method:

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

주석

To use signals, your class has to extend the Object class or any type extending it like Node, KinematicBody, Control...

In the Game node, we get both the Character and Lifebar nodes, then connect the character, that emits the signal, to the receiver, the Lifebar node in this case.

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

This allows the Lifebar to react to health changes without coupling it to the Character node.

You can write optional argument names in parentheses after the signal's definition:

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

These arguments show up in the editor's node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of arguments when you emit signals; it's up to you to emit the correct values.

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

GDScript can bind an array of values to connections between a signal and a method. When the signal is emitted, the callback method receives the bound values. These bound arguments are unique to each connection, and the values will stay the same.

You can use this array of values to add extra constant information to the connection if the emitted signal itself doesn't give you access to all the data that you need.

Building on the example above, let's say we want to display a log of the damage taken by each character on the screen, like Player1 took 22 damage.. The health_changed signal doesn't give us the name of the character that took damage. So when we connect the signal to the in-game console, we can add the character's name in the binds array argument:

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

Our BattleLog node receives each element in the binds array as an extra argument:

# 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 offers support for coroutines via the yield built-in function. Calling yield() will immediately return from the current function, with the current frozen state of the same function as the return value. Calling resume() on this resulting object will continue execution and return whatever the function returns. Once resumed, the state object becomes invalid. Here is an example:

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.

Will print:

Hello
my dear
world

It is also possible to pass values between yield() and resume(), for example:

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.

Will print:

Hello
world
cheers!

코루틴 & 시그널

The real strength of using yield is when combined with signals. yield can accept two arguments, an object and a signal. When the signal is received, execution will recommence. Here are some examples:

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

Coroutines themselves use the completed signal when they transition into an invalid state, for example:

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 키워드

When using nodes, it's common to desire to keep references to parts of the scene in a variable. As scenes are only warranted to be configured when entering the active scene tree, the sub-nodes can only be obtained when a call to Node._ready() is made.

var my_label

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

This can get a little cumbersome, especially when nodes and external references pile up. For this, GDScript has the onready keyword, that defers initialization of a member variable until _ready() is called. It can replace the above code with a single line:

onready var my_label = get_node("MyLabel")

Assert 키워드

The assert keyword can be used to check conditions in debug builds. These assertions are ignored in non-debug builds. This means that the expression passed as argument won't be evaluated in a project exported in release mode. Due to this, assertions must not contain expressions that have side effects. Otherwise, the behavior of the script would vary depending on whether the project is run in a debug build.

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

When running a project from the editor, the project will be paused if an assertion error occurs.