---
name: godot-ecs-component
description: Godot 4 ECS实体组件系统模式，包含组件化设计、数据与逻辑分离、组件通信。
---

# Godot ECS 组件系统设计

## 概述

ECS（Entity Component System）是一种面向数据的架构模式，将游戏对象拆分为 Entity（实体）、Component（组件）和 System（系统）三个部分。在 Godot 中，我们通过组合 Node 和 Resource 实现 ECS 模式。

## 1. 架构设计

### 1.1 组件层级结构

```
┌─────────────────────────────────────────┐
│              Entity (Node)               │
│  - 角色节点、敌人节点、可交互对象          │
├─────────────────────────────────────────┤
│            Component (Node)              │
│  - HealthComponent、BuffComponent        │
│  - 可挂载到任意 Entity 上                 │
├─────────────────────────────────────────┤
│            Data (Resource)               │
│  - HealthData、WeaponData                │
│  - 可序列化、便于保存和复用               │
└─────────────────────────────────────────┘
```

### 1.2 核心组件示例

```gdscript
# health_component.gd
class_name HealthComponent
extends Node

signal health_changed(current: int, maximum: int)
signal damaged(amount: int, source: Node)
signal healed(amount: int)
signal died

@export var max_health: int = 100
@export var invincibility_time: float = 0.0

var current_health: int:
    set(value):
        var old := current_health
        current_health = clampi(value, 0, max_health)
        if current_health != old:
            health_changed.emit(current_health, max_health)

var _invincible: bool = false

func _ready() -> void:
    current_health = max_health

func take_damage(amount: int, source: Node = null) -> int:
    if _invincible or current_health <= 0:
        return 0

    var actual := mini(amount, current_health)
    current_health -= actual
    damaged.emit(actual, source)

    if current_health <= 0:
        died.emit()
    elif invincibility_time > 0:
        _start_invincibility()

    return actual

func heal(amount: int) -> int:
    var actual := mini(amount, max_health - current_health)
    current_health += actual
    if actual > 0:
        healed.emit(actual)
    return actual

func _start_invincibility() -> void:
    _invincible = true
    await get_tree().create_timer(invincibility_time).timeout
    _invincible = false
```

## 2. Hitbox 与 Hurtbox 系统

### 2.1 HitboxComponent

```gdscript
# hitbox_component.gd
class_name HitboxComponent
extends Area2D

signal hit(hurtbox: HurtboxComponent)

@export var damage: int = 10
@export var knockback_force: float = 200.0

var owner_node: Node

func _ready() -> void:
    owner_node = get_parent()
    area_entered.connect(_on_area_entered)

func _on_area_entered(area: Area2D) -> void:
    if area is HurtboxComponent:
        var hurtbox := area as HurtboxComponent
        if hurtbox.owner_node != owner_node:
            hit.emit(hurtbox)
            hurtbox.receive_hit(self)
```

### 2.2 HurtboxComponent

```gdscript
# hurtbox_component.gd
class_name HurtboxComponent
extends Area2D

signal hurt(hitbox: HitboxComponent)

@export var health_component: HealthComponent

var owner_node: Node

func _ready() -> void:
    owner_node = get_parent()

func receive_hit(hitbox: HitboxComponent) -> void:
    hurt.emit(hitbox)

    if health_component:
        health_component.take_damage(hitbox.damage, hitbox.owner_node)
```

## 3. 组件场景结构

```
Player (CharacterBody2D)
├── HealthComponent
│   └── (接收伤害，管理生命值)
├── HitboxComponent
│   └── (检测攻击，发送伤害)
├── HurtboxComponent
│   └── (接收伤害，触发健康组件)
├── BuffComponent
│   └── (管理增益/减益效果)
└── InventoryComponent
    └── (管理物品栏)
```

## 4. 组件设计原则

| 原则 | 说明 |
|------|------|
| **单一职责** | 每个组件只负责一个功能 |
| **松耦合** | 组件通过信号通信，不直接依赖 |
| **可复用** | 同一组件可挂载到不同实体 |
| **数据分离** | 数据存储在 Resource 中 |

## 5. 数据资源模式

```gdscript
# weapon_data.gd
class_name WeaponData
extends Resource

@export var name: StringName
@export var damage: int
@export var attack_speed: float
@export var range: float
@export_multiline var description: String
@export var icon: Texture2D
@export var projectile_scene: PackedScene
@export var sound_attack: AudioStream
```

```gdscript
# character_stats.gd
class_name CharacterStats
extends Resource

signal stat_changed(stat_name: StringName, new_value: float)

@export var max_health: float = 100.0
@export var attack: float = 10.0
@export var defense: float = 5.0
@export var speed: float = 200.0

var _current_health: float

func _init() -> void:
    _current_health = max_health

func get_current_health() -> float:
    return _current_health

func take_damage(amount: float) -> float:
    var actual_damage := maxf(amount - defense, 1.0)
    _current_health = maxf(_current_health - actual_damage, 0.0)
    stat_changed.emit("health", _current_health)
    return actual_damage

func duplicate_for_runtime() -> CharacterStats:
    var copy := duplicate() as CharacterStats
    copy._current_health = copy.max_health
    return copy
```

## 6. 设计模式总结

| 模式 | 应用场景 |
|------|----------|
| **组合模式** | Entity 通过组合组件构建 |
| **观察者模式** | 组件通过信号解耦通信 |
| **资源模式** | 数据存储在 Resource 中便于序列化 |
| **外观模式** | Entity 提供统一接口访问组件 |
