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.

Classe Object

Voir aussi

Cette page décrit l'implémentation C++ des objets dans Godot. Vous cherchez la référence de la classe Object ? Jetez un oeil ici.

Définition générale

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

Références :

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.

A l'intérieur de bind_methods, il y a deux ou trois choses qui peuvent être faites. La déclaration des fonctions en est une :

ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method);

Default values for arguments can be passed as parameters at the end:

ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method, DEFVAL(-1), DEFVAL(-2)); // Default values for arg2name (-1) and arg3name (-2).

Default values must be provided in the same order as they are declared, skipping required arguments and then providing default values for the optional ones. This matches the syntax for declaring methods in C++.

D_METHOD est une macro qui convertit le "methodname" en un StringName pour plus d'efficacité. Les noms d'arguments sont utilisés pour l'introspection, mais lors de la compilation de version release, la macro les ignore, de sorte que les chaînes de caractères(string) sont inutilisées et optimisées(optimized away).

Consultez _bind_methods de Control ou Object pour plus d'exemples.

Si l'on se contente d'ajouter des modules et des fonctionnalités qui ne sont pas censés être documentés de manière aussi complète, la macro D_METHOD() peut être ignorée sans risque et une chaîne de caractère passant le nom peut être passée pour plus de brièveté.

Références :

Constantes

Les classes ont souvent des énumérations telles que :

enum SomeMode {
   MODE_FIRST,
   MODE_SECOND
};

For these to work when binding to methods, the enum must be declared convertible to int. A macro is provided to help with this:

VARIANT_ENUM_CAST(MyClass::SomeMode); // now functions that take SomeMode can be bound.

Les constantes peuvent également être liées à l'intérieur de bind_methods, en utilisant :

BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);

Propriétés (set/get)

Les objets exportent des propriétés, les propriétés sont utiles pour :

  • Sérialiser et dé-sérialiser l’objet.

  • Création d'une liste de valeurs modifiables pour la classe dérivée de l'objet.

Properties are usually defined by the PropertyInfo() class and constructed as:

PropertyInfo(type, name, hint, hint_string, usage_flags)

Par exemple :

PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "0,49,1", PROPERTY_USAGE_EDITOR)

This is an integer property named "amount". The hint is a range, and the range goes from 0 to 49 in steps of 1 (integers). It is only usable for the editor (editing the value visually) but won't be serialized.

Un autre exemple :

PropertyInfo(Variant::STRING, "modes", PROPERTY_HINT_ENUM, "Enabled,Disabled,Turbo")

Il s'agit d'une propriété de type chaîne de caractère(string), qui peut prendre n'importe quelle chaîne de caractère mais l'éditeur n'autorisera que ceux définis avec un indice. Comme aucun drapeau d'utilisation n'a été spécifié, les drapeaus par défaut sont PROPERTY_USAGE_STORAGE et PROPERTY_USAGE_EDITOR.

Il existe de nombreux conseils et indicateurs d'utilisation disponibles dans object.h, vérifiez-les.

Les propriétés peuvent également fonctionner comme des propriétés C # et être accessibles à partir d'un script en, utilisant une indexation, mais cette utilisation est généralement déconseillée, car l'utilisation de fonctions est à privilégier pour la lisibilité. De nombreuses propriétés sont également liées à des catégories, telles que "animation/frame" qui rendent également l'indexation impossible à moins d'utiliser l'opérateur [].

A partir de _bind_methods(), les propriétés peuvent être créées et liées tant que les fonctions set/get existent. Exemple :

ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount")

Cela crée la propriété en utilisant le setter et le getter.

Liaison (Binding) des propriétés à l'aide de _set/_get/_get_property_list

Il existe une méthode de création de propriétés supplémentaire lorsque plus de flexibilité est souhaitée (par exemple, l'ajout ou la suppression de propriétés dans un contexte donné).

Les fonctions suivantes peuvent être surchargée dans une classe dérivée, elles ne sont PAS virtuelles, NE LES RENDEZ PAS virtuelles, elles sont appelées à chaque surcharge et les précédentes ne sont pas invalidées (appel à plusieurs niveaux).

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

C'est aussi un peu moins efficace car p_property doit être comparé aux noms souhaités dans l'ordre de la série.

Signaux

Objects can have a set of signals defined (similar to Delegates in other languages). This example shows how to connect to them:

// 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.

L'ajout des signaux à la classe se fait dans _bind_methods, en utilisant la macro ADD_SIGNAL , pour exemple :

ADD_SIGNAL(MethodInfo("been_killed"))

Object ownership and casting

Objects are allocated on the heap. There are two different ownership models:

  • Objects derived from RefCounted are 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.

Références :

Casting dynamique

Godot fournit un casting dynamique entre les classes dérivées d’objets, par exemple :

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.

Notifications

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 Notifications Godot page.

Ressources

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.

Une ressource sans chemin est également correcte.

Références :

Chargement des ressources

Les ressources peuvent être chargées en utilisant l'API ResourceLoader, comme ceci :

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.

Références :

Enregistrement de ressources

L'enregistrement d'une ressource peut être effectué avec l'API de sauvegarde de ressources :

ResourceSaver::save("res://someresource.res", instance)

The instance will be saved, and sub resources that have a path to a file will be saved as a reference to that resource. Sub resources without a path will be bundled with the saved resource and assigned sub-IDs, like res://someresource.res::1. This also helps to cache them when loaded.

Références :