basic ECS spawner
This commit is contained in:
341
addons/gecs/docs/GETTING_STARTED.md
Normal file
341
addons/gecs/docs/GETTING_STARTED.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Getting Started with GECS
|
||||
|
||||
> **Build your first ECS project in 5 minutes**
|
||||
|
||||
This guide will walk you through creating a simple player entity with health and transform components using GECS. By the end, you'll understand the core concepts and have a working example.
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
- Godot 4.x installed
|
||||
- Basic GDScript knowledge
|
||||
- 5 minutes of your time
|
||||
|
||||
## ⚡ Step 1: Setup (1 minute)
|
||||
|
||||
### Install GECS
|
||||
|
||||
1. **Download GECS** and place it in your project's `addons/` folder
|
||||
2. **Enable the plugin**: Go to `Project > Project Settings > Plugins` and enable "GECS"
|
||||
3. **Verify setup**: The ECS singleton should be automatically added to AutoLoad
|
||||
|
||||
> 💡 **Quick Check**: If you see errors, make sure `ECS` appears in `Project > Project Settings > AutoLoad`
|
||||
|
||||
## 🎮 Step 2: Your First Entity (2 minutes)
|
||||
|
||||
Entities in GECS extend Godot's `Node` class. You have two options for creating entities:
|
||||
|
||||
### **Option A: Scene-based Entities** (For spatial properties)
|
||||
|
||||
Use this when you need access to `Node3D` or `Node2D` properties like position, rotation, scale, or want to add visual children (sprites, meshes, etc.).
|
||||
|
||||
> ⚠️ **Key Point**: `Entity` extends `Node` (not `Node3D` or `Node2D`), so create a scene with the appropriate spatial node type as the root, then attach your entity script to it.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Create a new scene** in Godot:
|
||||
- Click `Scene > New Scene` or press `Ctrl+N`
|
||||
- Select **"Node3D"** as the root node type (for 3D games) or **"Node2D"** (for 2D games)
|
||||
- Rename the root node to `Player`
|
||||
|
||||
2. **Attach the entity script**:
|
||||
- With the root node selected, click the "Attach Script" button (📄+ icon)
|
||||
- Save as `e_player.gd`
|
||||
|
||||
3. **Save the scene**:
|
||||
- Save as `e_player.tscn` in your scenes folder
|
||||
|
||||
**File: `e_player.gd`**
|
||||
|
||||
```gdscript
|
||||
# e_player.gd
|
||||
class_name Player
|
||||
extends Entity
|
||||
|
||||
func on_ready():
|
||||
# Sync the entity's scene position to the Transform component
|
||||
if has_component(C_Transform):
|
||||
var c_trs = get_component(C_Transform) as C_Transform
|
||||
c_trs.position = self.global_position
|
||||
```
|
||||
|
||||
> 💡 **Use case**: Players, enemies, projectiles, or anything that needs a position in your game world.
|
||||
|
||||
### **Option B: Code-based Entities** (Pure data containers)
|
||||
|
||||
Use this when you DON'T need spatial properties and just want a pure data container (e.g., game managers, abstract systems, timers).
|
||||
|
||||
```gdscript
|
||||
# Just extend Entity directly
|
||||
class_name GameManager
|
||||
extends Entity
|
||||
|
||||
# No scene needed - instantiate with GameManager.new()
|
||||
```
|
||||
|
||||
> 💡 **Use case**: Game state managers, quest trackers, inventory systems, or any non-spatial game logic.
|
||||
|
||||
---
|
||||
|
||||
**For this tutorial**, we'll use **Option A** (scene-based) since we want our player to move around the screen with a position.
|
||||
|
||||
## 📦 Step 3: Your First Components (1 minute)
|
||||
|
||||
Components hold data. Let's create health and transform components:
|
||||
|
||||
**File: `c_health.gd`**
|
||||
|
||||
```gdscript
|
||||
# c_health.gd
|
||||
class_name C_Health
|
||||
extends Component
|
||||
|
||||
@export var current: float = 100.0
|
||||
@export var maximum: float = 100.0
|
||||
|
||||
func _init(max_health: float = 100.0):
|
||||
maximum = max_health
|
||||
current = max_health
|
||||
```
|
||||
|
||||
**File: `c_transform.gd`**
|
||||
|
||||
```gdscript
|
||||
# c_transform.gd
|
||||
class_name C_Transform
|
||||
extends Component
|
||||
|
||||
@export var position: Vector3 = Vector3.ZERO
|
||||
|
||||
func _init(pos: Vector3 = Vector3.ZERO):
|
||||
position = pos
|
||||
```
|
||||
|
||||
**File: `c_velocity.gd`**
|
||||
|
||||
```gdscript
|
||||
# c_velocity.gd
|
||||
class_name C_Velocity
|
||||
extends Component
|
||||
|
||||
@export var velocity: Vector3 = Vector3.ZERO
|
||||
|
||||
func _init(vel: Vector3 = Vector3.ZERO):
|
||||
velocity = vel
|
||||
```
|
||||
|
||||
> 💡 **Key Principle**: Components only hold data, never logic. Think of them as data containers.
|
||||
> ⚠️ **Important Note**: Components `_init` function requires that all arguments have a default value or Godot will crash.
|
||||
|
||||
## ⚙️ Step 4: Your First System (1 minute)
|
||||
|
||||
Systems contain the logic that operates on entities with specific components. This system moves entities across the screen:
|
||||
|
||||
**File: `s_movement.gd`**
|
||||
|
||||
```gdscript
|
||||
# s_movement.gd
|
||||
class_name MovementSystem
|
||||
extends System
|
||||
|
||||
func query():
|
||||
# Find all entities that have both transform and velocity
|
||||
return q.with_all([C_Transform, C_Velocity])
|
||||
|
||||
func process(entities: Array[Entity], components: Array, delta: float):
|
||||
# Process each entity in the array
|
||||
for entity in entities:
|
||||
var c_trs = entity.get_component(C_Transform) as C_Transform
|
||||
var c_velocity = entity.get_component(C_Velocity) as C_Velocity
|
||||
|
||||
# Move the entity based on its velocity
|
||||
c_trs.position += c_velocity.velocity * delta
|
||||
|
||||
# Update the actual entity position in the scene
|
||||
entity.global_position = c_trs.position
|
||||
|
||||
# Bounce off screen edges (simple example)
|
||||
if c_trs.position.x > 10 or c_trs.position.x < -10:
|
||||
c_velocity.velocity.x *= -1
|
||||
```
|
||||
|
||||
> 💡 **System Logic**: Query finds entities with required components, process() runs the movement logic on each entity every frame.
|
||||
|
||||
## 🎬 Step 5: See It Work (1 minute)
|
||||
|
||||
Now let's put it all together in a main scene:
|
||||
|
||||
### Create Main Scene
|
||||
|
||||
1. **Create a new scene** with a `Node` as the root
|
||||
2. **Add a World node** as a child (Add Child Node > search for "World")
|
||||
3. **Attach this script** to the root node:
|
||||
|
||||
**File: `main.gd`**
|
||||
|
||||
```gdscript
|
||||
# main.gd
|
||||
extends Node
|
||||
|
||||
@onready var world: World = $World
|
||||
|
||||
func _ready():
|
||||
ECS.world = world
|
||||
|
||||
# Load and instantiate the player entity scene
|
||||
var player_scene = preload("res://e_player.tscn") # Adjust path as needed
|
||||
var e_player = player_scene.instantiate() as Player
|
||||
|
||||
# Add components to the entity
|
||||
e_player.add_components([
|
||||
C_Health.new(100),
|
||||
C_Transform.new(),
|
||||
C_Velocity.new(Vector3(2, 0, 0)) # Move right at 2 units/second
|
||||
])
|
||||
|
||||
add_child(e_player) # Add to scene tree
|
||||
ECS.world.add_entity(e_player) # Add to ECS world
|
||||
|
||||
# Create the movement system
|
||||
var movement_system = MovementSystem.new()
|
||||
ECS.world.add_system(movement_system)
|
||||
|
||||
func _process(delta):
|
||||
# Process all systems
|
||||
if ECS.world:
|
||||
ECS.process(delta)
|
||||
```
|
||||
|
||||
**Run your project!** 🎉 You now have a working ECS setup where the player entity moves across the screen and bounces off the edges! The MovementSystem updates entity positions based on their velocity components.
|
||||
|
||||
> 💡 **Scene-based entities**: Notice we load and instantiate the `e_player.tscn` scene instead of calling `Player.new()`. This is required because we need access to spatial properties (position). For entities that don't need spatial properties, `Entity.new()` works fine.
|
||||
|
||||
## 🎯 What You Just Built
|
||||
|
||||
Congratulations! You've created your first ECS project with:
|
||||
|
||||
- **Entity**: Player - a container for components
|
||||
- **Components**: C_Health, C_Transform, C_Velocity - pure data containers
|
||||
- **System**: MovementSystem - logic that moves entities based on velocity
|
||||
- **World**: Container that manages entities and systems
|
||||
|
||||
## 📈 Next Steps
|
||||
|
||||
Now that you have the basics working, here's how to level up:
|
||||
|
||||
### 1. Create Entity Prefabs (Recommended)
|
||||
|
||||
Instead of creating entities in code, use Godot's scene system:
|
||||
|
||||
1. **Create a new scene** with your Entity class as the root node
|
||||
2. **Add visual children** (MeshInstance3D, Sprite3D, etc.)
|
||||
3. **Add components via define_components()** or `component_resources` array in Inspector
|
||||
4. **Save as .tscn file** (e.g., `e_player.tscn`)
|
||||
5. **Load and instantiate** in your main scene
|
||||
|
||||
```gdscript
|
||||
# Improved e_player.gd with define_components()
|
||||
class_name Player
|
||||
extends Entity
|
||||
|
||||
func define_components() -> Array:
|
||||
return [
|
||||
C_Health.new(100),
|
||||
C_Transform.new(),
|
||||
C_Velocity.new(Vector3(1, 0, 0)) # Move right slowly
|
||||
]
|
||||
|
||||
func on_ready():
|
||||
# Sync scene position to component
|
||||
if has_component(C_Transform):
|
||||
var c_trs = get_component(C_Transform) as C_Transform
|
||||
c_trs.position = self.global_position
|
||||
```
|
||||
|
||||
### 2. Organize Your Main Scene
|
||||
|
||||
Structure your main scene using the proven scene-based pattern:
|
||||
|
||||
```
|
||||
Main.tscn
|
||||
├── World (World node)
|
||||
├── DefaultSystems (instantiated from default_systems.tscn)
|
||||
│ ├── input (SystemGroup)
|
||||
│ ├── gameplay (SystemGroup)
|
||||
│ ├── physics (SystemGroup)
|
||||
│ └── ui (SystemGroup)
|
||||
├── Level (Node3D for static environment)
|
||||
└── Entities (Node3D for spawned entities)
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- **Visual organization** in Godot editor
|
||||
- **Easy system reordering** between groups
|
||||
- **Reusable system configurations**
|
||||
|
||||
### 3. Learn More Patterns
|
||||
|
||||
### 🧠 Understand the Concepts
|
||||
|
||||
**→ [Core Concepts Guide](CORE_CONCEPTS.md)** - Deep dive into Entities, Components, Systems, and Relationships
|
||||
|
||||
### 🔧 Add More Features
|
||||
|
||||
Try adding these to your moving player:
|
||||
|
||||
- **Input system** - Add C_Input component and system to control movement with arrow keys
|
||||
- **Multiple entities** - Create more moving objects with different velocities
|
||||
- **Collision system** - Add C_Collision component and detect when entities hit each other
|
||||
- **Gravity system** - Add downward velocity to make entities fall
|
||||
|
||||
### 📚 Learn Best Practices
|
||||
|
||||
**→ [Best Practices Guide](BEST_PRACTICES.md)** - Write maintainable ECS code
|
||||
|
||||
### 🔧 Explore Advanced Features
|
||||
|
||||
- **[Component Queries](COMPONENT_QUERIES.md)** - Filter by component property values
|
||||
- **[Relationships](RELATIONSHIPS.md)** - Link entities together for complex interactions
|
||||
- **[Observers](OBSERVERS.md)** - Reactive systems that respond to changes
|
||||
- **[Performance Optimization](PERFORMANCE_OPTIMIZATION.md)** - Make your games run fast
|
||||
|
||||
## ❓ Having Issues?
|
||||
|
||||
### Player not responding?
|
||||
|
||||
- Check that `ECS.process(delta)` is called in `_process()`
|
||||
- Verify components are added to the entity via `define_components()` or Inspector
|
||||
- Make sure the system is added to the world
|
||||
- Ensure transform synchronization is called in entity's `on_ready()`
|
||||
|
||||
### Can't access position/rotation properties?
|
||||
|
||||
- ⚠️ **Entity extends Node, not Node3D**: To access spatial properties, create a scene with `Node3D` (3D) or `Node2D` (2D) as the root node type
|
||||
- Attach your entity script (that extends `Entity`) to the Node3D/Node2D root
|
||||
- Load and instantiate the scene file (don't use `.new()` for spatial entities)
|
||||
- **If you don't need spatial properties**: Using `Entity.new()` is perfectly fine for pure data containers
|
||||
- See Step 2 for both entity creation approaches
|
||||
|
||||
### Errors in console?
|
||||
|
||||
- Check that all classes extend the correct base class
|
||||
- Verify file names match class names
|
||||
- Ensure GECS plugin is enabled
|
||||
|
||||
**Still stuck?** → [Troubleshooting Guide](TROUBLESHOOTING.md)
|
||||
|
||||
## 🏆 What's Next?
|
||||
|
||||
You're now ready to build amazing games with GECS! The Entity-Component-System pattern will help you:
|
||||
|
||||
- **Scale your game** - Add features without breaking existing code
|
||||
- **Reuse code** - Components and systems work across different entity types
|
||||
- **Debug easier** - Clear separation between data and logic
|
||||
- **Optimize performance** - GECS handles efficient querying for you
|
||||
|
||||
**Ready to dive deeper?** Start with [Core Concepts](CORE_CONCEPTS.md) to really understand what makes ECS powerful.
|
||||
|
||||
**Need help?** [Join our Discord community](https://discord.gg/eB43XU2tmn) for support and discussions.
|
||||
|
||||
---
|
||||
|
||||
_"The best way to learn ECS is to build with it. Start simple, then add complexity as you understand the patterns."_
|
||||
Reference in New Issue
Block a user