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...
Die Klasse "Object"
Siehe auch
Diese Seite beschreibt die C++-Implementierung von Objects in Godot. Suchen Sie nach der Object-Klassenreferenz? Have a look here.
Allgemeine Definition
Object is the base class for almost everything. Most classes in Godot inherit directly or indirectly from it. Declaring them is a matter of using a single macro like this:
class CustomObject : public Object {
GDCLASS(CustomObject, Object); // This is required to inherit from Object.
};
Objects come with a lot of built-in functionality, like reflection and editable properties:
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
Referenzen:
Registering Object classes
Most Object subclasses are registered by calling GDREGISTER_CLASS.
GDREGISTER_CLASS(MyCustomClass)
This will register it as a named, public class in the ClassDB, which will allow the class to be instantiated by
scripts, code, or by deserialization. Note that classes registered as GDREGISTER_CLASS should expect to be
instantiated or freed automatically, for example by the editor or the documentation system.
Besides GDREGISTER_CLASS, there are a few other modes of privateness:
// 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);
It is also possible to use GDSOFTCLASS(MyCustomClass, SuperClass) instead of GDCLASS(MyCustomClass, SuperClass).
Classes defined this way are not registered in the ClassDB at all. This is sometimes used for platform-specific
subclasses.
Registering bindings
Object-derived classes can override the static function
static void _bind_methods(). When the class is registered, this
static function is called to register all the object methods,
properties, constants, etc. It's only called once.
In _bind_methods gibt es ein paar Dinge, die getan werden können. Das Registrieren von Funktionen ist eines davon:
ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method);
Default-Werte für Argumente können als Parameter am Ende übergeben werden:
ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method, DEFVAL(-1), DEFVAL(-2)); // Default values for arg2name (-1) and arg3name (-2).
Defaultwerte müssen in der gleichen Reihenfolge angegeben werden, in der sie deklariert werden, wobei erforderliche Argumente übersprungen und dann Defaultwerte für die optionalen Argumente angegeben werden. Dies entspricht der Syntax für die Deklaration von Methoden in C++.
D_METHOD ist ein Makro, das "methodname" zur Steigerung der Effizienz in einen StringName konvertiert. Argumentnamen werden zur Selbstbeobachtung verwendet, aber beim Kompilieren beim Release werden sie vom Makro ignoriert, sodass die Strings nicht verwendet und nicht optimiert werden.
Weitere Beispiele finden Sie unter _bind_methods von Control oder Object.
Wenn nur Module und Funktionen hinzugefügt werden, von denen nicht erwartet wird, dass sie so gründlich dokumentiert werden, kann das Makro D_METHOD() ignoriert und einen String, der den Namen übergibt, kann der Kürze halber übergeben werden.
Referenzen:
Konstanten
Klassen haben oft Enums wie:
enum SomeMode {
MODE_FIRST,
MODE_SECOND
};
Damit diese beim Binding an Methoden funktionieren, muss die Aufzählung als nach int konvertierbar deklariert werden. Dazu wird ein Makro bereitgestellt:
VARIANT_ENUM_CAST(MyClass::SomeMode); // now functions that take SomeMode can be bound.
Die Konstanten können auch innerhalb von _bind_methods gebunden werden, indem Sie Folgendes verwenden:
BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);
Propertys (set/get)
Objects exportieren Propertys, Propertys sind für Folgendes nützlich:
Serialisierung und Deserialisierung des Objects.
Erstellen einer Liste editierbarer Werte für die vom Object abgeleitete Klasse.
Propertys werden normalerweise durch die Klasse PropertyInfo() definiert und konstruiert als:
PropertyInfo(type, name, hint, hint_string, usage_flags)
Zum Beispiel:
PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "0,49,1", PROPERTY_USAGE_EDITOR)
Dies ist eine Integer-Property mit dem Namen "amount". Die Variable "hint" ist eine Range und die Range reicht von 0 bis 49 in Schritten von 1 (Integer). Sie kann nur für den Editor verwendet werden (Wert visuell bearbeiten), wird jedoch nicht serialisiert.
Ein weiteres Beispiel:
PropertyInfo(Variant::STRING, "modes", PROPERTY_HINT_ENUM, "Enabled,Disabled,Turbo")
Dies ist eine String-Property, die jede beliebigen String annehmen kann, aber der Editor wird nur die definierten Hints-Strings zulassen. Da keine Verwendungsflags angegeben wurden, sind die Default-Flags PROPERTY_USAGE_STORAGE und PROPERTY_USAGE_EDITOR.
In object.h sind viele Hints und Verwendungsflags verfügbar, schauen Sie mal rein.
Propertys können auch wie C#-Propertys funktionieren und von einem Skript aus über Indizierung aufgerufen werden, aber von dieser Verwendung wird im Allgemeinen abgeraten, da die Verwendung von Funktionen aus Gründen der Lesbarkeit vorzuziehen ist. Viele Eigenschaften sind auch an Kategorien gebunden, wie z.B. "Animation/Frame", was eine Indizierung ebenfalls unmöglich macht, es sei denn, man verwendet den Operator [].
Aus _bind_methods() können Propertys erstellt und gebunden werden, solange set/get-Funktionen vorhanden sind. Beispiel:
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount")
Dadurch wird die Property mit Hilfe des Setters und des Getters erstellt.
Binding von Propertys mit _set/_get/_get_property_list
Ein zusätzliches Verfahren zum Erstellen von Propertys besteht, wenn mehr Flexibilität gewünscht wird (d.h. Hinzufügen oder Entfernen von Propertys im Kontext).
Die folgenden Funktionen können in einer von Object abgeleiteten Klasse überschrieben werden. Sie sind NICHT virtuell. Machen Sie sie NICHT virtuell. Sie werden bei jeder Überschreibung aufgerufen und die vorherigen Funktionen werden nicht ungültig gemacht (mehrstufiger Aufruf).
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
Dies ist auch etwas weniger effizient, da p_property mit den gewünschten Namen in serieller Reihenfolge verglichen werden muss.
Signale
Für Objects kann eine Reihe von Signalen definiert werden (ähnlich wie bei Delegates in anderen Sprachen). Dieses Beispiel zeigt, wie man sich mit ihnen verbindet:
// 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 is a macro to create a custom callable function pointer to member functions.
For the values of p_flags, see ConnectFlags.
Das Hinzufügen von Signalen zu einer Klasse erfolgt in _bind_methods, z.B. mit dem Makro ADD_SIGNAL:
ADD_SIGNAL(MethodInfo("been_killed"))
Object ownership and casting
Objects are allocated on the heap. There are two different ownership models:
Objects derived from
RefCountedare reference counted.All other objects are manually memory managed.
The ownership models are fundamentally different. Refer to the section for each respectively to learn how to create, store, and free the object.
When you do not know whether an object passed to you (via Object *) is RefCounted, and you need to store it,
you should store its ObjectID rather than a pointer (as explained below, in the manual memory management section).
When an object is passed to you via Variant, especially when using deferred callbacks, it is
possible that the contained Object * was already freed by the time your function runs.
Instead of converting directly to Object *, you should use get_validated_object:
void do_something(Variant p_variant) {
Object *object = p_variant.get_validated_object();
ERR_FAIL_NULL(object);
}
Manual memory management
Manually memory managed objects are created using memnew and freed using memdelete:
Node *node = memnew(Node);
// ...
memdelete(node);
node = nullptr;
When you are not the sole owner of an object, storing a pointer to it is dangerous: The object may at any point be freed through other references to it, causing your pointer to become a dangling pointer, which will eventually result in a crash.
When storing objects you are not the only owner of, you should store its ObjectID rather than a pointer:
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 memory management
RefCounted subclasses are memory managed with reference counting semantics.
They are constructed using memnew, and should be stored in Ref instances. When the last Ref instance is
dropped, the object automatically self-destructs.
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();
You should never call memdelete for RefCounted subclasses, because there may be other owners of it.
You should also never store RefCounted subclasses using raw pointers, for example
RefCounted *object = memnew(RefCounted). This is unsafe because other owners may destruct the object, leaving you
with a dangling pointer, which will eventually result in a crash.
Referenzen:
Dynamisches Casting
Godot bietet zum Beispiel dynamisches Casting zwischen von Object abgeleiteten Klassen an:
void some_func(Object *p_object) {
Button *button = Object::cast_to<Button>(p_object);
}
If the cast fails, nullptr is returned. This works the same as dynamic_cast, but does not use
C++ RTTI.
Benachrichtigungen
All objects in Godot have a _notification method that allows them to respond to engine-level callbacks that may relate to it. More information can be found on the Benachrichtigungen in Godot page.
Ressourcen
Resource inherits from RefCounted, so all resources
are reference counted. Resources can optionally contain a path, which
reference a file on disk. This can be set with resource.set_path(path),
though this is normally done by the resource loader. No two different
resources can have the same path; attempting to do so will result in an error.
Ressourcen ohne Pfad sind ebenfalls in Ordnung.
Referenzen:
Ressourcen laden
Ressourcen können mit der ResourceLoader-API wie folgt geladen werden:
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.
Referenzen:
Ressourcen speichern
Das Speichern einer Ressource kann mit der Resource Saver-API erfolgen:
ResourceSaver::save("res://someresource.res", instance)
Die Instanz wird gespeichert und Unterressourcen, die einen Pfad zu einer Datei haben, werden als Verweis auf diese Ressource gespeichert. Unterressourcen ohne Pfad werden mit der gespeicherten Ressource gebündelt und mit Sub-IDs versehen, wie res://someresource.res::1. Dies hilft auch, sie beim Laden zu cachen.