I'm a bit confused about the correct use of TypedArrays in gdextension. How can I expose them correctly to gdscript?
~~Consider a class MyClass : RefCounted
.~~
EDIT: Consider a class MyClass : Resource
.
I noticed godot defines a MAKE_TYPED_ARRAY
macro, which it uses to create the TypedArrray
implementations for it's Variants
.
Is it necessary or beneficial to use this for our own types? For example: MAKE_TYPED_ARRAY(MyClass, Variant::OBJECT)
Does the TypedArray<MyClass>
type need to be registered to expose it?
And most of all, why do I still need to cast the bloody values because they seem to be returned as Variant despite the typing of the TypedArray
.
It's all just a little confusing and not a lot of documentation seems to be out there.
See Creating a new array type compatible with `godot::Variant`? for some additional context.
EDIT: after some experimenting it seems like calling MAKE_TYPED_ARRAY(MyClass, Variant::OBJECT)
doesn't seem to make much of a difference either way. At least not from the gdscript user perspective.
The same goes for registering TypedArray<MyClass>
I managed to expose a TypedArray<MyClass>
, and assigning an Array[MyClass]
from gdscript works just fine.
The editor however is a different story.
Consider the following:
//test.h
class TestObject : public Resource {
//...
};
class TypedArrayTest : public Resource {
GDCLASS(TypedArrayTest, Resource)
private:
TypedArray<TestObject> test_array;
protected:
static void _bind_methods();
public:
TypedArray<TestObject> get_test_array() const;
void set_test_array(const TypedArray<TestObject> p_value);
};
//test.cpp
void TypedArrayTest::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_test_array"), &TypedArrayTest::get_test_array);
ClassDB::bind_method(D_METHOD("set_test_array", "p_value"), &TypedArrayTest::set_test_array);
ClassDB::add_property("TypedArrayTest", PropertyInfo(Variant::ARRAY, "test_array"), "set_test_array", "get_test_array");
}
//implementation of get and set
So now we @export this in gdscript.
@export var test:TypedArrayTest
This works, but the array in the property editor shows up like an untyped array.
To assign an element, I first need to select Object
as a type. Next I need to assign New TestObject
instance to that element.
All this for just one element, that's a bit cumbersome.
The editor does check the type though, so that is alright. You can't assign any other type than Object
, or any other instance than TestObject
.
The most annoying part is that the editor shows the whole list of types in every step of this assignment.
Is there any way to limit this?
When you recreate this whole thing in plain gdscript, the editor does recognize @export var gd_test: Array[TestObjectGdsImplemetation]
corretly.
In this case he editor does display the intended behaviour. You can just assign new TestObjectGdsImplemetation
instances in one simple step, without going through all the nonsense I described above.
This leads me to believe it should be possible to achieve the same from GDExtension as well.
godotforums thread (with screenshots): https://godotforums.org/d/38121-how-do-i-properly-configure-typedarrays-in-gdextension
official godot forum thread: https://forum.godotengine.org/t/how-to-set-up-a-typedarray-with-a-custom-type-in-gdextension/37652
I finally figured it out. I went down so many rabbit holes, but of course the solution was simple. As it turns out, in my attempt to write a simple test case, I didn't use the appropriate PropertyHint
.
The following solved it:
void TypedArrayTest::_bind_methods() {
//...
ClassDB::add_property(
"TypedArrayTest",
PropertyInfo(
Variant::ARRAY,
"test_array",
PROPERTY_HINT_TYPE_STRING,
String::num(Variant::OBJECT) + "/" + String::num(PROPERTY_HINT_RESOURCE_TYPE) + ":TestObject"
),
"set_test_array",
"get_test_array"
);
}
It had been staring me in the face this whole time, straight from the Godot docs: https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyhint
Instead of using PROPERTY_HINT_TYPE_STRING
I tried to use PROPERTY_HINT_ARRAY_TYPE
as well, but I couldn't get that to work. Maybe this one only works with built in types, I'm not sure.