Classe Object

Vedi anche

This page describes the C++ implementation of objects in Godot. Looking for the Object class reference? Have a look here.

Definizione generale

Object è la classe base per quasi tutto. La maggior parte delle classi in Godot eredita direttamente o indirettamente da essa. Gli oggetti forniscono riflessione e proprietà modificabili, e dichiararle è questione di utilizzare una singola macro come questa:

class CustomObject : public Object {

    GDCLASS(CustomObject, Object); // this is required to inherit
};

Questo aggiunge molte funzionalità agli Object. Per esempio:

obj = memnew(CustomObject);
print_line("Object class: ", obj->get_class()); // print object class

obj2 = Object::cast_to<OtherClass>(obj); // converting between classes, this also works without RTTI enabled.

Riferimenti:

Registrare un Object

ClassDB è una classe statica che contiene la lista completa delle classi registrate che ereditano da Object, nonché vincoli dinamici a tutti i loro metodi, proprietà e costanti di tipo intero.

Le classi si registrano chiamando:

ClassDB::register_class<MyCustomClass>()

La registrazione consentirà di creare istanze della classe tramite script, codice o di ricrearle alla deserializzazione.

La registrazione come virtuale è la stessa cosa, ma non è possibile istanziare la classe.

ClassDB::register_virtual_class<MyCustomClass>()

Le classi derivate da oggetti possono sovrascrivere la funzione statica static void _bind_methods(). Quando una classe viene registrata, questa funzione statica viene chiamata per registrare tutti i metodi, le proprietà, le costanti, ecc. dell'oggetto. Viene chiamata una sola volta. Se una classe derivata da Object viene istanziata ma non è stata registrata, viene registrata automaticamente come virtuale.

All'interno di _bind_methods, ci sono un paio di cose che si possono fare. Una di queste è registrare funzioni:

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

I valori predefiniti per gli argomenti si possono passare come parametri alla fine:

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

I valori predefiniti si devono fornire nello stesso ordine in cui sono dichiarati, saltando gli argomenti obbligatori e fornendo i valori predefiniti per quelli facoltativi. Questa sintassi corrisponde a quella usata per dichiarare i metodi in C++.

D_METHOD è una macro che converte "methodname" in StringName per una più efficienza. I nomi degli argomenti sono utilizzati per l'introspezione, ma quando si compila un rilascio, la macro li ignora, quindi le stringhe rimangono inutilizzate e vengono rimosse come misura di ottimizzazione.

Dare un occhiata al metodo _bind_methods di Control o Object per più esempi.

If just adding modules and functionality that is not expected to be documented as thoroughly, the D_METHOD() macro can safely be ignored and a string passing the name can be passed for brevity.

Riferimenti:

Costanti

Le classi spesso hanno enumerazioni come:

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.

The constants can also be bound inside _bind_methods, by using:

BIND_CONSTANT(MODE_FIRST);
BIND_CONSTANT(MODE_SECOND);

Proprietà (set/get)

Objects export properties, properties are useful for the following:

  • Serializzare e deserializzare l'oggetto.

  • Creare di una lista di valori modificabili per la classe derivata da Object.

Le proprietà sono solitamente definite dalla classe PropertyInfo() e costruite come:

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

Per esempio:

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

Questa è una proprietà di tipo intero denominata "amount". L'indicazione è un intervallo, che va da 0 a 49 con incrementi di 1 (interi). È utilizzabile solo per l'editor (modificare visivamente il valore), ma non sarà serializzato.

Un altro esempio:

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

Questa è una proprietà stringa, può accettare qualsiasi stringa, ma l'editor accetterà solo quelle definite come indicazione. Poiché non sono stati specificati flag di utilizzo, quelli predefiniti sono PROPERTY_USAGE_STORAGE e PROPERTY_USAGE_EDITOR.

Esistono numerose indicazioni e flag di utilizzo disponibili in object.h, vale la pena dargli un'occhiata.

Le proprietà possono anche funzionare come proprietà di C# ed essere accessibili da script tramite indicizzazione, ma questo utilizzo è generalmente sconsigliato, poiché l'uso di funzioni è preferibile per motivi di leggibilità. Molte proprietà sono inoltre vincolate a categorie, come "animation/frame", che rendono impossibile l'indicizzazione a meno che non si utilizzi l'operatore [].

Da _bind_methods(), le proprietà si possono creare e vincolare purché esistano funzioni set/get. Esempio:

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

In questo modo la proprietà viene creata, utilizzando il setter e il getter.

Vincolare proprietà tramite _set/_get/_get_property_list

Esiste un metodo aggiuntivo per creare proprietà quando si desidera più flessibilità (ad esempio aggiungendo o rimuovendo proprietà in base al contesto).

Le seguenti funzioni si possono sovrascrivere in una classe derivata da Object, NON sono virtuali, NON renderle virtuali, vengono chiamate a ogni sovrascrittura e le quelle precedenti non vengono invalidate (chiamata multilivello).

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

Questa soluzione è anche un po' meno efficiente, poiché p_property deve essere confrontato con i nomi desiderati in ordine seriale.

Dynamic casting

Godot provides dynamic casting between Object-derived classes, for example:

void somefunc(Object *some_obj) {

     Button *button = Object::cast_to<Button>(some_obj);
}

Se il cast fallisce, viene restituito NULL. Questo sistema utilizza RTTI, ma funziona bene (anche se un po' più lentamente) anche quando RTTI è disabilitato. Ciò è utile su piattaforme in cui è ideale una dimensione binaria ridotta, come HTML5 o console (con un basso consumo di memoria).

Segnali

Gli oggetti possono avere una serie di segnali definiti (simili ai delegati in altri linguaggi). Questo esempio mostra come connettersi a essi:

obj->connect(<signal>, target_instance, target_method)
// for example:
obj->connect("enter_tree", this, "_node_entered_tree")

Il metodo _node_entered_tree deve essere registrato nella classe attraverso ClassDB::bind_method (spiegato in precedenza).

L'aggiunta di segnali a una classe è effettuata in _bind_methods, attraverso la macro ADD_SIGNAL, ad esempio:

ADD_SIGNAL(MethodInfo("been_killed"))

Notifiche

Tutti gli oggetti in Godot hanno un metodo _notification che consente di rispondere ai callback a livello di motore che potrebbero riguardarli. Ulteriori informazioni sono disponibili nella pagina Notifiche di Godot.

Riferimenti

RefCounted eredita da Object e contiene un conteggio di riferimenti. È la base per i tipi di oggetto i quali riferimenti sono conteggiati. La loro dichiarazione si deve effettuare attraverso il template Ref<>. Ad esempio:

class MyReference: public RefCounted {
    GDCLASS(MyReference, RefCounted);
};

Ref<MyReference> myref(memnew(MyReference));

myref è conteggiato per riferimento. Verrà liberato quando nessun altro template Ref<> vi punterà.

Riferimenti:

Risorse

Resource eredita da RefCounted, quindi tutte le risorse sono conteggiate per riferimento. Le risorse possono facoltativamente contenere un percorso, che fa riferimento a un file su disco. Questo si può impostare con resource.set_path(path), sebbene ciò sia normalmente effettuato dal caricatore di risorse. Due risorse diverse non possono avere lo stesso percorso; tentare di fare ciò causerà un errore.

Anche le risorse senza un percorso sono permesse.

Riferimenti:

Caricamento delle risorse

Le risorse si possono caricare con l'API di ResourceLoader, in questo modo:

Ref<Resource> res = ResourceLoader::load("res://someresource.res")

Se un riferimento a una risorsa è stato caricato in precedenza ed è presente in memoria, il caricatore di risorse restituirà tale riferimento. Ciò significa che può essere caricata una sola risorsa alla volta da un file referenziato su disco.

  • resourceinteractiveloader (TODO)

Riferimenti:

Salvataggio delle risorse

È possibile salvare una risorsa tramite l'API del salvatore di risorse:

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

L'istanza sarà salvata. Le sotto-risorse che hanno un percorso a un file saranno salvate come riferimento a quella risorsa. Le sotto-risorse senza un percorso saranno raggruppate con la risorsa salvata e vi saranno assegnati dei sub-ID, come res://someresource.res::1. Questo aiuta anche a memorizzarle nella cache quando vengono caricate.

Riferimenti: