GDScript basics

소개

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 를 보십시오.
do do...while 루프의 향후 구현을 위해 예약됩니다.
while while 을 보십시오.
match match 를 보십시오.
switch 향후 구현을 위해 예약됩니다.
case 향후 구현을 위해 예약됩니다.
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)합니다. Classes as resources 를 참고하세요.
yield 코루틴을 지원합니다. Coroutines with yield 를 참고하세요.
assert 조건을 가정하고 실패 시 오류를 기록합니다. 디버그가 아닌 빌드에는 무시됩니다. Assert keyword 를 참고하세요.
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 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 also 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:

딕셔너리 (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()

If the variable is initialized within the declaration, the type can be inferred, so it's possible to omit the type name:

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를 사용할 수 있습니다.

Casting between object types results in the same object if the value is of the same type or a subtype of the cast type.

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

Although the type of constants is inferred from the assigned value, it's also possible to add explicit type specification:

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_IDLE = 0
const STATE_JUMP = 5
const STATE_SHOOT = 6
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}

함수(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

주석

Non-void functions must always return a value, so if your code has branching statements (such as an if/else construct), all the possible paths must have a return. E.g., if you have a return inside an if block but not after it, the editor will raise an error because if the block is not executed, the function won't have a valid value to 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)

Remember that default functions, like _init, and most notifications, such as _enter_tree, _exit_tree, _process, _physics_process, etc. are called in all base classes automatically. So there is only a need to call the function explicitly when overloading them in some way.

정적 함수

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

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

A match statement is used to branch execution of a program. It's the equivalent of the switch statement found in many other languages, but offers some additional features.

기본 구문:

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

Crash-course for people who are familiar with switch statements:

  1. switchmatch 로 바꾸십시오
  2. case 를 제거하십시오
  3. Remove any breaks. If you don't want to break by default, you can use continue for a fallthrough.
  4. default 를 하나의 밑줄로 변경하십시오.

제어 흐름:

The patterns are matched from top to bottom. If a pattern matches, the corresponding block will be executed. After that, the execution continues below the match statement. If you want to have a fallthrough, you can use continue to stop execution in the current block and check the ones below it.

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

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

    The length of the array is tested first, it has to be the same size as the pattern, otherwise the pattern doesn't match.

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

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

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

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

    The size of the dictionary is tested first, it has to be the same size as the pattern, otherwise the pattern doesn't match.

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

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

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

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

    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

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

기본 클래스 (즉, 현재 클래스에서 extend 된 것)에서 함수를 호출하려면, . 를 함수 이름 앞에 붙이세요:

.basefunc(args)

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

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

클래스 생성자

클래스 인스턴스에 호출되는, 클래스 생성자의 이름은 _init 입니다. 앞서 언급한 바와 같이, 부모 클래스의 생성자는 클래스를 상속할 때 자동으로 호출됩니다. 그래서 보통 ._init() 을 명시적으로 호출할 필요는 없습니다.

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

이것은 예제를 통해 더 잘 설명됩니다. 이 시나리오가 있다고 가정하십시오:

# 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) has to define _init as well and pass appropriate parameters to _init from State.gd
  2. Idle.gd 는 기본 클래스 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 base class even if it does nothing. Which 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)

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

extends Button

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

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

One of the fundamental benefits of exporting member variables is to have them visible and editable in the editor. This way, artists and game designers can modify values that later influence how the program runs. For this, a special export syntax is provided.

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

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

배열 내보내기

Exporting arrays works, but with an important caveat: While regular arrays are created local to every class instance, exported arrays are shared between all instances. This means that editing them in one instance will cause them to change in all other instances. Exported arrays can have initializers, but they must be constant expressions.

# 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

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 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는 입력이 유효한지 알아보기 위해, 툴 스크립트나 플러그인에서 에디터로 변수를 내보낼 때 특히 유용합니다.

As said, local access will not trigger the setter and getter. Here is an illustration of this:

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)

툴 모드

Scripts, by default, 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")

메모리 관리

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

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

시그널(Signal)

인스턴스에 어떤 일이 발생했다는 알림을 보내는 것이 종종 요구됩니다. GDScript는 내장 Godot 시그널을 지원합니다. signal 키워드를 사용하여 GDScript에 쉽게 시그널을 선언할 수 있습니다.

# No arguments.
signal your_signal_name
# With arguments.
signal your_signal_name_with_args(a, b)

이 시그널은 에디터나 다른 코드에서 정규 시그널처럼 연결될 수 있습니다. 시그널이 선언된 인스턴스에서 시그널을 가져와 다른 인스턴스의 메서드에 연결합니다:

func _callback_no_args():
    print("Got callback!")

func _callback_args(a,b):
    print("Got callback with args! a: ", a, " and b: ", b)

func _at_some_func():
    instance.connect("your_signal_name", self, "_callback_no_args")
    instance.connect("your_signal_name_with_args", self, "_callback_args")

또한 커스텀 값으로 인수가 없는 시그널에 인수를 바인딩 할 수 있습니다:

func _at_some_func():
    instance.connect("your_signal_name", self, "_callback_args", [22, "hello"])

다양한 객체들의 시그널이 단일 콜백함수에 연결되어 있고, 보내는 자(sender)가 식별되야 할 때 유용합니다:

func _button_pressed(which):
    print("Button was pressed: ", which.get_name())

func _ready():
    for b in get_node("buttons").get_children():
        b.connect("pressed", self, "_button_pressed",[b])

마지막으로, 커스텀 시그널은 Object.emit_signal 메서드를 사용해 방출됩니다:

func _at_some_func():
    emit_signal("your_signal_name")
    emit_signal("your_signal_name_with_args", 55, 128)
    some_instance.emit_signal("some_signal")

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.

이렇게 프린트 될 것입니다:

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!

코루틴 & 시그널

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"), "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 will only continue execution once both buttons have been pressed.

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)