Skip to content

Add first-class custom resource support #18

@ghost

Description

Describe the project you are working on:
Data-heavy RPG

Describe how this feature / enhancement will help your project:
I have a lot of different structures in my game which represent various different things, such as healing items, equipment, party members, item/character stats, enemies, and so on. To make it easy for designers/modders to add and change this data, these are represented through extending Resource, which, combined with class_name, allows you to easily create .tres files containing exported variables that can be assigned to in the inspector. So to modify data, one can double-click some file in the file system, glance over to the inspector, and easily change properties this way, leveraging all the various options associated with export.

image

This works, but only up to a point. Let's say, I now want all of my party members, equipment, and enemies to have a set of stats associated with them. I can write a stats class like so:

extends Resource
class_name Stats

export var strength: float
export var defense: float
...more stats

And now I can just do the following in all the associated scripts, and get to modify that data as I would with any other resource:

export(Stats) var stats = Stats.new()

...except, unfortunately, that doesn't work, as Godot will throw an error about not recognizing Stats as a Resource, though we see that Stats, due to extending Resource, should count as one. This works with any resource the engine ships with:

export(Texture) var tex
export(AudioStream) var stream
export(Shape2D) var shape

And you even get a little menu to create a new resource:

image

But, sadly, the engine rejects Stats as being something entirely different. If there was support for this kind of resource, we should be able to see a menu something like this, after the menu to create a new Stats resource:

image

Which would alleviate all the work-arounds outlined below.

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:

export(Stats) var stats
or
export var stats: Stats

stats_mock

If this enhancement will not be used often, can it be worked around with a few lines of script?:

In short: no, just the opposite, it requires a lot more code to be written to work around this inhibition, or requires a tedious process to add a custom resource to the inspector.

The fundamental issue is the current inability for the user to simply export a resource of their own creation. We are left with the following options:

Brute Force

#unnamed and unstructured, no guarantees of anything
export(Array, float) var stats
export(Array, Array, String) var combat_messages
#this one's especially painful, where you need multiple types in one array
export(Array) var mixed_data
#...but nothing compares to this, besides the dictionary variation
export(Array, Array) var lots_of_mixed_data

#named but unstructured, and you have to name them for every dictionary added in the latter example, no autocomplete
export(Dictionary) var stats := {"strength" : 0.0, "defense" : 0.0, ...}
export(Array, Dictionary) var god_help_you_if_you_do_this

The OmniResource

#named, structured, autocompleted (if you cast)
#necessitates a process where you create the custom resource then copy it over (after scrolling through the ginormous list of resources this gives you), or save it to the system and drag and drop
export(Resource) var not_as_it_seems

image

Do Repeat Yourself

#player_character.gd
export var strength: float
export var defense: float
...

#enemy.gd
export var strength: float
export var defense: float
...

#equipment.gd
export var start_strength: float
export var start_defense: float
...
export var upgraded_strength: float
export var upgraded_defense: float
...

Forget About It!

#my_character.gd
func get_character() -> PlayerCharacter:
    var player_character = PlayerCharacter.new()
    player_character.stats = Stats.new()
    player_character.stats.strength = 7
    ...
    return player_character

#character_defs.gd
func get_characters() -> Array:
   ...

#loader.gd
func load_character(path_to_json) -> PlayerCharacter:
    ...turning json back into an object logic...

There's the opportunity to build an inspector plugin or use _get_property_list(), both of which require quite a bit of code and technical know-how, on a per-resource basis. Based on what people have told me in the discord, it's a painful process (at least with property list) which they seek to avoid in the future. We could also go into C++ land and build modules for the engine to understand our data types, but again, it's a lot of code, and beyond the skill level of many of us who haven't delved into the engine code (assuming we know C++ at all); even if it were easily understood by anyone, or you know what you're doing, it's still many magnitudes more work than export var my_res: MyRes working out of the box.

It takes a lot of code, or unsafe code, or bad programming practices, or a tiresome process to make up for functionality that Godot already provides, leaving many of us to reinvent the wheel time and time again if we want this functionality. If exporting a custom resource Just Worked, lots of time, energy and headaches would be saved.

Is there a reason why this should be core and not an add-on in the asset library?:
I don't believe first-class support for custom resources would constitute as bloat. I believe it's an essential but missing feature for all games which require any kind of user-created resource type, for anyone who needs to export a bundle of named data with type safety, for anyone who wants to associate their own resource type with their own node type, for anyone who needs complex but easily modified data saved to disk; since virtually every game made with Godot makes use of Resources, every developer would benefit from this, if not by a little, by a lot.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions