Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

리소스

노드와 리소스

지금까지 우리는 Godot에서 동작을 코딩하는 데 쓰이고 엔진 대부분의 기능이 의존하는 Node 클래스에 초점을 두었습니다. 이와 마찬가지로 중요한 데이터 타입: Resource가 있습니다.

노드는 스프라이트 그리기, 3D 모델, 물리 시뮬레이션, 사용자 인터페이스 정렬 등의 기능을 제공합니다. 리소스데이터 컨테이너입니다. 리소스 자체적으로는 아무 것도 하지 않습니다. 대신 노드는 리소스에 포함된 데이터를 사용합니다.

Godot가 디스크에 저장하거나 디스크에서 불러오는 모든 것은 리소스입니다. 씬(.tscn 또는 .scn 파일), 이미지, 스크립트 등이 있습니다. 다음은 몇 가지 리소스의 예입니다:

엔진이 디스크에서 리소스를 불러올 때 항상 한 번만 불러옵니다. 해당 리소스의 복사본이 이미 메모리에 있는 경우 리소스를 다시 불러오려고 하면 매번 같은 복사본을 반환합니다. 리소스에는 데이터만 포함되어 있으므로 리소스를 복제할 필요가 없습니다.

노드 또는 리소스와 같은 모든 개체는 속성을 내보낼 수 있습니다. String, integer, Vector2 등과 같은 많은 타입의 속성 중 하나가 리소스가 될 수 있습니다. 즉, 노드와 리소스 모두 리소스를 속성으로 포함할 수 있습니다:

../../_images/nodes_resources.webp

외부 대 내장

리소스를 저장하는 두 가지 방법이 있습니다. 두 가지 방법은 다음과 같습니다:

  1. 씬의 외부 에 개별 파일로 저장.

  2. .tscn이나 .scn 파일 안에 내장된 채로 저장.

좀 더 구체적으로 말하면, 다음은 Sprite 노드의 Texture입니다:

../../_images/spriteprop.webp

리소스 미리보기를 클릭해서 리소스를 보고 속성을 편집할 수 있습니다.

../../_images/resourcerobi.webp

Path 속성은 리소스의 경로를 알려줍니다. 이 경우 리소스는 robi.png라는 PNG 이미지에서 가져옵니다. 이와 같이 파일에서 리소스를 불러오는 경우 외부 리소스라고 합니다. Path를 지우거나 비워 두면 내장 리소스가 됩니다.

내장 리소스와 외부 리소스 간의 전환은 씬을 저장할 때 발생합니다. 위의 예시에서 경로 "res://robi.png"를 지우고 저장한다면, Godot는 .tscn 씬 파일 안에 이미지를 저장합니다.

참고

내장 리소스로 저장하더라도 씬을 여러 번 인스턴스화하면 엔진은 해당 복사본을 하나만 불러옵니다.

코드에서 리소스 불러오기

코드에서 리소스를 불러오는 방법은 두 가지가 있습니다. 첫 번째로, 언제든지 load() 함수를 사용해 불러올 수 있습니다:

func _ready():
    # Godot loads the Resource when it reads this very line.
    var imported_resource = load("res://robi.png")
    $sprite.texture = imported_resource

리소스를 미리 불러올(preload)수도 있습니다. load와는 다르게, 이 함수는 디스크에서 파일을 읽고 컴파일 시간에 파일을 불러옵니다. 결과적으로, 변수 경로로 미리 불러오기를 호출할 수 없고 경로에 상수 문자열을 사용해야 합니다.

func _ready():
    # Godot loads the resource at compile-time
    var imported_resource = preload("res://robi.png")
    get_node("sprite").texture = imported_resource

씬 불러오기

씬도 리소스이지만 함정이 있습니다. 씬은 디스크에 PackedScene 타입 리소스로 저장됩니다. 씬은 리소스 안에 압축되어 있습니다.

씬의 인스턴스를 얻으려면 PackedScene.instance() 메서드를 사용해야 합니다.

func _on_shoot():
        var bullet = preload("res://bullet.tscn").instantiate()
        add_child(bullet)

이 메서드는 씬의 계층 구조에 노드를 만들고, 구성하고, 씬의 루트 노드를 반환합니다. 그런 다음 이것을 다른 노드의 자식으로 추가할 수 있습니다.

이 접근 방식에는 몇 가지 장점이 있습니다. PackedScene.instance() 함수가 빠르기 때문에 매번 디스크에서 다시 불러올 필요 없이 새로운 적, 총알, 효과 등을 만들 수 있습니다. 항상 그렇듯이 이미지, 메시 등은 씬 인스턴스 간에 모두 공유됩니다.

리소스 해제(Free)하기

리소스가 더 이상 사용되지 않으면 자동으로 해제됩니다. 대부분의 경우 리소스는 노드에 포함되어 있기 때문에 노드가 소유한 리소스를 다른 노드에서 사용하지 않는 경우 노드를 해제하면 해당 리소스도 해제합니다.

여러분만의 리소스 만들기

Godot의 어떤 오브젝트나 마찬가지로 유저는 리소스를 스크립트로 작성할 수도 있습니다. 리소스 스크립트는 개체 속성과 직렬화된 텍스트 또는 이진 데이터(/.tres, /.res) 간에 자유롭게 변환하는 기능을 상속받습니다. 또한 참조 타입으로부터 참조 카운팅(reference-counting) 메모리 관리를 상속받습니다.

이는 JSON, CSV 또는 커스텀 TXT 파일과 같은 대체 데이터 구조에 비해 많은 뚜렷한 이점이 있습니다. 사용자는 이러한 애셋을 구문 분석(parse)하기 위해 Dictionary (JSON) 또는 File로만 가져올 수 있습니다. 리소스를 구분하는 것은 Object, Reference, Resource 기능의 상속입니다:

  • 상수를 정의할 수 있으므로 다른 데이터 필드나 오브젝트의 상수가 필요하지 않습니다.

  • 속성에 대한 setter/getter 메서드를 포함한 메서드를 정의할 수 있습니다. 이를 통해 기본 데이터를 추상화하고 캡슐화할 수 있습니다. 리소스 스크립트의 구조를 변경해야 하는 경우 리소스를 사용하는 게임을 변경할 필요가 없습니다.

  • 시그널을 정의할 수 있으므로 리소스가 관리하는 데이터의 변경사항에 대한 응답을 트리거할 수 있습니다.

  • 정의된 속성이 있으므로 사용자는 자신의 데이터가 존재한다는 것을 100% 알 수 있습니다.

  • 리소스 자동 직렬화 및 역직렬화는 Godot 엔진에 내장된 기능입니다. 사용자는 리소스 파일의 데이터를 가져오거나 내보내기 위해 사용자 지정 로직을 구현할 필요가 없습니다.

  • 리소스는 하위 리소스를 재귀적으로 직렬화할 수도 있습니다. 이는 유저가 훨씬 더 정교한 데이터 구조를 설계할 수 있음을 의미합니다.

  • 유저는 리소스를 버전 관리에 적합한 텍스트 파일(*.tres)로 저장할 수 있습니다. 게임을 내보낼 때 Godot는 리소스 파일을 바이너리 파일(*.res)로 직렬화해서 속도와 압축률을 높입니다.

  • Godot 엔진의 인스펙터는 리소스 파일을 바로 렌더링하고 편집합니다. 따라서 사용자는 데이터를 시각화하거나 편집하기 위해 사용자 지정 로직을 구현할 필요가 없는 경우가 많습니다. 이렇게 하려면 파일시스템 독에서 리소스 파일을 더블 클릭하거나 인스펙터에서 폴더 아이콘을 클릭하고 대화 상자에서 파일을 여세요.

  • 기본 리소스 외에 다른 리소스 타입을 확장할 수 있습니다.

Godot의 인스펙터에서는 커스텀 리소스를 쉽게 만들 수 있습니다.

  1. 인스펙터에서 새 리소스 오브젝트를 만듭니다. 스크립트가 해당 유형을 확장하는 동안 리소스를 파생하는 유혈일 수도 있습니다.

  2. 인스펙터에서 script 속성을 여러분의 스크립트로 설정합니다.

인스펙터에는 이제 리소스 스크립트의 사용자 지정 속성을 표시합니다. 해당 값을 편집하고 리소스를 저장하면 인스펙터가 사용자 지정 속성도 직렬화합니다! 인스펙터에서 리소스를 저장하려면 인스펙터의 툴 메뉴(오른쪽 상단)를 클릭하고 "저장" 또는 "다른 이름으로 저장..."을 선택하세요.

스크립트 언어가 스크립트 클래스를 지원하는 경우 프로세스가 간소화됩니다. 스크립트 이름만 정의하면 인스펙터의 생성 대화 상자에 추가됩니다. 이렇게 하면 생성한 리소스 오브젝트에 스크립트가 자동으로 추가됩니다.

몇 가지 예를 살펴보겠습니다. 만들기 :ref:`Resource <class_Resource>`을 지정하고 이름을 ``bot_stats``로 지정합니다. 파일 탭에 전체 이름 ``bot_stats.tres``로 표시되어야 합니다. 스크립트가 없으면 쓸모가 없으므로 데이터와 로직을 추가해 보겠습니다! ``bot_stats.gd``라는 이름에 스크립트를 연결합니다(또는 새 스크립트를 만든 다음 끌어서 놓기만 하면 됩니다).

참고

새 리소스 클래스가 만들기 리소스 GUI에 나타나도록 하려면 GDScript에 클래스 이름을 제공하거나 C#에서 [GlobalClass] 특성을 사용해야 합니다.

class_name BotStats
extends Resource

@export var health: int
@export var sub_resource: Resource
@export var strings: PackedStringArray

# Make sure that every parameter has a default value.
# Otherwise, there will be problems with creating and editing
# your resource via the inspector.
func _init(p_health = 0, p_sub_resource = null, p_strings = []):
    health = p_health
    sub_resource = p_sub_resource
    strings = p_strings

이제 :ref:`CharacterBody3D <class_CharacterBody3D>`을 생성하고 이름을 ``Bot``로 지정하고 다음 스크립트를 추가합니다.

extends CharacterBody3D

@export var stats: Resource

func _ready():
    # Uses an implicit, duck-typed interface for any 'health'-compatible resources.
    if stats:
        stats.health = 10
        print(stats.health)
        # Prints "10"

이제 bot``라는 이름의 :ref:`CharacterBody3D <class_CharacterBody3D>` 노드를 선택하고 ``bot_stats.tres 리소스를 인스펙터로 끌어다 놓습니다. 10이 인쇄되어야 합니다! 분명히 이 설정은 이보다 더 고급 기능에 사용될 수 있지만 모든 작동 방식을 실제로 이해하는 한 리소스와 관련된 다른 모든 것을 파악해야 합니다.

참고

리소스 스크립트는 Unity의 ScriptableObjects와 유사합니다. 인스펙터는 사용자 지정 리소스를 위한 내장 지원을 제공합니다. 원한다면 사용자만의 Control 기반 도구 스크립트를 디자인하고 이를 EditorPlugin과 결합해서 데이터에 대한 사용자 지정 시각화 및 편집기를 만들 수도 있습니다.

언리얼 엔진 4의 DataTables와 CurveTables도 리소스 스크립트를 사용해 쉽게 다시 만들 수 있습니다. DataTables은 커스텀 구조체에 매핑된 문자열로, 이차적인 커스텀 리소스 스크립트에 문자열을 매핑하는 딕셔너리와 유사합니다.

# bot_stats_table.gd
extends Resource

const BotStats = preload("bot_stats.gd")

var data = {
    "GodotBot": BotStats.new(10), # Creates instance with 10 health.
    "DifferentBot": BotStats.new(20) # A different one with 20 health.
}

func _init():
    print(data)

딕셔너리 값을 인라이닝(inlining)하는 대신에...

  1. 스프레드시트에서 테이블 값을 가져와서 키 값 쌍을 생성할 수 있습니다. 또는...

  2. 편집기 안에서 시각화를 설계하고 해당 타입 리소스를 열 때 인스펙터에 리소스를 추가하는 간단한 플러그인을 제작할 수 있습니다.

CurveTable은 float 배열 또는 Curve/Curve2D 리소스 개체에 매핑된다는 점을 제외하고는 동일합니다.

경고

리소스 파일(*.tres/*.res)은 파일에서 사용하는 스크립트의 경로를 저장한다는 점에 유의하세요. 불러오면 이 스크립트를 해당 유형의 확장자로 가져와서 불러옵니다. 이는 스크립트의 하위 클래스, 즉 (GDScript에서 class 키워드 사용과 같은) 내부 클래스를 할당하려는 시도는 작동하지 않습니다. Godot는 스크립트 하위 클래스 속성의 사용자 지정 속성을 제대로 직렬화하지 않을 것입니다.

아래 예시에서, Godot는 Node 스크립트를 불러오고, Resource를 확장(extend)하지 않는지 확인한 다음, 이 경우 타입이 호환되지 않으므로 스크립트가 Resource 오브젝트를 불러오는 데 실패했다고 판단합니다.

extends Node

class MyResource:
    extends Resource
    @export var value = 5

func _ready():
    var my_res = MyResource.new()

    # This will NOT serialize the 'value' property.
    ResourceSaver.save(my_res, "res://my_res.tres")