godot-resource-data-patterns
0
总安装量
3
周安装量
安装命令
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-resource-data-patterns
Agent 安装分布
opencode
3
gemini-cli
3
codex
3
claude-code
2
github-copilot
2
Skill 文档
Resource & Data Patterns
Resource-based design, typed arrays, and serialization define reusable, inspector-friendly data structures.
Available Scripts
data_factory_resource.gd
Expert resource factory with type validation and batch instantiation.
resource_pool.gd
Object pooling for Resource instances – reduces allocation overhead in hot paths.
resource_validator.gd
Validates Resource files for missing exports and configuration issues.
MANDATORY – For Data Systems: Read data_factory_resource.gd before implementing item/stat databases.
NEVER Do in Resource Design
- NEVER modify resource instances without duplicating â
player.stats.health -= 10on loaded resource? Modifies the.tresfile on disk. MUST use.duplicate()first. - NEVER use untyped arrays â
@export var items: Array = []accepts ANY type = runtime errors. UseArray[ItemData]for type safety + autocomplete. - NEVER forget @export for inspector editing â Resource property without
@export? Invisible in Inspector. Use@exportfor editable properties. - NEVER put logic in base Resource â
Resourcehas no lifecycle (_ready,_process). Useextends RefCountedfor runtime logic OR attach to Node. - NEVER serialize Node references â
@export var player_node: Nodein Resource? Breaks on save/load. Store NodePath OR UID instead. - NEVER use ResourceSaver.save() without error check â
ResourceSaver.save(res, path)can fail (permissions, invalid path). MUST check return error code.
| Type | Use Case | Serializable | Can Save to Disk | Inspector Support |
|---|---|---|---|---|
Resource |
Data that needs saving/loading | â | â | â |
RefCounted |
Temporary runtime data | â | â | â |
Node |
Scene hierarchy entities | â (scene files) | â | â |
When to Use Resources
Use Resources For:
- Item definitions (weapons, consumables, equipment)
- Character stats/progression systems
- Skill/ability data
- Configuration files
- Dialogue databases
- Enemy/NPC templates
Use RefCounted For:
- Temporary calculations
- Runtime-only state machines
- Utility classes without data persistence
Implementation Patterns
Pattern 1: Custom Resource Class
# item_data.gd
extends Resource
class_name ItemData
@export var item_name: String = ""
@export var description: String = ""
@export_enum("Weapon", "Consumable", "Armor") var item_type: int = 0
@export var icon: Texture2D
@export var value: int = 0
@export var stackable: bool = false
@export var max_stack: int = 1
func use() -> void:
match item_type:
0: # Weapon
print("Equipped weapon: ", item_name)
1: # Consumable
print("Consumed: ", item_name)
2: # Armor
print("Equipped armor: ", item_name)
Create Resource Instances:
- In Inspector: Right-click â New Resource â ItemData
- Fill in properties, Save as
res://items/health_potion.tres
Pattern 2: Character Stats Resource
# character_stats.gd
extends Resource
class_name CharacterStats
@export var max_health: int = 100
@export var max_mana: int = 50
@export var strength: int = 10
@export var defense: int = 5
@export var speed: float = 100.0
var current_health: int = max_health:
set(value):
current_health = clampi(value, 0, max_health)
var current_mana: int = max_mana:
set(value):
current_mana = clampi(value, 0, max_mana)
func take_damage(amount: int) -> int:
var actual_damage := maxi(amount - defense, 0)
current_health -= actual_damage
return actual_damage
func heal(amount: int) -> void:
current_health += amount
func duplicate_stats() -> CharacterStats:
var stats := CharacterStats.new()
stats.max_health = max_health
stats.max_mana = max_mana
stats.strength = strength
stats.defense = defense
stats.speed = speed
stats.current_health = current_health
stats.current_mana = current_mana
return stats
Usage:
# player.gd
extends CharacterBody2D
@export var stats: CharacterStats
func _ready() -> void:
if stats:
# Create runtime copy to avoid modifying the original resource
stats = stats.duplicate_stats()
Pattern 3: Database Pattern (Array of Resources)
# item_database.gd
extends Resource
class_name ItemDatabase
@export var items: Array[ItemData] = []
func get_item_by_name(item_name: String) -> ItemData:
for item in items:
if item.item_name == item_name:
return item
return null
func get_items_by_type(item_type: int) -> Array[ItemData]:
var filtered: Array[ItemData] = []
for item in items:
if item.item_type == item_type:
filtered.append(item)
return filtered
Create Database:
- Create
ItemDatabaseresource - Expand
itemsarray in Inspector - Add
ItemDataresources to array - Save as
res://data/item_database.tres
Usage:
# Global autoload
const ITEM_DB := preload("res://data/item_database.tres")
func get_item(name: String) -> ItemData:
return ITEM_DB.get_item_by_name(name)
Pattern 4: Runtime-Only Data (RefCounted)
For data that doesn’t need persistence:
# damage_calculation.gd
extends RefCounted
class_name DamageCalculation
var base_damage: int
var critical_hit: bool
var damage_type: String
func calculate_final_damage(target_defense: int) -> int:
var final_damage := base_damage - target_defense
if critical_hit:
final_damage *= 2
return maxi(final_damage, 1)
Usage:
var calc := DamageCalculation.new()
calc.base_damage = 50
calc.critical_hit = randf() > 0.8
calc.damage_type = "physical"
var damage := calc.calculate_final_damage(enemy.defense)
Advanced Patterns
Pattern 5: Nested Resources
# weapon_data.gd
extends ItemData
class_name WeaponData
@export var damage: int = 10
@export var attack_speed: float = 1.0
@export var special_effects: Array[StatusEffect] = []
# status_effect.gd
extends Resource
class_name StatusEffect
@export var effect_name: String
@export var duration: float
@export var damage_per_second: int
Pattern 6: Resource Scripts with Signals
# inventory.gd
extends Resource
class_name Inventory
signal item_added(item: ItemData)
signal item_removed(item: ItemData)
var items: Array[ItemData] = []
func add_item(item: ItemData) -> void:
items.append(item)
item_added.emit(item)
func remove_item(item: ItemData) -> void:
items.erase(item)
item_removed.emit(item)
Pattern 7: Resource Loading at Runtime
# Load resource dynamically
var item: ItemData = load("res://items/sword.tres")
# Preload for better performance (compile-time)
const SWORD := preload("res://items/sword.tres")
# Load all resources in a directory
func load_all_items() -> Array[ItemData]:
var items: Array[ItemData] = []
var dir := DirAccess.open("res://items/")
if dir:
dir.list_dir_begin()
var file_name := dir.get_next()
while file_name != "":
if file_name.ends_with(".tres"):
var item: ItemData = load("res://items/" + file_name)
items.append(item)
file_name = dir.get_next()
return items
Best Practices
1. Always Duplicate Resources in Runtime
# â
Good - create instance copy
@export var stats: CharacterStats
func _ready():
stats = stats.duplicate() # Or custom duplicate method
# â Bad - modifies the original resource file
@export var stats: CharacterStats
func _ready():
stats.current_health -= 10 # This changes the .tres file!
2. Use @export for Inspector Editing
# â
Makes properties editable in Inspector
@export var max_health: int = 100
@export var icon: Texture2D
@export_range(0, 100) var drop_chance: int = 50
3. Organize Resources by Category
res://data/
items/
weapons/
sword.tres
bow.tres
consumables/
health_potion.tres
characters/
player_stats.tres
enemy_goblin.tres
databases/
item_database.tres
4. Type Your Arrays
# â
Good - typed array
@export var items: Array[ItemData] = []
# â Bad - untyped array
@export var items: Array = []
Saving/Loading Resources
# Save resource to disk
func save_inventory(inventory: Inventory, path: String) -> void:
ResourceSaver.save(inventory, path)
# Load resource from disk
func load_inventory(path: String) -> Inventory:
if ResourceLoader.exists(path):
return ResourceLoader.load(path)
return null
Reference
Related
- Master Skill: godot-master