Skip to main content

this _get_property_list code running in the Inspector


 

this _validate_property code running in the Inspector


 

this _get_property_list code running in the Inspector

this _validate_property code running in the Inspector

Source Link
Theraot
  • 28.3k
  • 4
  • 55
  • 83

I want to begin by mentioning that Godot 4.4 will have typed dictionaries. And I intend to use them to replace my solution once it is stable.

Suport for enum as key is not yet merged at time of writing (see #100512). Yet, I believe this feature would be just what you want.


You can iterate over the values of the enum (Since you have a named enum, you can access it as dictionary) and output the properties in _get_property_list, and handle them in _get and _set.

This is working code (tested in Godot 4.3):

@tool
extends Node

enum Payload {
    IRON,
    MERCURY,
    SULPHUR,
}

@export_storage var textures:Array[Texture2D] = []

func _get_property_list() -> Array[Dictionary]:
    var result:Array[Dictionary] = []
    result.append(
        {
            name = "Tex",
            type = TYPE_NIL,
            hint_string = "tex_",
            usage = PROPERTY_USAGE_GROUP
        }
    )
    for key:String in Payload:
        result.append(
            {
                name = "tex_" + key,
                type = TYPE_OBJECT, 
                hint = PROPERTY_HINT_RESOURCE_TYPE,
                hint_string = "Texture2D",
                usage = PROPERTY_USAGE_EDITOR
            }
        )

    return result

func _get(property:StringName) -> Variant:
    if not property.begins_with("tex_"):
        return null

    property = property.trim_prefix("tex_")
    if not Payload.has(property):
        return null

    if textures.size() != Payload.size():
        textures.resize(Payload.size())

    var enum_value:int = Payload[property]
    return textures[enum_value]

func _set(property:StringName, value:Variant) -> bool:
    if not property.begins_with("tex_"):
        return false

    property = property.trim_prefix("tex_")
    if not Payload.has(property):
        return false

    if textures.size() != Payload.size():
        textures.resize(Payload.size())

    var enum_value:int = Payload[property]
    textures[enum_value] = value
    return true

Another option is to have for the inspector a propery which type is the enum, and a property which type is a texture, and then using getter and setter, we map to the actual values.

This is working code (tested in Godot 4.3):

@tool
extends Node

enum Payload {
    IRON,
    MERCURY,
    SULPHUR,
}

@export_storage var textures:Array[Texture2D] = []

@export var current:Payload

@export var texture:Texture2D:
    get:
        if textures.size() != Payload.size():
            textures.resize(Payload.size())

        return textures[current]
    set(mod_value):
        if textures.size() != Payload.size():
            textures.resize(Payload.size())

        textures[current] = mod_value

func _validate_property(property: Dictionary) -> void:
    match property.name:
        "current", "texture":
            property.usage = PROPERTY_USAGE_EDITOR

Notes

  • With either of these code snippets adding new enum values does not imply extra effort, since this generates the properties based on the values of the enum.
  • Both code snippets check the size of the array on every access, which is not much trouble, but an alternative is to set the size of the array in _init.
  • Both code snippets assume the enum has consecutive integer keys (which is the default). If it is not the case, you could move from an array to a dictionary to hold the textures.
  • In both code snippets I'm using @export_storage so Godot stores the underlying property. And PROPERTY_USAGE_EDITOR is used to tell Godot to show a property in the inspector but not store it.

And finally, I want to mention that yet another option is to implement a custom property inspector. However, I don't think it is worth the effort for this case.