In GDScript we can export properties. Or, in other words, expose them. With this we can edit their values through specialized editors within the Inspector panel.
Depending on the needs we can create a script derived (extends) from Resource
meant to hold data, making those editable by exposing the relevant properties. In fact I have done something way more elaborate to hold databases, including some relational data (that is, tables that can reference other tables). This can be found in the Database Addon. Yet, sometimes all we want is an asset holding some data. No need for database tables or the likes.
As an example of this "simplified data", in a project I wanted the possibility to change the "skin" of the shown objects, which I called "theme" in the game itself. In order to "describe" each theme I created a resource script containing the required properties, like theme name, list of textures and so on. Then, for each theme, I had to create an instance of this resource.
When attempting to edit properties like that, there is a big limitation: We can't export custom resource types. Besides that, array editing is rather clunky. To solve that an editor plugin is required. And for this I bring the DataAsset plugin. Bellow an array of custom scripted resources is being rearranged:
To start things it's first necessary to have a script derived from DatAsset
. This base class is provided with the addon. In this tutorial we will work on a sample_dasset.gd. One important aspect that must be taken in consideration is the fact that the script must be marked as a tool
one otherwise the plugin will fail to obtain certain required data:
tool
extends DataAsset
class_name SampleDataAsset
Lets add a few basic exported properties just so we get something to work with in the editor:
export var some_string: String = ""
export var some_float: float = 1.0
export(int, FLAGS, "physical", "poison", "fire", "cold", "lightning") var some_flags: int = 0
export var some_resource: Resource = null
export var some_texture: Texture = null
export var some_array: Array = []
Next we need a file resource, of the SampleDataAsset
type. To do this, right click the appropriate directory within the FileSystem panel then New Resource.... In the window that pops up, select SampleDataAsset
:
As soon as the new asset is created it should already open the new editor:
As you can see, this asset editor is docked at the bottom part of the window, sharing space with the Output. If you scroll and/or expand the (DataAsset) editor panel you will probably notice a few things related to this specific script:
some_float
property? The usual export way of working with ranges in numerical types will not work with this plugin.some_resource
property will popup a huge list of possible resources that can be added here.some_array
property, telling that we must implement the get_property_info()
function.The base DataAsset
class contains a get_property_info()
function that will be called by the plugin when populating the window with the property editors. In our derived data asset we implement this function in order to provide the plugin with the information that it needs to properly fill the editor.
The mentioned function must return a Dictionary
. Each key in it should be the name of the corresponding property. The value associated with that key must be another Dictionary
, containing the relevant fields. I will exemplify the 3 aspects that I have highlighted. After that I will provide tables with the complete list of options that can be used, by property type.
So, lets begin with the some_float
. First lets define that we don't want values to be negative but we can set to any other positive value. For that we tell the plugin to use the range_min
option and set it to be 0.0
:
func get_property_info() -> Dictionary:
return {
"some_float": {
"range_min": 0.0,
},
}
Once the script is updated, reload the asset (by double clicking its file within the FileSystem panel). Now attempting to assign a value smaller than 0 will not work. At this point the value editor still shows the "spin buttons" to increase/decrease the value. If we also set a range_max
then those buttons will be removed and a slider will be shown, because then we obtain a well defined range. So, lets do it and set 1.0 to be the maximum value for this property:
func get_property_info() -> Dictionary:
return {
"some_float": {
"range_min": 0.0,
"range_max": 1.0,
},
}
And so, this specific property editor should now display a slider rather than the spin buttons:
Lets now move to the some_resource
property. In order to filter out the list of possible New instance of... options we have to specify the base type of the desired resource. This is done by providing a type
option within the property's dictionary. In it we must provide the path to the script that implements the resource. So, lets create a simple sample resource type to be used. Note that a class_name must be provided otherwise the plugin will fail to gather the required information. That said, a sample_resource:
extends Resource
class_name SampleScriptedResource
export var some_color: Color = Color(1, 1, 1, 1)
export var some_vec3: Vector3 = Vector3(0, 1, 0)
export(String, FILE, "*.png, *.jpg") var some_image_file: String = ""
Now, back to the sample_dasset.gd, we specify that we want the some_resource
property to be of the type defined in the sample_resource.gd file:
func get_property_info() -> Dictionary:
return {
"some_float": {
"range_min": 0.0,
"range_max": 1.0,
},
"some_resource": {
"type": "res://shared/scripts/sample_resource.gd",
},
}
Once the script is saved and the resource reloaded, the New instance of... popup menu should be filtered to show onlySampleScriptedResource
and any other that extends it:
Once the new instance is created, it should look like this:
Finally we need to specify the type of entries that we would like to be added into the some_array
property. Again we use the type
option. But this time we can specify:
type
most of the TYPE_*
constants.To continue with the example we will simply specify the same sample_resource.gd mostly to see how things will look:
func get_property_info() -> Dictionary:
return {
"some_float": {
"range_min": 0.0,
"range_max": 1.0,
},
"some_resource": {
"type": "res://shared/scripts/sample_resource.gd",
},
"some_array": {
"type": "res://shared/scripts/sample_resource.gd",
},
}
Reloading the resource now should change to the proper array editor. There will be a big "Append" button. Clicking it will popup a list of resource types that are derived from the specified type, including the base one. Note that you can even append null
into the array when the desired type is a Resource
(which is the case for the example):
After appending a few instances of the SampleScriptedResource
and editing the some_color
property of each instance, the array editor looks something like the animation bellow. In it I'm also showcasing the fact that we can re-arrange array elements by simply dragging them:
OK, that's it for the example! The tables bellow lists all options that can be used in the return value of the get_property_info()
function. Each table is for a given property type. Note that the default value will be used in case the option is not provided. And if there is no "default" listed then that "option" is actually required.
Name | Default | Description |
---|---|---|
label | "On" | A string determining the label that will be set within the checkbox. |
Name | Default | Description |
---|---|---|
range_min | 0 | Determines the minimum value that can be assigned to the property. |
range_max | 100 | Determines the maximum value that can be assigned to the property. |
step | 1 | The "delta" applied to the value when clicking the spin buttons or moving the slider. |
Name | Default | Description |
---|---|---|
range_min | 0.0 | Determines the minimum value that can be assigned to the property. |
range_max | 1.0 | Determines the maximum value that can be assigned to the property. |
step | 0.1 | The "delta" applied to the value when clicking the spin buttons or moving the slider. |
Name | Default | Description |
---|---|---|
type | - | The full path to a script defining a resource type. |
allow_base | true | If this is false then the popup menu with instance options will not display the specified base class, only derived ones. |
Name | Default | Description |
---|---|---|
type | - | Either an integer as a subset of TYPE_* , a string with the name of a core resource or the full path to the script defining a custom resource type. |
Before wrapping up this "tutorial" there are some minor things that I want to mention:
Texture
properties.DataAsset
is indeed a script, you can pretty much add any other desired code into it.