A Godot 4 plugin for managing and querying collections of resources through a dedicated editor interface and runtime API.
Overview
YARD has two complementary aspects:
A table-based resource editor. The YARD editor tab lets you create and manage registries: catalogues of resources grouped by class. Each registry provides a spreadsheet-like view of your resources and their properties.
A lightweight runtime API. At runtime, a Registry is just a small .tres file holding UIDs and string IDs. It does not load the resources it references. _You_ control when loading happens, and how.
Features
- π· Reference resources by stable, human-readable string IDs instead of file paths or UIDs lying around in some autoload
- π Restrict a registry to a specific class β only matching resources can be added
- π Sync a registry from a directory β entries are added and removed automatically as resource files appear or disappear, recursively or not
- π₯§ Bake a property index in the editor for zero-cost runtime queries by resource property values
- π¦ Load entries individually, all at once (blocking), or asynchronously via threaded loading
- β‘ No runtime overhead beyond what you explicitly request, all expensive operations happen in the editor
Installation
- Copy the
addons/yardfolder into your project'saddons/directory - Enable the plugin in Project > Project Settings > Plugins
Usage
Creating a registry
Open the Registry tab in the editor, click File > New Registry, and configure:
- Class restriction β only resources of this class (or its subclasses) will be accepted
- Scan directory β the registry will stay in sync with resource files in this folder
- Indexed properties β property names to bake into the index for runtime filtering
If you didn't specify a scan directory, you can add entries manually by dragging and dropping resources from the FileSystem dock into the registry table, or use the resource picker at the bottom.
Loading an entry
const ENEMIES: Registry = preload("res://data/enemy_registry.tres")
func _on_fight_started() -> void:
var skeleton: Enemy = ENEMIES.load_entry(&"skeleton")
Loading all entries
# Blocking
var all_enemies := ENEMIES.load_all_blocking()
# Threaded
var tracker := ENEMIES.load_all_threaded_request()
Querying the index
The property index can be baked while in the editor. At runtime, queries run without loading any resource.
# All entries where rarity == LEGENDARY
var legendaries := WEAPONS.filter_by_value(&"rarity", Rarity.LEGENDARY)
# All entries where level >= 10
var high_level := WEAPONS.filter_by(&"level", func(v): return v >= 10)
# AND query across multiple properties
var legendary_swords := WEAPONS.filter_by_values({
&"rarity": Rarity.LEGENDARY,
&"type": &"sword",
})
API Reference
Full API documentation for Registry is available in the in-editor class reference.
Lookup
| Method | Description |
|---|---|
has(id) |
Returns true if the string ID or UID exists in the registry |
has_string_id(string_id) |
Returns true if the string ID exists |
has_uid(uid) |
Returns true if the UID exists |
get_uid(id) |
Resolves a string ID or UID to its canonical UID |
get_string_id(uid) |
Returns the string ID for a given UID |
get_all_string_ids() |
Returns all registered string IDs |
get_all_uids() |
Returns all registered UIDs |
size() |
Number of entries |
is_empty() |
Returns true if the registry has no entries |
Loading
| Method | Description |
|---|---|
load_entry(id, type_hint, cache_mode) |
Loads and returns a single resource |
load_all_blocking(type_hint, cache_mode) |
Loads all entries synchronously, returns Dictionary[StringName, Resource] |
load_all_threaded_request(type_hint, ...) |
Requests threaded loading for all entries, returns a RegistryLoadTracker |
Filtering
Filtering methods require the relevant properties to have been indexed in the editor.
| Method | Description |
|---|---|
is_property_indexed(property) |
Returns true if the property has a baked index |
filter_by_value(property, value) |
Returns string IDs of entries where property == value |
filter_by(property, predicate) |
Returns string IDs of entries where predicate.call(value) is true |
filter_by_values(criteria) |
Returns string IDs matching all criteria (AND logic) |
How the property index work
The index is simply a nested dictionary stored inside the registry .tres file :
_property_index = {
&"rarity": {
Rarity.LEGENDARY: { &"excalibur": true, &"mjolnir": true },
Rarity.COMMON: { &"stick": true },
}
&"level": {
1: { &"stick": true },
10: { &"excalibur": true },
12: { &"mjolnir": true },
}
}
Contributing
Contributions are welcome. For major changes, open an issue first to discuss what you have in mind.
License
Changelog for version v1.1.0
No changelog provided for this version.
Reviews (1)
Clean solution for editing resources as a table in Godot 4. So far I haven't tried the runtime data querying yet, as I have my own solution at the moment, but even just for editing the resources this is already better than the alternatives. Edit: I have now replaced my own resource managemeny system with YARD, it is very slick, even allowing my to use custom exports to let me pick id strings as dropdowns in the inspector, rather than having to hand type id strings anywhere. Edit: They're also super responsive on Github! I left an issue, and it was resolved in 2 days. Crazy! I can't imagine starting a project without this plugin. It's just too useful.
Login to write a review.