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의 씬 시스템은 강력하고 유연하지만 하나 이상의 씬에 필요한 정보(예: 플래이어의 점수나 인벤토리)를 저장할 방법이 없다는 단점이 있습니다.

몇 가지 방법으로 이 문제를 해결할 수 있지만 한계점이 있습니다:

  • 다른 씬을 자식으로 로드 및 언로드하는 "마스터(master)" 씬을 사용할 수 있습니다. 하지만 그렇게 하면 더 이상 씬들을 개별적으로 실행할 수 없고 올바르게 작동할 것이라고 예상할 수 없습니다.

  • 정보를 디스크의 user://경로에 저장한 다음 이를 필요로 하는 씬에서 로드할 수 있지만 데이터를 자주 저장하고 로드하는 것은 번거롭고 느릴 수 있습니다.

싱글톤 패턴은 씬 간에 지속적으로 정보를 저장해야 하는 일반적인 사례를 해결하는 데 유용한 툴입니다. 우리의 경우 여러 싱글톤의 이름이 서로 다르기만 하면 동일한 장면이나 클래스를 여러 싱글톤에서 재사용할 수 있습니다.

이 개념을 사용해서 다음과 같은 오브젝트를 만들 수 있습니다:

  • 현재 실행 중인 씬에 관계없이 항상 불러와집니다

  • 플레이어 정보와 같은, 전역 변수를 저장할 수 있습니다.

  • 씬을 변경하는 것과 씬 사이에서의 전환을 다룰 수 있습니다.

  • GDScript가 전역 변수를 지원하지 않기 때문에 싱글톤처럼 작동합니다.

노드와 스크립트를 오토로드하면 위와 같은 특성을 적용할 수 있습니다.

참고

Godot는 오토로드를 싱글톤 디자인 패턴에 따르는 "진정한" 싱글톤으로 만들지 않습니다. 원한다면 사용자가 두 번 이상 인스턴스화할 수 있습니다.

편집기 플러그인의 일부로 자동 로드를 생성하는 경우, 플러그인이 활성화되면 :ref:`프로젝트 설정 <doc_making_plugins_autoload>`에 자동으로 등록하는 것을 고려해 보세요.

오토로드

오토로드를 생성해서 장면이나 Node를 상속받는 스크립트를 불러올 수 있습니다.

참고

스크립트를 오토로드할 때 Node가 생성되고 스크립트가 여기에 추가됩니다. 이 노드는 다른 씬이 로드되기 전에 루트 뷰포트에 추가됩니다.

../../_images/singleton.webp

씬이나 스크립트를 오토로드하려면 메뉴에서 시작하여 프로젝트 > 프로젝트 설정 > 전역 > 오토로드로 이동하세요.

../../_images/autoload_tab.webp

여기에서 씬이나 스크립트를 원하는 만큼 추가할 수 있습니다. 목록의 각 항목에는 노드의 name 속성으로 할당된 이름이 필요합니다. 전역 씬 트리에 추가되는 항목의 순서는 위쪽/아래쪽 화살표 키를 사용하여 조작할 수 있습니다.

../../_images/autoload_example.webp

Enable 열이 선택되어 있으면(기본값) get_node() 함수 없이 싱글톤에 직접 접근할 수 있습니다:

PlayerVariables.health -= 10

Enable 열은 C# 코드에 영향을 주지 않습니다. 그러나 싱글톤가 C# 스크립트인 경우 ``Instance``라는 정적 속성을 포함하고 이를 ``_Ready()``에 할당하면 비슷한 효과를 얻을 수 있습니다.

public partial class PlayerVariables : Node
{
    public static PlayerVariables Instance { get; private set; }

    public int Health { get; set; }

    public override void _Ready()
    {
        Instance = this;
    }
}

이를 통해 GetNode() 및 typecast 없이 C# 코드에서 싱글톤에 액세스할 수 있습니다.

PlayerVariables.Instance.Health -= 10;

오토로드 오브젝트(스크립트 및/또는 씬)는 씬 트리의 다른 노드와 마찬가지 방식으로 접근할 수 있습니다. 실제로 실행 중인 씬 트리를 보면 오토로드된 노드가 나타난 것을 볼 수 있습니다:

../../_images/autoload_runtime.webp

경고

자동 로드는 런타임에 free() 또는 ``queue_free()``를 사용하여 제거하면 안 됩니다. 그렇지 않으면 엔진이 충돌합니다.

커스텀 씬 전환기

이 튜토리얼에서는 오토로드를 사용해 씬 전환기를 빌드하는 방법을 설명합니다. 기초적인 씬 전환은 SceneTree.change_scene_to_file() 메서드를 사용할 수 있습니다 (자세한 사항은 씬 트리(SceneTree) 사용하기 참조). 그러나 씬을 변경할 때 더 복잡한 동작이 필요한 경우 오토로드를 사용하면 더 많은 기능을 제공합니다.

시작하려면 여기 autoload.zip에서 템플릿을 다운로드하고 Godot에서 여세요.

프로젝트가 마지막으로 이전 Godot 버전에서 열렸다는 알림 창이 나타날 수 있지만, 이건 문제가 되지 않습니다. 확인을 클릭하여 프로젝트를 여세요.

이 프로젝트에는 Scene1.tscnScene2.tscn의 두 씬이 있습니다. 각 씬에는 장면 이름을 표시하는 레이블과 pressed() 시그널이 연결된 버튼이 있습니다. 프로젝트를 실행하면 Scene1.tscn에서 시작합니다. 하지만 버튼을 눌러도 아무 작업도 수행되지 않습니다.

스크립트 만들기

Script 탭으로 전환하고 Global.gd라는 새 스크립트를 만드세요. Node를 상속받는지 확인하세요:

../../_images/autoload_script.webp

다음 단계는 이 스크립트를 오토로드 목록에 추가하는 것입니다. 메뉴에서 프로젝트 > 프로젝트 설정 > 전역 > 오토로드를 열고 찾아보기 버튼을 클릭하거나 경로 res://global.gd를 입력하여 스크립트를 선택하세요. 추가를 눌러 오토로드 목록에 추가하고 스크립트 이름을 "Global"로 지정하세요. 스크립트가 "Global"이라는 이름으로 해당 스크립트에 접근하려면 이 이름이 필수입니다:

../../_images/autoload_tutorial1.webp

이제 프로젝트에서 어떤 씬이든지 실행할 때마다 이 스크립트가 항상 로드됩니다.

스크립트로 돌아가서 _ready() 함수에서 현재 씬을 가져와야 합니다. (버튼이 있는) 현재 씬과 Global.gd는 모두 루트의 자식이지만 오토로드된 노드가 항상 첫 번째에 있습니다. 이는 루트의 마지막 자식이 항상 불러온 씬임을 의미합니다.

extends Node

var current_scene = null

func _ready():
    var root = get_tree().root
    # Using a negative index counts from the end, so this gets the last child node of `root`.
    current_scene = root.get_child(-1)

이제 씬을 변경하는 함수가 필요합니다. 이 함수는 현재 씬을 해제하고 요청한 씬으로 교체해야 합니다.

func goto_scene(path):
    # This function will usually be called from a signal callback,
    # or some other function in the current scene.
    # Deleting the current scene at this point is
    # a bad idea, because it may still be executing code.
    # This will result in a crash or unexpected behavior.

    # The solution is to defer the load to a later time, when
    # we can be sure that no code from the current scene is running:

    _deferred_goto_scene.call_deferred(path)


func _deferred_goto_scene(path):
    # It is now safe to remove the current scene.
    current_scene.free()

    # Load the new scene.
    var s = ResourceLoader.load(path)

    # Instance the new scene.
    current_scene = s.instantiate()

    # Add it to the active scene, as child of root.
    get_tree().root.add_child(current_scene)

    # Optionally, to make it compatible with the SceneTree.change_scene_to_file() API.
    get_tree().current_scene = current_scene

Object.call_deferred()를 사용하면 두 번째 함수는 현재 씬의 모든 코드가 완료된 후에만 실행됩니다. 따라서 현재 씬이 사용 중인 동안에는 제거되지 않습니다(즉, 해당 코드가 계속 실행 중임).

마지막으로, 두 씬에서 비어있는 콜백 함수를 채워야 합니다:

# Add to 'scene_1.gd'.

func _on_button_pressed():
    Global.goto_scene("res://scene_2.tscn")

그리고

# Add to 'scene_2.gd'.

func _on_button_pressed():
    Global.goto_scene("res://scene_1.tscn")

프로젝트를 실행하고 버튼을 누를 때마다 씬이 바뀌는지 확인해보세요.

참고

씬이 작다면 씬 전환이 빠르게 됩니다. 그러나 씬이 더 복잡한 경우 표시되는 데 상당한 시간이 걸릴 수 있습니다. 이를 처리하는 방법을 배우려면 다음 튜토리얼 백그라운드 불러오기을 살펴보세요.

로딩 시간이 비교적 짧은 경우(3초 미만) 장면을 변경하기 직전에 일종의 2D 요소를 표시해서 "로딩 플라크(plaque)"를 표시할 수 있습니다. 그런 다음 씬이 변경된 직후에 숨길 수 있습니다. 이는 장면이 로드되고 있음을 플레이어에게 알려주는 데 사용할 수 있습니다.