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-patternIs 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 Case | Example Resource | Alternative |
|---|---|---|
| Item definitions | ItemData with name, icon, value | Dictionary (loses type safety) |
| Enemy configuration | EnemyStats with health, speed, damage | Exported vars on Node (not reusable) |
| Character stats | CharacterStats with base values | Autoload (global state, hard to test) |
| Ability definitions | AbilityData with cooldown, cost, effect | Hardcoded constants |
| Level metadata | LevelConfig with music, time limit, goals | JSON (no editor integration) |
| Audio / visual themes | UIThemeData with color palette, fonts | Theme resource (same idea, built-in) |
| Dialogue trees | DialogueLine referencing next line | JSON (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),
ResourcePreloadersetup, and the directory-walking loader pattern.
7. Resource vs Node
| Aspect | Resource | Node |
|---|---|---|
| Purpose | Data storage and configuration | Behavior, rendering, physics, input |
| Scene tree | Not in the tree | Lives in the scene tree |
| Lifecycle hooks | None (_init only) | _ready, _process, _physics_process, etc. |
| Sharing | Shared by default (same path = same object) | Each instance is independent |
| Serialization | Saved as .tres / .res, Inspector-editable | Saved inside .tscn |
| Signals | Supported | Supported |
| Use for | Item data, stats, config, ability blueprints | Player, enemy, UI widgets, cameras |
| Avoid for | Anything needing per-frame updates or scene queries | Static 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
.tresfrom 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_nameso the editor can find it for New Resource - C# classes have
[GlobalClass]attribute - All Inspector-editable fields use
@export/[Export] -
@export_rangeused on numeric fields with designer-tunable bounds -
@export_groupand@export_categoryused 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
-
.tresused during development;.resconsidered for shipped production data -
ResourceSaver.save()return value checked and errors reported withpush_error() -
.tres/.resfiles never loaded from untrusted external sources
How can the creator link this skill?
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>