godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.53k stars 21.26k forks source link

Object metadata. What to do with it? #18591

Closed reduz closed 5 years ago

reduz commented 6 years ago

As you guys probably know, all objects in Godot can have metadata. This is accessed from code as:

obj.set_meta("key",value)
obj.has_meta("key")
obj.get_meta("key")

This metadata is even serialized, but currently it's usage by the community is rather unclear to us developers. In the editor itself, It's mostly unused. There remain a few cases where the 2D editor puts metadata into nodes (for locking and marking as bones) but this could be moved away.

Metadata can't be edited by users from the UI. This would be doable, but then editor should not store data in there anymore. This is also easy to do.

I'm also not sure if anyone uses these functions from code in games.

So, we were discussing with other devs, what should we do with it? I see three paths for this:

1) Move the remaining usages of it away from it and deprecate the feature 2) Move the remaining usages of it away from it, but keep the feature AND make metadata available for editing in inspector 3) Move the remaining usages away from it and do nothing.

Feedback very welcome on this!

vnen commented 6 years ago

Has anyone here mentioned any usage of metadata that can't be accomplished with script vars?

Of course, script variables are powerful enough to do anything metadata can do, but I don't think that's the point. Adding scripts to anything you want metadata can be quite cumbersome.

You have essentially two options:

  1. Adding script files to your project and load them when you need metadata and loading them into the objects.
  2. Make scripts in runtime to attach them to the objects.

Both options are worse than metadata in terms of usability and performance. Technically you can make a single script that mimics the metadata functionality, but how is that any better? If it's "monkey patching", it's not different doing it with scripts or in the engine.

So the question is not whether it can replaced by scripting, but whether the use of metadata in itself is a bad practice. Which I don't think it is (this thread has some legitimate cases where it couldn't be easily replaced). Sometimes you only need extra data, not different behavior, and for that a script might be too much.

mwerezak commented 6 years ago

Built in scripts make (1) pretty easy, so I don't see a usability advantage. Probably whatever is gained by not having to create a built in script is outweighed by having to type get_meta/set_meta and enclosing the field name with quotes instead of accessing a var.

I don't know enough to comment on performance, so fair enough on that point.

supagu commented 6 years ago

I use this API in my game to store some additional info, particularly useful in UI stuff where I want to associate some game data to a UI element without having to write my own UI element. It's cleaner than mapping from a UI element to a dictionary of game data.

aaronfranke commented 6 years ago

I'd like to add that I currently use metadata to transfer information between C# and GDScript scripts. Unfortunately get_meta crashes the game if there is no metadata defined yet, so I would find it very useful if I could set some default values in the inspector.

Zylann commented 6 years ago

@aaronfranke use has_meta()?

aaronfranke commented 6 years ago

I would prefer default values over checks, but yes that is a solution for now.

Zylann commented 6 years ago

@aaronfranke oh right you just meant set meta from inspector... I was confused by "default"

sxkod commented 6 years ago

I am hoping for 2. Use case- an exploration game where I can drop a whole bunch of artifacts and attach meta to them from inspector - such as firstaid_box value, bomb strength, scroll_secret etc and the player can query the artifact and get info. With scripts I will need a script for each or some other way of keeping tab rather than dropping the artifacts and editing their values in the UI.

Thanks for all you devs do :-)

ArdaE commented 6 years ago

I'd prefer it to be kept, i.e. 3 or 2.

Other frameworks have similar features (e.g. the attached properties, and to a lesser degree, the closely related dependency properties of XAML/WPF/UWP) to solve various issues including storage of sparsely populated data efficiently and preventing unwanted design coupling and code bloat (https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/attached-properties-overview).

For me, the metadata feature of Godot has solved a different problem in one project that would have otherwise required (a) a separately managed dictionary (not ideal as it requires central knowledge outside of object instances that would need to be kept in sync with the objects as objects are created and destroyed), (b) a very convoluted design where I would have to create an extra layer of node in the scene tree for each built-in type, as a parent to it, just to insert custom information into built-in node types, (c) creating a new custom type for each built-in type that I want to add my custom data to, causing my data definition to be repeated in multiple places, or (d) introducing a custom non-visible entry into the scene tree, as a child to each built-in type instance, to store my custom data. These are all undesirable solutions, and Godot's existing metadata feature enables a very clean, straightforward design that I was able to implement in two lines of code. Having said that my case is a very advanced one (related to live manipulation and reloading of scripts within a framework, not a game, where I can't rely on custom data defined within the nodes' scripts, and hence metadata stored by a common parent node comes in very handy). What I am doing is not something that the average godot user would or should do, so if the extra pointer needed for the metadata feature is not desired, I can live without it by creating a centrally managed dictionary, i.e. option (a) above. But I can think of various cases in my career when the need for custom metadata came up that would have been extremely convoluted to solve without a (pointer typed) custom tag or dictionary. I find it's a feature that's rarely ever needed, but when it is, having support for it is a lifesaver.

ArdaE commented 6 years ago

P.S. I'm assuming this should already be the case, but if not, I would make sure Godot does not create a metadata dictionary for an object unless there's at least one entry in it, so there isn't any overhead other than a single pointer/reference that's set to null when the feature is not used.

SuperDIMMaX commented 5 years ago

Access meta in editor? Maybe it will be usefull... anyone :/

export var meta : Dictionary setget meta_set, meta_get

func meta_set(v):
    self.__meta__ = v

func meta_get():
    return self.__meta__

small tip get_meta() and set set_meta() == self.__meta__

nobuyukinyuu commented 5 years ago

@SuperDIMMaX

https://docs.godotengine.org/en/3.0/classes/class_object.html#class-object-get-meta

SuperDIMMaX commented 5 years ago

@SuperDIMMaX

https://docs.godotengine.org/en/3.0/classes/class_object.html#class-object-get-meta

I know. but self.__meta__ - faster for me ) I.e. a = 1 == set("a", 1)

SuperDIMMaX commented 5 years ago

metadata is simple dictionary in object named __meta__ .... I guess ....

willnationsdev commented 5 years ago

Don't we just need to make a new EditorPropertyMetadata that's identical to the Dictionary one but hardcoded to have Strings for keys rather than any Variant? And then you just have to add __meta__ to the Object properties in the inspector with some sort of property hint. No?

SuperDIMMaX commented 5 years ago

Don't we just need to make a new EditorPropertyMetadata that's identical to the Dictionary one but hardcoded to have Strings for keys rather than any Variant? And then you just have to add meta to the Object properties in the inspector with some sort of property hint. No?

I use this variant - temporary ... i see, someone need access metadata in editor - this temporary variant. Or you mean write editor plugin?

SuperDIMMaX commented 5 years ago

And i see strange with get access to meta - self.__meta__ working __meta__ not work.

willnationsdev commented 5 years ago

I am referring to just exposing it as a property visible to the Inspector (since I've never seen it there, and I'm therefore guessing it doesn't have PROPERTY_USAGE_EDITOR), defining an EditorProperty object to render it similarly to a Dictionary, but only with String keys allowed, and then handling all the calls on the backend via _get, _set, and _get_property_list.

I'm not sure what you mean by "temporary variant".

SuperDIMMaX commented 5 years ago

now, in inspector you not see metadata of node, in my case, you see and can modify metadata in editor... export

export var meta : Dictionary setget meta_set, meta_get

func meta_set(v):
    self.__meta__ = v

func meta_get():
    return self.__meta__

with remote debug :)

SuperDIMMaX commented 5 years ago

2nd variant:

tool
extends Node2D

func _get_property_list():
    return  [{"hint": "Metadata", "usage": PROPERTY_USAGE_DEFAULT, "name": "Metadata", "type": TYPE_DICTIONARY}]

func _get(p):
    if p == "Metadata":
        return self.__meta__
func _set(p, v):
    if p == "Metadata":
        self.__meta__ = v
        return true

Write plugin or not....

willnationsdev commented 5 years ago

Yes, I was suggesting that something similar to this be done, but on the editor C++ side, so that it's built into the engine.

SuperDIMMaX commented 5 years ago

1 question: Why meta - self.__meta__ working __meta__ not work (node.__meta__ - working)?

ArdaE commented 5 years ago

@SuperDIMMax, what you’re doing is a nice attempt but it’ll only work in cases where metadata is not really needed in the first place. (If you were able to and have already created a script for the node, why not add your own dictionary to it?).

In more detail: Metadata solves a problem that occasionally arises when one uses someone else’s framework, where extra data needs to be attached to built-in types. Many frameworks solve this by providing a generic Tag you can use however you want, while others — including Godot — go a step further and provide a pre-made dictionary for it (for ease of use at the expense of some minor performance/memory penalty for some use cases). The problems that metadata solves only exist when there’s a distinction between framework (engine) code and user code. If you’re able to attach a script to a node type like you do in your case, that means you could have created your own dictionary to start with, and that you really didn’t need Godot’s metadata feature.

If metadata is to be displayed in Godot’s editor, support for it should be implemented within the editor. (Unless Godot supports extension of existing classes with new functions, in which case your code could be used by attaching it to the Object class. I don’t know if Godot has support for such a concept or not, though technically it should be able to support it, considering its general design).

SuperDIMMaX commented 5 years ago

In my case, need dynamically create nodes with unknown number of variables (and functions)... I can write:

class foo extends Node:
    var a = {}

var t = foo.new()
t.a = {"j":100, "k":200}
add_child(t)

but, its slowly and need more ram... P.s. set_meta("a",100) faster, than self.__meta__["a"] = 100

mysticfall commented 5 years ago

Currently, I'm using it to store additional data (i.e. keys) for TreeItem. But it can be replaced with something else, so I don't have a strong opinion about the matter.

On a side note, I wish it didn't throw an error when an unserializable value is given, so that it can treat such values as transient.

The reason is, I want to use it as a way to overcome such a case where you need to add a feature to a built-in node type but cannot extend it.

In C#, there's a concept called 'extension method' with which you can 'attach' a new method to a given type without actually modifying its source. But you can't add any property that way (I heard it's planned for a future release though) so object metadata could be used as a backing store for such ad-hoc properties, if it supported unserializable values.

It could be quite a corner case, but still I think it can be also useful in other scenarios as well, like for storing an actual game object in a TreeItem rather than just its key, for example.

jonbonazza commented 5 years ago

Could this be used (maybe with some QoL improvements from the editor) to easily support Saved Node State? (For example, for saving games).

nobuyukinyuu commented 5 years ago

Probably, but node state can already be saved with PackedScene into a serialized format. It might be more useful as a feature request to be able to have that output to a string if you want to save such things into metadata.

slapin commented 5 years ago

nice feature to add simple marks and data to objects when groups are not enough. that would be great if I knew about this feature before... So many useless scripts would be eliminated.

slapin commented 5 years ago

I'd be fine with 2 or 3, also would be nice to have instance-specific metadata too.

mhilbrunner commented 5 years ago

Closing this, as the consensus seems to be to definitely keep object metadata. I opened a new issue for exposing metadata to the editor: https://github.com/godotengine/godot/issues/29081