[GDExtension] Adding array of resources as properties

Godot Version

4.2

Question

Hey guys, new here!
I’m having trouble understanding how am i supposed to expose an array of resources (or for what matters an array of Ref wrapped RefCounted instances) in GDExtension.

Here is an (non-working) example of how I would do it:

namespace godot {

class ImageCollection : public Resource {
    GDCLASS(ImageCollection, Resource)

private:
    Vector<Ref<Image>> m_images;

public:
    void set_images(const Vector<Ref<Image>>& p_images)
    {
        m_images = p_images;
    }

    Vector<Ref<Image>> get_images() const
    {
        return m_images;
    }

protected:
    static void _bind_methods()
    {
        ClassDB::bind_method(D_METHOD("set_images", "images"), &ImageCollection::set_images);
        ClassDB::bind_method(D_METHOD("get_images"), &ImageCollection::get_images);
        ClassDB::add_property("ImageCollection",
            PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_ARRAY_TYPE, "Image"), //
            "set_images", //
            "get_images"); //
    }
};

}

I tried to use both TypedArray<Ref> and Vector<Ref> but the compiler didn’t like any of them. I also tried TypedArray and Vector to no avail.

Any suggestions on what I’m doing wrong?

Thank you! :smile:

Hey man I’m struggle-bussing my way through some GDExtension stuff too - this thread here has lots of good info, maybe it would help you

https://forum.godotengine.org/t/returning-custom-classes-from-c-gdextension/65920

also the Terrain3D project is up on github and a great example of a GDExtension that has a resource it defines that uses an array of images (or textures, anyways). Maybe check that out too.

So… I’ve been experimenting a bit and i finally figured it out.
Here is how to expose and array of Ref<WhateverRefCounted> as a property of a node/resource.

namespace godot {

MAKE_TYPED_ARRAY(Ref<Image>, Variant::OBJECT)
MAKE_TYPED_ARRAY_INFO(Ref<Image>, Variant::OBJECT)

class ImageCollection : public Resource {

    GDCLASS(ImageCollection, Resource)

private:
    TypedArray<Ref<Image>> m_images;

public:
    void set_images(TypedArray<Ref<Image>> p_images)
    {
        m_images = p_images;
    }

    TypedArray<Ref<Image>> get_images() const
    {
        return m_images;
    }

protected:
    static void _bind_methods()
    {
        ClassDB::bind_method(D_METHOD("set_images", "images"), &ImageCollection::set_images);
        ClassDB::bind_method(D_METHOD("get_images"), &ImageCollection::get_images);
        ADD_PROPERTY(
			PropertyInfo(
				Variant::ARRAY,
				"images",
				PROPERTY_HINT_TYPE_STRING,
                String::num(Variant::OBJECT) + "/" + String::num(PROPERTY_HINT_RESOURCE_TYPE) + "Image"
			),
			"set_images",
			"get_images"
		);
    }
};

}
  1. Use the MAKE_TYPED_ARRAY and MAKE_TYPED_ARRAY_INFO macros with your Ref<Whatever> as first parameter and Variant::OBJECT for the second parameter. To avoid re-declarations I suggest doing this in a separate file since this macros declare new types.
  2. Use TypedArrays (not Vector like i did in my first example).
  3. Do not pass values to the setters by reference, only by value.
  4. When binding your property, in the PropertyInfo use PROPERTY_HINT_TYPE_STRING as property hint and for the hint string pass String::num(Variant::OBJECT) + "/" + String::num(PROPERTY_HINT_RESOURCE_TYPE) + "Image" with the intended resource type instead of “Image” (source: this post)

I tested this a lil bit and it seems to function as it should, I hope your PC doesn’t explode because of me.
I hope to have been helpfull.