Skip to content
Development
Skill

/godot-resources

Godot 4.7 data-driven design with custom Resources: class_name and @export fields, .tres files, shared references vs duplicate_deep(), uids and threaded loading. Use for items, stats or enemy configs as data, or when editing one enemy changes every enemy or a .tres stops

From plugin
awesome-gamedev-agent-skills
8.6k79 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill godot-resources --agent claude-code

SKILL.md

godot-resources.SKILL.md
name: godot-resources
description: >
  Godot 4.7 data-driven design with custom Resources: class_name and @export fields, .tres files,
  shared references vs duplicate_deep(), uids and threaded loading. Use for items, stats or enemy
  configs as data, or when editing one enemy changes every enemy or a .tres stops loading. Not for
  save slots (`save-systems`) or the C# version (`godot-csharp`).

Godot Resources (4.x)

Model game data as `Resource` objects saved as `.tres`/`.res`, share them safely, and load them without hitches. Targets **Godot 4.7** (see Version notes).

When to use

  • Items, stats, enemy configs, dialogue lines or level metadata as data files; custom

`Resource` subclasses; `ResourceLoader`/`ResourceSaver` code.

**When *not* to use:** scene structure → `godot-nodes-scenes`; the player's runtime progress, slots and migration → `save-systems`; C# → `godot-csharp`.

Core workflow

1. **Subclass `Resource`** with `class_name` and typed `@export` fields (Pattern 1). The `class_name` is what lets the editor offer it in "New Resource" and as an export type. 2. **Create instances as text `.tres` files.** You can write them directly (Pattern 2) instead of clicking through the FileSystem dock and Inspector; use binary `.res` only for large generated data. 3. **Reference resources from nodes** with `@export var data: ItemResource` or `@export var loot: Array[ItemResource]`, or load them by path in code. 4. **Load** with `preload` (constant path) or `load` (variable path); use `ResourceLoader.load_threaded_request` for large scenes (see the reference). 5. **Duplicate before mutating** a shared resource at runtime (Pattern 3); every node that loaded the same path holds the same object. 6. **Check before you finish.**

  • After adding or moving `.tres`/`.gd` files, run the `godot-nodes-scenes` skill's

`scripts/check_godot_project.py <project-dir>` to catch broken `ext_resource` paths, uids and ids. Fix every `ERROR`.

  • Prove the data loads and copies correctly with a headless test (Pattern 4):

`godot --headless --path . --import` once on a fresh checkout (so `class_name` types resolve), then `godot --headless --path . --script res://tests/test_items.gd`, and read the exit code. With no Godot binary (`godot`, `godot4` or `$GODOT_BIN`), ask the user to run both lines and paste the output.

Patterns

1. A custom data resource

# res://items/item.gd
extends Resource
class_name ItemResource

@export var id: StringName = &""
@export var display_name: String = "Item"
@export_multiline var description: String = ""
@export var icon: Texture2D
@export var max_stack: int = 99
@export var value: int = 0

2. The same item as a hand-written `.tres`

[gd_resource type="Resource" script_class="ItemResource" format=3]

[ext_resource type="Script" path="res://items/item.gd" id="1_item"]

[resource]
script = ExtResource("1_item")
id = &"sword"
display_name = "Sword"
max_stack = 1
value = 50

Leave out the `uid=` attributes; Godot adds them the next time the file is saved. Fields left out keep the script defaults.

3. Duplicate before mutating shared data

func give_unique_copy(template: ItemResource) -> ItemResource:
    # true = deep: copies nested arrays/dictionaries and *embedded* sub-resources, but
    # sub-resources saved in their own .tres stay shared. Use
    # template.duplicate_deep(Resource.DEEP_DUPLICATE_ALL) to copy those as well.
    var copy: ItemResource = template.duplicate(true)
    copy.value += 5                # mutating the copy won't touch the template .tres
    return copy

4. Headless check: the file loads as its class and copies are independent

# res://tests/test_items.gd
extends SceneTree

func _initialize() -> void:
    var failed := 0
    var sword := load("res://items/sword.tres") as ItemResource
    if sword == null:
        failed += 1
        push_error("sword.tres did not load as ItemResource")
    else:
        var copy: ItemResource = sword.duplicate(true)
        copy.value += 1
        if copy.value == sword.value:
            failed += 1
            push_error("duplicate(true) shares state with the template")
    print("Results: %d failed" % failed)
    quit(1 if failed > 0 else 0)

Gotchas

  • **Resources are shared by reference.** Every `load()` of one path returns the same cached

object, so mutating it changes every user (and re-saving writes the change into the file). Duplicate for per-instance state, or set `resource_local_to_scene = true` for a copy per scene instance.

  • **A deep copy still changes the original's sub-resource** → `duplicate(true)` only copies

sub-resources embedded in the file; one saved as its own `.tres` (e.g. a shared `stats.tres` inside `goblin.tres`) stays the same object. Use `duplicate_deep(Resource.DEEP_DUPLICATE_ALL)` when those must be copied too.

  • **`class_name` is required for editor and typed use.** Without it the class is missing

from "New Resource" and cannot be an `@export` type.

  • **`class_name` "not declared" only in headless or CI runs** → the global class cache in

`.godot/` is missing on a fresh checkout. Run `godot --headless --path . --import` first.

  • **`res://` is read-only in exported games.** Write runtime data to `user://`.

`ResourceSaver.save` to `res://` works only while running from the project folder.

  • **Moving a `.gd` or `.tres` outside the editor breaks `ext_resource` paths.** Move the

`.uid` sidecar with scripts, then run the checker; a uid that still resolves keeps the link working.

  • **Nodes stored in a Resource don't serialize.** Resources hold data; reference scenes as

`PackedScene`, not node instances.

  • **Cyclic references** (A holds B holds A) can fail to save or load cleanly; keep data

graphs acyclic or link by id.

  • **`preload` vs `load`.** `preload` needs a constant path and loads with the script;

`load` takes a runtime

Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
8.6k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills12d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react3d252k
@facebook@facebookInstall Skill

Questions about godot-resources.

Which coding agents can use godot-resources?
godot-resources installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.