Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resource Inheritance & Partial Resources #7887

Closed
DaloLorn opened this issue Sep 27, 2023 · 1 comment
Closed

Resource Inheritance & Partial Resources #7887

DaloLorn opened this issue Sep 27, 2023 · 1 comment

Comments

@DaloLorn
Copy link

Describe the project you are working on

A 2D tactical game with a large number of unit types and variants, where two units of the same type may have different stats for story or balancing reasons. (For instance, ships owned by a different faction may have 20% more health or something.) It is also intended to have modding support if anyhow possible.

Describe the problem or limitation you are having in your project

I've been trying to come up with a decent way for ship types to inherit certain stats from each other, reducing duplication and making them easier to update, whether in the Godot inspector or a random text editor.

However, I've defined them as a custom subclass of Resource, and a Resource cannot know whether it was copied from another Resource, nor retain a reference to its parent. In fact, when dealing with custom resources, ResourceSaver/ResourceLoader doesn't even seem to recognize whether a field has been set to its default value, bloating the exported resources with boilerplate data.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

  1. Ideally stop serializing default values for custom resources, or add a project setting to control whether it happens.
  2. Add a new field to the resource structure as observed in the .tres and .tscn file formats, analogous to the instance field observed in the node structure. Like instanced nodes, overridden resources would default to all the values held in their ancestor, but offer the ability to override any given value on a case-by-case basis.

This way, different resource "families" can have different default values without needing a subclass to explicitly redefine and shadow the original variables those defaults apply to. (If that's even legal - I haven't checked - it would make for frighteningly fragile and needlessly verbose code.)

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Assuming a scene with a root node of type ShipControl, inheriting from Control and exposing fields health and armor, we could get a node looking something like this:

script = ExtResource("scriptId")
health = 50
armor = 50

Instantiating that ship, moving it around a bit, and reducing its health, could then look something like this (never mind that it would probably not be a root node in reality!):

health = 20
offset_left = 48
offset_bottom = 48

I propose the same structure for gd_resource (and its constituent resource), and for sub_resource:

health = 25
armor = 40

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

I can think of a multitude of workarounds, but none I would consider trivial:

  • Writing a custom (de)serialization protocol for resources of this type. I think I've seen enough parsers in my lifetime, and I've hacked a unit type loader in Starflare to achieve the results I'm hoping to see here (and better!), so I'm reasonably confident I could write one in GDScript... but it's still a decent bit of work. What's more, I'm considerably less confident that the editor would support my hackery without needing a plugin, which is a whole other rabbit hole.

  • My current workaround uses accessor trickery on the consuming objects to fake overrides. For instance:

    @export var maxHP: int: 
          get: return maxHP if maxHP else baseType.maxHP
  • Using a bunch of instanced nodes (or subclasses of UnitType) to define defaults. I'm actually sort-of doing the former already, because at one point I end up needing to hold a reference to a Unit or a UnitType, so I instantiate an unmodified Unit for every UnitType. It's not quite as bad, though, because I'm not making a new scene for every UnitType...

  • It now occurs to me I could try making a factory, though this will be painful in light of the fact that nested classes cannot be used as resources. Depending on how I go about it, I might need to make 20 new script files just to support that factory. 😬

Is there a reason why this should be core and not an add-on in the asset library?

I can't imagine being the only person who tries to construct nodes on-the-fly from a set of resources. Using nodes directly is viable, but if you want to support modding, it's somewhat easier for a random nobody to understand and edit a resource than a packed scene.

@Calinou
Copy link
Member

Calinou commented Sep 27, 2023

Thanks for the proposal! Consolidating in #4089.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants