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.
Checking the stable version of the documentation...
객체 클래스
더 보기
이 페이지는 Godot에서 객체의 C++ 구현을 설명합니다. 객체 클래스 참조를 찾고 계십니까? 여기를 살펴보세요.
일반 정의
:ref:`Object <class_object>`는 거의 모든 것에 대한 기본 클래스입니다. Godot의 대부분의 클래스는 직접 또는 간접적으로 상속받습니다. 이를 선언하려면 다음과 같은 단일 매크로를 사용하면 됩니다.
class CustomObject : public Object {
GDCLASS(CustomObject, Object); // This is required to inherit from Object.
};
객체에는 반사 및 편집 가능한 속성과 같은 다양한 기능이 내장되어 있습니다.
CustomObject *obj = memnew(CustomObject);
print_line("Object class: ", obj->get_class()); // print object class
OtherClass *obj2 = Object::cast_to<OtherClass>(obj); // Converting between classes, similar to dynamic_cast
참고자료:
스크립트를 클래스로 등록하기
대부분의 Object 서브클래스는 ``GDREGISTER_CLASS``를 호출하여 등록됩니다.
GDREGISTER_CLASS(MyCustomClass)
그러면 클래스가 스크립트, 코드 또는 역직렬화를 통해 인스턴스화될 수 있도록 ``ClassDB``에 명명된 공용 클래스로 등록됩니다. ``GDREGISTER_CLASS``로 등록된 클래스는 예를 들어 편집기나 문서 시스템에 의해 자동으로 인스턴스화되거나 해제되어야 합니다.
GDREGISTER_CLASS 외에도 몇 가지 다른 비공개 모드가 있습니다.
// Registers the class publicly, but prevents automatic instantiation through ClassDB.
GDREGISTER_VIRTUAL_CLASS(MyCustomClass);
// Registers the class publicly, but prevents all instantiation through ClassDB.
GDREGISTER_ABSTRACT_CLASS(MyCustomClass);
// Registers the class in ClassDB, but marks it as private,
// such that it is not visible to scripts or extensions.
// This is the same as not registering the class explicitly at all
// - in this case, the class is registered as internal automatically
// when it is first constructed.
GDREGISTER_INTERNAL_CLASS(MyCustomClass);
// Registers the class such that it is only available at runtime (but not in the editor).
GDREGISTER_RUNTIME_CLASS(MyCustomClass);
GDCLASS(MyCustomClass, SuperClass) 대신 ``GDSOFTCLASS(MyCustomClass, SuperClass)``를 사용하는 것도 가능합니다. 이렇게 정의된 클래스는 ``ClassDB``에 전혀 등록되지 않습니다. 이는 때때로 플랫폼별 하위 클래스에 사용됩니다.
스크립트를 클래스로 등록하기
개체 파생 클래스는 정적 함수 ``static void _bind_methods()``를 재정의할 수 있습니다. 클래스가 등록되면 이 정적 함수가 호출되어 모든 객체 메서드, 속성, 상수 등을 등록합니다. 이 함수는 한 번만 호출됩니다.
_bind_methods 내에서 수행할 수 있는 몇 가지 작업이 있습니다. 기능 등록은 다음 중 하나입니다.
ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method);
인수의 기본값은 마지막에 매개변수로 전달될 수 있습니다.
ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method, DEFVAL(-1), DEFVAL(-2)); // Default values for arg2name (-1) and arg3name (-2).
기본값은 선언된 순서와 동일하게 제공되어야 하며 필수 인수는 건너뛰고 선택적 인수에는 기본값을 제공해야 합니다. 이는 C++에서 메서드를 선언하는 구문과 일치합니다.
``D_METHOD``는 효율성을 높이기 위해 "methodname"을 StringName으로 변환하는 매크로입니다. 인수 이름은 자체 검사에 사용되지만 릴리스 시 컴파일할 때 매크로는 이를 무시하므로 문자열은 사용되지 않고 최적화됩니다.
더 많은 예제를 보려면 컨트롤 또는 개체의 ``_bind_methods``를 확인하세요.
철저하게 문서화될 것으로 예상되지 않는 모듈과 기능을 추가하는 경우 D_METHOD() 매크로는 안전하게 무시될 수 있으며 간결성을 위해 이름을 전달하는 문자열이 전달될 수 있습니다.
참고자료:
상수
클래스에는 다음과 같은 열거형이 있는 경우가 많습니다.
enum SomeMode {
MODE_FIRST,
MODE_SECOND
};
메서드에 바인딩할 때 이러한 기능이 작동하려면 열거형을 int로 변환할 수 있도록 선언해야 합니다. 이를 돕기 위해 매크로가 제공됩니다.
VARIANT_ENUM_CAST(MyClass::SomeMode); // now functions that take SomeMode can be bound.
다음을 사용하여 상수를 _bind_methods 내부에 바인딩할 수도 있습니다.
BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);
속성(설정/가져오기)
개체 내보내기 속성, 속성은 다음에 유용합니다.
개체를 직렬화 및 역직렬화합니다.
객체 파생 클래스에 대해 편집 가능한 값 목록을 만듭니다.
속성은 일반적으로 PropertyInfo() 클래스에 의해 정의되고 다음과 같이 구성됩니다.
PropertyInfo(type, name, hint, hint_string, usage_flags)
예를 들면:
PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "0,49,1", PROPERTY_USAGE_EDITOR)
이는 "amount"라는 정수 속성입니다. 힌트는 범위이며 범위는 1(정수) 단위로 0부터 49까지입니다. 편집기(값을 시각적으로 편집)에만 사용할 수 있지만 직렬화되지는 않습니다.
또 다른 예:
PropertyInfo(Variant::STRING, "modes", PROPERTY_HINT_ENUM, "Enabled,Disabled,Turbo")
이것은 문자열 속성이며 모든 문자열을 사용할 수 있지만 편집기에서는 정의된 힌트 문자열만 허용합니다. 사용 플래그가 지정되지 않았으므로 기본 플래그는 PROPERTY_USAGE_STORAGE 및 PROPERTY_USAGE_EDITOR입니다.
object.h에는 많은 힌트와 사용 플래그가 있으므로 확인해 보세요.
속성은 C# 속성처럼 작동할 수도 있고 인덱싱을 사용하여 스크립트에서 액세스할 수도 있지만 가독성을 위해 함수를 사용하는 것이 선호되므로 일반적으로 이러한 사용은 권장되지 않습니다. 많은 속성은 연산자 []를 사용하지 않는 한 인덱싱을 불가능하게 만드는 "애니메이션/프레임"과 같은 범주와도 바인딩됩니다.
``_bind_methods()``부터 설정/가져오기 기능이 존재하는 한 속성을 생성하고 바인딩할 수 있습니다. 예:
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount")
그러면 setter와 getter를 사용하여 속성이 생성됩니다.
_set/_get/``_get_property_list``를 사용하는 바인딩 속성
더 많은 유연성이 필요한 경우(예: 컨텍스트에 따라 속성 추가 또는 제거) 속성을 생성하는 추가 방법이 있습니다.
다음 함수는 객체 파생 클래스에서 재정의될 수 있습니다. 가상이 아니며 가상으로 만들지 마세요. 재정의할 때마다 호출되며 이전 함수는 무효화되지 않습니다(다단계 호출).
protected:
void _get_property_list(List<PropertyInfo> *r_props) const; // return list of properties
bool _get(const StringName &p_property, Variant &r_value) const; // return true if property was found
bool _set(const StringName &p_property, const Variant &p_value); // return true if property was found
또한 ``p_property``는 원하는 이름과 일련 순서로 비교해야 하므로 효율성이 약간 떨어집니다.
시그널
개체에는 정의된 시그널 집합이 있을 수 있습니다(다른 언어의 대리인과 유사). 이 예에서는 연결 방법을 보여줍니다.
// This is the function signature:
//
// Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0)
//
// For example:
obj->connect("signal_name_here", callable_mp(this, &MyCustomType::method), CONNECT_DEFERRED);
callable_mp``는 멤버 함수에 대한 사용자 정의 호출 가능 함수 포인터를 생성하는 매크로입니다. ``p_flags 값은 :ref:`ConnectFlags <enum_Object_ConnectFlags>`을 참조하세요.
클래스에 시그널를 추가하는 작업은 _bind_methods``에서 ``ADD_SIGNAL 매크로를 사용하여 수행됩니다. 예:
ADD_SIGNAL(MethodInfo("been_killed"))
형 변환과 캐스트하기
객체는 힙에 할당됩니다. 두 가지 소유권 모델이 있습니다.
``RefCounted``에서 파생된 개체는 참조 계산됩니다.
다른 모든 객체는 수동으로 메모리를 관리합니다.
소유권 모델은 근본적으로 다릅니다. 객체를 생성, 저장 및 해제하는 방법을 알아보려면 각각에 대한 섹션을 참조하세요.
``Object *``를 통해 전달된 객체가 ``RefCounted``인지 여부를 모르고 이를 저장해야 하는 경우 포인터 대신 ``ObjectID``를 저장해야 합니다(아래 설명과 같이 수동 메모리 관리 섹션 참조).
:ref:`Variant<class_Variant>`를 통해 객체가 전달될 때, 특히 지연된 콜백을 사용하는 경우, 함수가 실행될 때 포함된 ``Object *``가 이미 해제되었을 가능성이 있습니다. ``Object *``로 직접 변환하는 대신 ``get_validated_object``를 사용해야 합니다.
void do_something(Variant p_variant) {
Object *object = p_variant.get_validated_object();
ERR_FAIL_NULL(object);
}
메모리 관리
수동으로 메모리 관리되는 개체는 ``memnew``를 사용하여 생성되고 ``memdelete``를 사용하여 해제됩니다.
Node *node = memnew(Node);
// ...
memdelete(node);
node = nullptr;
개체의 유일한 소유자가 아닌 경우 해당 개체에 대한 포인터를 저장하는 것은 위험합니다. 개체는 언제든지 해당 개체에 대한 다른 참조를 통해 해제될 수 있으며 이로 인해 포인터가 매달린 포인터가 되어 결국 충돌이 발생할 수 있습니다.
당신이 유일한 소유자가 아닌 객체를 저장할 때 포인터 대신 ``ObjectID``를 저장해야 합니다.
Node *node = memnew(Node);
ObjectID node_id = node.get_instance_id();
// ...
Object *maybe_node = ObjectDB::get_instance(node_id);
ERR_FAIL_NULL(maybe_node); // The node may have been freed between calls.
메모리 관리
RefCounted 하위 클래스는 `참조 계산 의미론 <https://en.wikipedia.org/wiki/Reference_counting>`__으로 메모리 관리됩니다.
이는 memnew``를 사용하여 구성되며 ``Ref 인스턴스에 저장되어야 합니다. 마지막 Ref 인스턴스가 삭제되면 개체가 자동으로 자체 소멸됩니다.
class MyRefCounted: public RefCounted {
GDCLASS(MyReference, RefCounted);
};
Ref<MyRefCounted> my_ref = memnew(MyRefCounted);
// ...
// Ref holds shared ownership over the object, so the object
// will not be freed. As long as you have a valid, non-null
// Ref, it can be safely assumed the object is still valid.
my_ref->get_class_name();
다른 소유자가 있을 수 있으므로 RefCounted 하위 클래스에 대해 ``memdelete``를 호출하면 안 됩니다.
또한 원시 포인터(예: RefCounted *object = memnew(RefCounted))를 사용하여 RefCounted 하위 클래스를 저장해서는 안 됩니다. 다른 소유자가 개체를 파괴하여 매달린 포인터가 남게 되어 결국 충돌이 발생할 수 있으므로 이는 안전하지 않습니다.
참고자료:
동적 캐스팅
Godot는 객체 파생 클래스 간의 동적 캐스팅을 제공합니다. 예를 들면 다음과 같습니다:
void some_func(Object *p_object) {
Button *button = Object::cast_to<Button>(p_object);
}
캐스트가 실패하면 ``nullptr``가 반환됩니다. 이는 ``dynamic_cast``와 동일하게 작동하지만 `C++ RTTI <https://en.wikipedia.org/wiki/Run-time_type_information>`__을 사용하지 않습니다.
알림(Notification)
Godot의 모든 객체에는 _notification 메서드가 있어 관련될 수 있는 엔진 수준 콜백에 응답할 수 있습니다. 자세한 내용은 알림(Notifications) 페이지에서 확인할 수 있습니다.
리소스
:ref:`Resource <class_resource>`은 RefCounted에서 상속되므로 모든 리소스는 참조 계산됩니다. 리소스에는 선택적으로 디스크의 파일을 참조하는 경로가 포함될 수 있습니다. 이는 일반적으로 리소스 로더에 의해 수행되지만 ``resource.set_path(path)``로 설정할 수 있습니다. 서로 다른 두 리소스는 동일한 경로를 가질 수 없습니다. 그렇게 하면 오류가 발생합니다.
경로가 없는 리소스도 괜찮습니다.
참고자료:
리소스 로딩
다음과 같이 ResourceLoader API를 사용하여 리소스를 로드할 수 있습니다.
Ref<Resource> res = ResourceLoader::load("res://someresource.res")
If a reference to that resource has been loaded previously and is in memory, the ResourceLoader will return that reference. This means that there can be only one resource loaded from a file referenced on disk at the same time.
참고자료:
자원 절약
리소스 절약 API를 사용하여 리소스를 저장할 수 있습니다.
ResourceSaver::save("res://someresource.res", instance)
인스턴스가 저장되고, 파일 경로가 있는 하위 리소스가 해당 리소스에 대한 참조로 저장됩니다. 경로가 없는 하위 리소스는 ``res://someresource.res::1``와 같이 저장된 리소스 및 할당된 하위 ID와 함께 묶입니다. 이는 로드할 때 캐시하는 데도 도움이 됩니다.