Godot 클래스는 정말 무엇입니까?

Godot는 생성 타입에 있어 두가지 중심 의미가 있습니다: 스크립트와 씬입니다. Godot를 객체 지향 디자인 중심으로 전개할 때, 둘은 모두 "클래스"를 나타냅니다. 초보자나 중급자에게 이것이 어떻게 가능한 지를 분명하게 알기는 어렵습니다.

Godot 엔진은 별도의 설치없이 클래스를 제공합니다 (Node처럼 말이죠), 하지만 사용자가 만든 타입은 사실 클래스가 아닙니다. 대신 엔진 클래스에서 작동하기 위해 엔진에게 초기화 시퀀스를 알려주는 리소스입나다.

Godot의 내장 클래스는 메서드를 가지며 클래스의 데이터를 ClassDB로 등록합니다. 이 데이터베이스는 ("reflection"이라 하는) 클래스 정보에 액세스하여 런타임을 제공합니다. ClassDB에 저장된 것은 다음 것도 포함됩니다...

  • 속성
  • 메서드
  • 상수
  • 시그널

게다가 ClassDB는 어떤 객체가 언제 연산을 수행하는 지를 확인합니다. 속성에 액세스하는가? 메서드를 호출하는가? 시그널을 방출하는가? 이 모두를 데이터베이스의 기록 (그리고 객체의 기본 타입의 기록)에서 확인함으로써 객체가 연산을 지원하는 지 여부를 볼 수 있습니다. 모든 C++ 객체는 정적 _bind_methods() 함수를 정의하는데 이걸로 데이터베이스에 등록한 C++ 내용물이 무엇이고 어떻게 등록했는지를 설명합니다.

따라서 엔진이 시작할 때 이 모든 데이터를 제공한다면, 사용자는 어떻게 고유의 데이터를 정의할까요? 사용자가 객체의 데이터에 추가하는 커스텀 데이터 세트를 정의하는 것이 좋습니다. 이 방법으로 사용자는 고유의 속성과 메서드를 엔진의 객체 쿼리 요청에 넣을 수 있습니다.

이것이 바로 Script입니다. 객체는 데이터베이스 이전에 붙어있는 스크립트를 확인합니다, 즉 스크립트는 메서드를 재정의할 수도 있습니다. 스크립트가 _get_property_list() 메서드를 정의한다면, ClassDB에서 객체가 가져오는 속성 목록이 데이터로 추가됩니다. 이는 다른 선언적 코드에서도 마찬가지입니다.

스크립트가 그 자체로 하나의 클래스가 되는 것을 보면 일부 사용자는 혼란에 빠질 수 있습니다. 사실 엔진은 기본 엔진 클래스를 자동으로 인스턴스화한 뒤 그 객체에 스크립트를 추가합니다. 이것으로 객체는 엔진 논리에 적절하다고 판단하는 스크립트의 내용을 이행합니다.

자체적인 문제가 있습니다. 객체의 규모가 커질수록, 그것을 만드는 데 필요한 스크립트의 규모도 더욱 더 커집니다. 이것은 노드 계층 구조를 만드는 것으로 드러납니다. 각 노드의 논리는 수 백줄의 코드가 될 수 있습니다.

한 노드를 자식으로 만드는 예제를 알아봅시다.

# main.gd
extends Node

var child # define a variable to store a reference to the child

func _init():
    child = Node.new() # Construct the child.
    child.name = "Child" # Change its name.
    child.script = preload("child.gd") # Give it custom features.
    child.owner = self # Serialize this node if self is saved.
    add_child(child) # Add "Child" as a child of self.
// Main.cs
using System;
using Godot;

namespace ExampleProject
{
    public class Main : Resource
    {
        public Node Child { get; set; }

        public Main()
        {
            Child = new Node(); // Construct the child.
            Child.Name = "Child"; // Change its name.
            Child.Script = (Script)ResourceLoader.Load("child.gd"); // Give it custom features.
            Child.Owner = this; // Serialize this node if this is saved.
            AddChild(Child); // Add "Child" as a child of this.
        }
    }
}

선언 노드의 두 부분만 자식 노드를 생성하는데 관여합니다: 변수 선언과 생성자 선언입니다. 자식에 관한 다른 나머지는 명령형 코드를 사용해 설정해야 합니다. 하지만 스크립트 코드는 엔진 C++ 코드보더 훨씬 더 느립니다. 각 변경사항은 스크립팅 API로의 호출을 만들게 되고, 이는 즉 데이터 구조 내 실행하기 위한 해당 논리를 찾는 많은 C++ "lookup들"을 의미합니다.

작업량을 줄이기 위해 무언가가 노드 계층 구조를 만들고 설정하는 데 관련된 모든 작업을 처리할 수 있다면 편리할 것입니다. 그러면 엔진은 빠른 C++ 코드를 사용해 그 구조를 다룰 수 있을 것이고, 스크립트 코드는 명령형 코드의 위험으로부터 벗어날 수 있을 것입니다.

이것이 바로 씬입니다 (PackedScene): 고급 "생성자" 직렬화를 제공하는 리소스로 일괄 처리를 위해 엔진이 오프로드하도록 합니다.

자, 왜 이렇게 씬 조직이 중요할까요? 씬이 바로 객체라는 것을 이해해야합니다. 때로는 하나의 씬과 스크립트과 붙은 루트 노드로 된 씬을 하위 노드로 사용하는 경우도 있습니다. 즉, 씬은 때로 스크립트의 선언적 코드의 확장이 됩니다.

씬이 정의하는 것은...

  • 어떤 객체를 스크립트에서 사용할 수 있는지?
  • 어떻게 그들이 조직되어있는지?
  • 어떻게 그들이 초기화되는지?
  • 그렇다면 서로에게 어떤 연결이 있는지?

이처럼 많은 객체 지향 원칙들이 "프로그래밍"에 적용됩니다, 즉 스크립트 또한 씬에 적용됩니다. 어떤 스크립트는 오직 하나의 씬에서만 작동하도록 설계됩니다 (보통은 씬 자체에서 번들로 제공됩니다). 그렇지 않은 스크립트는 씬 사이에서 재사용될 수 있습니다.

그럼에도 불구하고 씬은 항상 루트 스크립트의 확장이고, 그렇기에 클래스의 일부로 해석될 수 있습니다. 이 시리즈의 대부분은 이러한 시점에서 다루게 될 것입니다, 그러니 명심하세요.