Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

씬과 스크립트 중 하나를 사용해야 하는 경우

우리는 이미 씬과 스크립트가 다르다는 것을 압니다. 스크립트는 명령형 코드, 선언형 코드로 만든 씬을 사용해 엔진 클래스 확장을 정의합니다.

Each system's capabilities are different as a result. Scenes can define how an extended class initializes, but not what its behavior actually is. Scenes are often used in conjunction with a script, the scene declaring a composition of nodes, and the script adding behaviour with imperative code.

익명 타입

스크립트 하나 만으로 씬의 내용을 완전히 정의하는 것은 가능합니다. 본질적으로, Godot 편집기가 하는 일은 오직 오브젝트의 C++ 생성자에서 이루어집니다.

하지만 어떤 것을 사용해야 하는지 결정하는 것은 딜레마가 됩니다. 스크립트 인스턴스를 만드는 것은 인 게임 클래스를 만드는 것과 동일하지만 씬을 다루려면 API를 변경해야 합니다:

const MyNode = preload("my_node.gd")
const MyScene = preload("my_scene.tscn")
var node = Node.new()
var my_node = MyNode.new() # Same method call.
var my_scene = MyScene.instantiate() # Different method call.
var my_inherited_scene = MyScene.instantiate(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene.

또한 엔진과 스크립트 코드 간의 속도 차 때문에 스크립트가 씬에 비해 조금 더 느리게 작동합니다. 노드가 크고 복잡할수록, 씬으로 작업하는 것이 더 좋습니다.

명명된 타입

Scripts can be registered as a new type within the editor itself. This displays it as a new type in the node or resource creation dialog with an optional icon. This way, the user's ability to use the script is much more streamlined. Rather than having to...

  1. 사용하고 싶은 스크립트의 기본 타입을 알기.

  2. 기본 타입의 인스턴스를 생성하기.

  3. 노드에 스크립트를 추가하기.

With a registered script, the scripted type instead becomes a creation option like the other nodes and resources in the system. The creation dialog even has a search bar to look up the type by name.

There are two systems for registering types:

  • 맞춤 타입

    • 편집기만 가능. 런타임 도중 타입이름에 접근할 수 없습니다.

    • 상속된 맞춤 타입을 지원하지 않습니다.

    • 초기화 툴. 스크립트로 노드를 만듭니다. 더 이상은 없습니다.

    • 편집기는 스크립트에 대한 타입 인식이나 다른 엔진 타입이나 스크립트 간의 관계가 없습니다.

    • 사용자가 아이콘을 정의할 수 있도록 합니다.

    • 스크립트 리소스를 추상화하기 때문에 모든 스크립트 언어에서 작동합니다.

    • EditorPlugin.add_custom_type을 사용해 설정합니다.

  • Script Classes

    • 편집기와 런타임에 접근할 수 있습니다.

    • 상속 관계를 전체적으로 표시합니다.

    • 스크립트로 노드를 만듭니다, 하지만 편집기에서 타입을 바꾸거나 확장할 수도 있습니다.

    • 편집기는 스크립트, 스크립트 클래스 및 엔진 C++ 클래스 간의 상속 관계를 인식합니다.

    • 사용자가 아이콘을 정의할 수 있도록 합니다.

    • 엔진 개발자는 수동으로 언어 지원을 추가해야 합니다 (이름 표시와 런타임 접근 가능성 모두).

    • Godot 3.1+에서만 가능합니다.

    • 편집기는 프로젝트 폴더를 스캔하고 모든 스크립팅 언어의 노출된 이름을 등록합니다. 각 스크립팅 언어가 이 정보가 노출되는 것을 자체적인 지원으로 구현해야 합니다.

두 방법론 모두 생성 대화 상자에 이름을 추가할 수 있습니다, 하지만 특히 스크립트 클래스는 사용자가 스크립트 리소스를 불러오지 않고도 타입이름에 접근할 수 있습니다. 인스턴스를 만들고 상수나 정적 메서드에 접근하는 것은 어디서나 가능합니다.

이러한 기능으로, 사람들은 사용자가 사용하기 쉽도록 그들의 타입이 씬이 없는 스크립트가 되길 원합니다. 플러그인을 개발하거나 디자이너를 위한 사내 툴을 만드는 사람들에게 이러한 방식은 더 쉬운 작업 시간을 찾을 수 있을 겁니다.

단점은 명령형 프로그래밍을 주로 사용해야 하는 것입니다.

스크립트와 PackedScene의 성능 비교

마지막으로 씬과 스크립트를 선택할 때 고려할 사항은 실행 속도입니다.

As the size of objects increases, the scripts' necessary size to create and initialize them grows much larger. Creating node hierarchies demonstrates this. Each Node's logic could be several hundred lines of code in length.

아래의 코드 예제는 새로운 Node 를 생성하고, 이름을 변경하고, 스크립트를 지정하고, 소유자를 부모가 될 노드로 초기화해 디스크에 같이 저장되도록 한 뒤 Main 노드의 자식으로 등록하는 코드입니다:

# main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("child.gd")
    add_child(child)
    child.owner = self

이와 같은 스크립트 코드는 엔진쪽의 C++ 코드보다 훨씬 느립니다. 매 명령은 스크립트 API를 호출하기 때문에 실행할 로직을 찾기 위해 백엔드에서 많은 "lookup"이 일어나게 됩니다.

씬은 이런 문제를 피할 수 있도록 해줍니다. 씬이 상속하는 기본 타입인 :ref:`PackedScene <class_PackedScene>`은 오브젝트를 생성할 때 직렬화된 데이터를 사용하는 리소스를 정의합니다. 엔진은 백엔드에서 씬을 여러 뭉치로 처리할 수 있게 되고 스크립트보다 훨씬 나은 성능을 보입니다.

결론

마지막으로, 최선의 접근법은 다음을 고려해보는 것입니다:

  • 여러 다른 프로젝트에서 재사용하며, ("프로그래머"라는 이름표가 없는 사람들을 포함한) 모든 실력 수준의 사람들이 사용할 수 있는 기본 툴을 만들고 싶다면, 아마 스크립트로 만들어야 할 것입니다, 맞춤 이름/아이콘을 줄 수 있으니까요.

  • 게임에 특화된 개념을 만들고 시다면, 그것은 항상 씬이 되어야 합니다. 씬이 스크립트보다 더 쉽게 연결/편집하고 더 보안이 좋습니다.

  • If one would like to give a name to a scene, then they can still sort of do this by declaring a script class and giving it a scene as a constant. The script becomes, in effect, a namespace:

    # game.gd
    class_name Game # extends RefCounted, so it won't show up in the node creation dialog.
    extends RefCounted
    
    const MyScene = preload("my_scene.tscn")
    
    # main.gd
    extends Node
    func _ready():
        add_child(Game.MyScene.instantiate())