skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
jame581/godotprompter129 installs

resource-pattern

Use when creating data containers in Godot — custom Resources for configuration, items, stats, and editor integration

How do I install this agent skill?

npx skills add https://github.com/jame581/godotprompter --skill resource-pattern
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubpass

    This skill provides best practices and code examples for using the Resource pattern in Godot 4.3+. It covers data management, editor integration, and includes necessary security warnings regarding loading untrusted resource files.

  • Socketpass

    No alerts

  • Snykpass

    Risk: LOW · No issues

What does this agent skill do?

Resource Pattern in Godot 4.3+

Resources are Godot's built-in data containers. Use them for configuration, item definitions, character stats, and any data that lives outside the scene tree. All examples target Godot 4.3+ with no deprecated APIs.

Related skills: inventory-system for Resource-based item definitions, save-load for Resource serialization, component-system for data-driven component configuration, ability-system for Resource-based ability definitions built on this pattern.


1. What Are Resources

A Resource is a reference-counted data object that:

  • Is saved as a .tres (text) or .res (binary) file on disk
  • Is editable directly in the Godot Inspector
  • Is loaded once and shared by default — every node that loads the same path gets the same in-memory object
  • Can be nested inside other Resources and PackedScenes
  • Survives scene changes (unlike Node state, which is discarded on scene reload)

Because Resources are shared by default, they are ideal for read-only data (item definitions, audio settings, ability blueprints). For per-instance mutable state, call make_unique() or duplicate() — see section 8.


2. When to Use Resources

Use CaseExample ResourceAlternative
Item definitionsItemData with name, icon, valueDictionary (loses type safety)
Enemy configurationEnemyStats with health, speed, damageExported vars on Node (not reusable)
Character statsCharacterStats with base valuesAutoload (global state, hard to test)
Ability definitionsAbilityData with cooldown, cost, effectHardcoded constants
Level metadataLevelConfig with music, time limit, goalsJSON (no editor integration)
Audio / visual themesUIThemeData with color palette, fontsTheme resource (same idea, built-in)
Dialogue treesDialogueLine referencing next lineJSON (no type checking)

Use a custom Resource any time you want Inspector editing + typed data + sharing across scenes.


3. Basic Custom Resource

GDScript

# item_data.gd
class_name ItemData
extends Resource

enum ItemType { WEAPON, ARMOUR, CONSUMABLE, QUEST }

@export var name:        String   = ""
@export var description: String   = ""
@export var icon:        Texture2D
@export var value:       int      = 0
@export var item_type:   ItemType = ItemType.CONSUMABLE

Create an instance in the editor: right-click the FileSystem panel → New Resource → choose ItemData. Fill in the Inspector fields and save as res://data/items/health_potion.tres.

Load it at runtime:

var potion: ItemData = load("res://data/items/health_potion.tres")
print(potion.name)        # "Health Potion"
print(potion.value)       # 50

C#

// ItemData.cs
using Godot;

[GlobalClass]
public partial class ItemData : Resource
{
    public enum ItemType { Weapon, Armour, Consumable, Quest }

    [Export] public string   Name        { get; set; } = "";
    [Export] public string   Description { get; set; } = "";
    [Export] public Texture2D Icon       { get; set; }
    [Export] public int      Value       { get; set; } = 0;
    [Export] public ItemType Type        { get; set; } = ItemType.Consumable;
}

[GlobalClass] is required in C# so the editor recognizes the class and shows it in New Resource.

var potion = GD.Load<ItemData>("res://data/items/health_potion.tres");
GD.Print(potion.Name);   // "Health Potion"
GD.Print(potion.Value);  // 50

4. Editor Integration

Use class_name, @tool, and @icon to make custom Resources first-class in the Inspector — they appear in the Resource picker, can be created via right-click "New Resource", show a custom icon. @export_group and @export_subgroup organize properties.

See references/editor-integration.md for the full GDScript + C# pattern with class_name, @icon, @export_group.


5. Resource as Configuration

The strongest use case: data-driven game content. Loot tables, enemy stats, ability definitions, item catalogs all become custom Resources. Designers tweak .tres files in the Inspector; programmers wire the loader. Avoids JSON's loose schema and stringly-typed parsing.

See references/configuration-pattern.md for a worked LootTable + DropEntry example (GDScript + C#).


6. Resource Collections

@export var entries: Array[Entry] = [] exposes a typed array in the Inspector — drag and drop multiple Resource files. For startup-loaded sets, use ResourcePreloader. For asset-folder discovery at runtime, walk DirAccess.

See references/collections.md for typed-array exports (v1.6.0 C# parity preserved), ResourcePreloader setup, and the directory-walking loader pattern.


7. Resource vs Node

AspectResourceNode
PurposeData storage and configurationBehavior, rendering, physics, input
Scene treeNot in the treeLives in the scene tree
Lifecycle hooksNone (_init only)_ready, _process, _physics_process, etc.
SharingShared by default (same path = same object)Each instance is independent
SerializationSaved as .tres / .res, Inspector-editableSaved inside .tscn
SignalsSupportedSupported
Use forItem data, stats, config, ability blueprintsPlayer, enemy, UI widgets, cameras
Avoid forAnything needing per-frame updates or scene queriesStatic data that never changes at runtime

Rule of thumb: if it has no behavior and no need to exist in the scene tree, make it a Resource. If it needs to move, render, receive input, or run per-frame logic, make it a Node.


8. Sharing vs Unique

By default, Resources are shared by reference. Two scenes referencing res://items/sword.tres see the SAME instance — mutating one mutates both. Use .duplicate() (shallow) or .duplicate(true) (deep) for instance-local state.

See references/sharing-vs-unique.md for the full pattern (v1.6.0 C# parity preserved).


9. Saving Custom Resources

ResourceSaver.save(resource, path) writes to a .tres (text) or .res (binary) file. Use for save games (where the schema lives in code as a Resource subclass) instead of hand-rolled JSON when you want strong typing.

See references/saving-resources.md for the full save/load pattern (GDScript + C#) and security caveat (never load .tres from untrusted sources — they execute embedded GDScript).


10. Anti-patterns

Mutable shared Resources without duplicate() — accidental shared state

# BAD — all enemies share the same EnemyStats object.
# Damaging one enemy damages all of them.
class_name Enemy
extends CharacterBody2D

@export var stats: EnemyStats  # loaded from .tres, shared

func take_damage(amount: int) -> void:
    stats.health -= amount  # mutates the shared Resource!
# GOOD — each enemy owns its own copy.
func _ready() -> void:
    stats = stats.duplicate()  # now safe to mutate

Game logic inside Resources

# BAD — Resources have no scene tree access, no _process, no signals from nodes.
class_name EnemyStats
extends Resource

func update_health_regen(delta: float) -> void:
    # Can't call get_tree(), can't read Input, can't access nodes.
    # This logic belongs in a Node.
    health = min(health + regen_rate * delta, max_health)
# GOOD — keep logic in Nodes, data in Resources.
# enemy.gd
func _process(delta: float) -> void:
    _current_health = minf(_current_health + stats.regen_rate * delta, stats.max_health)

Giant monolithic Resources

# BAD — one Resource holds everything; impossible to reuse parts.
class_name GameConfig
extends Resource

@export var player_health: int
@export var player_speed: float
@export var enemy_goblin_health: int
@export var enemy_goblin_speed: float
@export var enemy_troll_health: int
# ... 200 more properties
# GOOD — small focused Resources, composed together.
class_name PlayerConfig
extends Resource

@export var health: int   = 100
@export var speed:  float = 200.0
class_name EnemyConfig
extends Resource

@export var health: int   = 50
@export var speed:  float = 80.0
// ❌ Anti-pattern: mutating a shared Resource — accidental shared state
// All enemies share the same EnemyStats object loaded from the .tres file.
// Damaging one enemy damages all of them.
[GlobalClass]
public partial class Enemy : CharacterBody2D
{
    [Export] public EnemyStats Stats;  // loaded from .tres, shared by default

    public void TakeDamage(int amount)
    {
        Stats.Health -= amount;  // mutates the shared Resource — affects every Enemy!
    }
}

// ✅ Correct: duplicate before mutating so each instance owns its own copy.
public partial class EnemyGood : CharacterBody2D
{
    [Export] public EnemyStats Stats;

    public override void _Ready()
    {
        Stats = (EnemyStats)Stats.Duplicate();  // now safe to mutate independently
    }

    public void TakeDamage(int amount)
    {
        Stats.Health -= amount;  // only affects this instance
    }
}

// ❌ Anti-pattern: game logic inside a Resource
// Resources have no scene-tree access, no _Process, and cannot call GetTree() or read Input.
[GlobalClass]
public partial class EnemyStatsBad : Resource
{
    [Export] public float Health    { get; set; }
    [Export] public float MaxHealth { get; set; }
    [Export] public float RegenRate { get; set; }

    // This logic belongs in a Node, not a Resource.
    public void UpdateHealthRegen(double delta)
    {
        // Cannot call GetTree(), cannot access nodes, cannot read Input.
        Health = Mathf.Min(Health + RegenRate * (float)delta, MaxHealth);
    }
}

// ✅ Correct: keep logic in Nodes, data in Resources.
public partial class EnemyCorrect : CharacterBody2D
{
    [Export] public EnemyStats Stats;
    private float _currentHealth;

    public override void _Ready()
    {
        Stats = (EnemyStats)Stats.Duplicate();
        _currentHealth = Stats.MaxHealth;
    }

    public override void _Process(double delta)
    {
        _currentHealth = Mathf.Min(_currentHealth + Stats.RegenRate * (float)delta, Stats.MaxHealth);
    }
}

// ❌ Anti-pattern: giant monolithic Resource
// One Resource holds everything; impossible to reuse individual pieces.
[GlobalClass]
public partial class GameConfigBad : Resource
{
    [Export] public int   PlayerHealth      { get; set; }
    [Export] public float PlayerSpeed       { get; set; }
    [Export] public int   EnemyGoblinHealth { get; set; }
    [Export] public float EnemyGoblinSpeed  { get; set; }
    [Export] public int   EnemyTrollHealth  { get; set; }
    // ... 200 more properties
}

// ✅ Correct: small focused Resources, composed together.
[GlobalClass]
public partial class PlayerConfig : Resource
{
    [Export] public int   Health { get; set; } = 100;
    [Export] public float Speed  { get; set; } = 200.0f;
}

[GlobalClass]
public partial class EnemyConfig : Resource
{
    [Export] public int   Health { get; set; } = 50;
    [Export] public float Speed  { get; set; } = 80.0f;
}

11. Checklist

  • Resource subclass uses class_name so the editor can find it for New Resource
  • C# classes have [GlobalClass] attribute
  • All Inspector-editable fields use @export / [Export]
  • @export_range used on numeric fields with designer-tunable bounds
  • @export_group and @export_category used to organize Inspector layout for Resources with many fields
  • Per-instance mutable Resources are duplicate()-d in _ready()
  • Read-only shared Resources (definitions, blueprints) are not duplicated
  • Game logic (per-frame updates, scene queries) lives in Nodes, not Resources
  • Large data sets split into focused single-responsibility Resources
  • .tres used during development; .res considered for shipped production data
  • ResourceSaver.save() return value checked and errors reported with push_error()
  • .tres / .res files never loaded from untrusted external sources

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/jame581/godotprompter/resource-pattern">View resource-pattern on skillZs</a>