11 KiB
GECS Troubleshooting Guide
Quickly solve common GECS issues
This guide helps you diagnose and fix the most common problems when working with GECS. Find your issue, apply the solution, and learn how to prevent it.
📋 Quick Diagnosis
My Game Isn't Working At All
Symptoms: No entities moving, systems not running, nothing happening
Quick Check:
# In your _process() method, ensure you have:
func _process(delta):
if ECS.world:
ECS.world.process(delta) # This line is critical!
Missing this? → Systems Not Running
Entities Aren't Moving/Updating
Symptoms: Entities exist but don't respond to systems
Quick Check:
- Are your entities added to the world?
ECS.world.add_entity(entity) - Do your entities have the right components? Check system queries
- Are your systems properly organized in scene hierarchy? Check default_systems.tscn
Still broken? → Entity Issues
Performance Is Terrible
Symptoms: Low FPS, stuttering, slow response
Quick Check:
- Enable profiling:
ECS.world.enable_profiling = true - Check entity count:
print(ECS.world.entity_count) - Look for expensive queries in your systems
Need optimization? → Performance Issues
🚫 Systems Not Running
Problem: Systems Never Execute
Error Messages:
- No error, but
process()method never called - Entities exist but don't change
Solution:
# ✅ Ensure this exists in your main scene
func _process(delta):
ECS.process(delta) # This processes all systems
# OR if using system groups:
func _process(delta):
ECS.process(delta, "physics")
ECS.process(delta, "render")
Prevention: Always call ECS.process() in your main game loop.
Problem: System Query Returns Empty
Symptoms: System exists but process() never called
Diagnosis:
# Add this to your system for debugging
class_name MySystem extends System
func _ready():
print("MySystem query result: ", query().execute().size())
func query():
return q.with_all([C_ComponentA, C_ComponentB])
Common Causes:
-
Missing Components:
# ❌ Problem - Entity missing required component var entity = Entity.new() entity.add_component(C_ComponentA.new()) # Missing C_ComponentB! # ✅ Solution - Add all required components entity.add_component(C_ComponentA.new()) entity.add_component(C_ComponentB.new()) -
Wrong Component Types:
# ❌ Problem - Using instance instead of class func query(): return q.with_all([C_ComponentA.new()]) # Wrong! # ✅ Solution - Use class reference func query(): return q.with_all([C_ComponentA]) # Correct! -
Component Not Added to World:
# ❌ Problem - Entity not in world var entity = Entity.new() entity.add_component(C_ComponentA.new()) # Entity never added to world! # ✅ Solution - Add entity to world ECS.world.add_entity(entity)
🎭 Entity Issues
Problem: Entity Components Not Found
Error Messages:
get_component() returned nullEntity does not have component of type...
Diagnosis:
# Debug what components an entity actually has
func debug_entity_components(entity: Entity):
print("Entity components:")
for component_path in entity.components.keys():
print(" ", component_path)
Solution: Ensure components are added correctly:
# ✅ Correct component addition
var entity = Entity.new()
entity.add_component(C_Health.new(100))
entity.add_component(C_Position.new(Vector2(50, 50)))
# Verify component exists before using
if entity.has_component(C_Health):
var health = entity.get_component(C_Health)
health.current -= 10
Problem: Component Properties Not Updating
Symptoms: Setting component properties has no effect
Common Causes:
-
Getting Component Reference Once:
# ❌ Problem - Stale component reference var health = entity.get_component(C_Health) # ... later in code, component gets replaced ... health.current = 50 # Updates old component! # ✅ Solution - Get fresh reference each time entity.get_component(C_Health).current = 50 -
Modifying Wrong Entity:
# ❌ Problem - Variable confusion var player = get_player_entity() var enemy = get_enemy_entity() # Accidentally modify wrong entity player.get_component(C_Health).current = 0 # Meant to be enemy! # ✅ Solution - Use clear variable names var player_health = player.get_component(C_Health) var enemy_health = enemy.get_component(C_Health) enemy_health.current = 0
💥 Common Errors
Error: "Cannot access property/method on null instance"
Full Error:
Invalid get index 'current' (on base: 'null instance')
Cause: Component doesn't exist on entity
Solution:
# ❌ Causes null error
var health = entity.get_component(C_Health)
health.current -= 10 # health is null!
# ✅ Safe component access
if entity.has_component(C_Health):
var health = entity.get_component(C_Health)
health.current -= 10
else:
print("Entity doesn't have C_Health!")
Error: "Class not found"
Full Error:
Identifier 'ComponentName' not found in current scope
Causes & Solutions:
-
Missing class_name:
# ❌ Problem - No class_name declaration extends Component # Script exists but can't be referenced by name # ✅ Solution - Add class_name class_name C_Health extends Component -
File not saved or loaded:
- Save your component script files
- Restart Godot if classes still not found
- Check for syntax errors in the component file
-
Wrong inheritance:
# ❌ Problem - Wrong base class class_name C_Health extends Node # Should be Component! # ✅ Solution - Correct inheritance class_name C_Health extends Component
🐌 Performance Issues
Problem: Low FPS / Stuttering
Diagnosis Steps:
-
Enable profiling:
ECS.world.enable_profiling = true # Check processing times func _process(delta): ECS.process(delta) print("Frame time: ", get_process_delta_time() * 1000, "ms") -
Check entity count:
print("Total entities: ", ECS.world.entity_count) print("System count: ", ECS.world.get_system_count())
Common Fixes:
-
Too Many Entities in Broad Queries:
# ❌ Problem - Overly broad query func query(): return q.with_all([C_Position]) # Matches everything! # ✅ Solution - More specific query func query(): return q.with_all([C_Position, C_Movable]) -
Expensive Queries Rebuilt Every Frame:
# ❌ Problem - Rebuilding queries in process func process(entities: Array[Entity], components: Array, delta: float): var custom_entities = ECS.world.query.with_all([C_ComponentA]).execute() # ✅ Solution - Use the system's query() method (automatically cached) func query(): return q.with_all([C_ComponentA]) # Automatically cached by GECS func process(entities: Array[Entity], components: Array, delta: float): # Just process the entities passed in - already filtered by query for entity in entities: # Process entity...
🔧 Integration Issues
Problem: GECS Conflicts with Godot Features
Issue: Using GECS entities with Godot nodes causes problems
Solution: Choose your approach consistently:
# ✅ Approach 1 - Pure ECS (recommended for complex games)
# Entities are not nodes, use ECS for everything
var entity = Entity.new() # Not added to scene tree
entity.add_component(C_Position.new())
ECS.world.add_entity(entity)
# ✅ Approach 2 - Hybrid (good for simpler games)
# Entities are nodes, use ECS for specific systems
var entity = Entity.new()
add_child(entity) # Entity is in scene tree
entity.add_component(C_Health.new())
ECS.world.add_entity(entity)
Avoid: Mixing approaches inconsistently in the same project.
Problem: GECS Not Working After Scene Changes
Symptoms: Systems stop working when changing scenes
Solution: Properly reinitialize ECS in new scenes:
# In each main scene script
func _ready():
# Create new world for this scene
var world = World.new()
add_child(world)
ECS.world = world
# Systems are usually managed via scene composition
# See default_systems.tscn for organization
# Create your entities
setup_entities()
Prevention: Always initialize ECS properly in each scene that uses it.
🛠️ Debugging Tools
Enable Debug Logging
Add to your project settings or main script:
# Enable GECS debug output
ECS.set_debug_level(ECS.DEBUG_VERBOSE)
# This will show:
# - Entity creation/destruction
# - Component additions/removals
# - System processing information
# - Query execution details
Entity Inspector Tool
Create a debug tool to inspect entities at runtime:
# DebugPanel.gd
extends Control
func _on_inspect_button_pressed():
var entities = ECS.world.get_all_entities()
print("=== ENTITY INSPECTOR ===")
for i in range(min(10, entities.size())): # Show first 10
var entity = entities[i]
print("Entity ", i, ":")
print(" Components: ", entity.components.keys())
print(" Groups: ", entity.get_groups())
# Show component values
for comp_path in entity.components.keys():
var comp = entity.components[comp_path]
print(" ", comp_path, ": ", comp)
📚 Getting More Help
Community Resources
- Discord: Join our community for help and discussions
- GitHub Issues: Report bugs
- Documentation: Complete Guide
Before Asking for Help
Include this information in your question:
- GECS version you're using
- Godot version you're using
- Minimal code example that reproduces the issue
- Error messages (full text, not paraphrased)
- Expected vs actual behavior
Still Stuck?
If this guide doesn't solve your problem:
- Check the examples in Getting Started
- Review best practices in Best Practices
- Search GitHub issues for similar problems
- Create a minimal reproduction and ask for help
"Every bug is a learning opportunity. The key is knowing where to look and what questions to ask."