# 节点事件系统

节点事件系统是组件间通信的重要机制，它提供了一种松耦合的方式让组件之间进行交互。

## 基础用法

### 事件监听和触发

```typescript
@apclass("EventDemo")
class EventDemo extends Component<GameEntity> {
  start() {
    // 监听事件
    this.node.on("scoreChange", this.onScoreChange, this);

    // 触发事件
    this.node.emit("scoreChange", 100);
  }

  private onScoreChange(score: number) {
    console.log(`分数变化: ${score}`);
  }

  onDestroy() {
    // 清理事件监听
    this.node.off("scoreChange", this.onScoreChange, this);
  }
}
```

### 一次性事件监听

```typescript
@apclass("OneTimeEventDemo")
class OneTimeEventDemo extends Component<GameEntity> {
  start() {
    // 只监听一次事件
    this.node.once("gameStart", () => {
      console.log("游戏开始！");
    });
  }
}
```

## 全局事件系统

### 使用全局事件总线

```typescript
import { Emitter, Component } from "@dao3fun/component";

@apclass("GlobalEventDemo")
class GlobalEventDemo extends Component<GameEntity> {
  start() {
    // 监听全局事件
    Emitter.on("playerJoin", this.onPlayerJoin, this);

    // 触发全局事件
    Emitter.emit("playerJoin", { id: 1, name: "玩家1" });
  }

  private onPlayerJoin(player: any) {
    console.log(`玩家加入: ${player.name}`);
  }

  onDestroy() {
    // 清理全局事件监听
    Emitter.off("playerJoin", this.onPlayerJoin, this);
  }
}
```

## 实战示例

### 游戏状态管理系统

```typescript
// 游戏状态管理器
@apclass("GameStateManager")
class GameStateManager extends Component<GameEntity> {
  private score: number = 0;
  private gameState: "idle" | "playing" | "paused" | "over" = "idle";

  start() {
    // 监听游戏相关事件
    this.node.on("startGame", this.startGame, this);
    this.node.on("pauseGame", this.pauseGame, this);
    this.node.on("resumeGame", this.resumeGame, this);
    this.node.on("gameOver", this.gameOver, this);
    this.node.on("addScore", this.addScore, this);
  }

  private startGame() {
    this.gameState = "playing";
    this.score = 0;
    this.node.emit("gameStateChanged", this.gameState);
  }

  private pauseGame() {
    this.gameState = "paused";
    this.node.emit("gameStateChanged", this.gameState);
  }

  private resumeGame() {
    this.gameState = "playing";
    this.node.emit("gameStateChanged", this.gameState);
  }

  private gameOver() {
    this.gameState = "over";
    this.node.emit("gameStateChanged", this.gameState);
    this.node.emit("finalScore", this.score);
  }

  private addScore(points: number) {
    this.score += points;
    this.node.emit("scoreUpdated", this.score);
  }

  onDestroy() {
    // 清理所有事件监听
    this.node.off("startGame", this.startGame, this);
    this.node.off("pauseGame", this.pauseGame, this);
    this.node.off("resumeGame", this.resumeGame, this);
    this.node.off("gameOver", this.gameOver, this);
    this.node.off("addScore", this.addScore, this);
  }
}

// UI 控制器
@apclass("UIController")
class UIController extends Component<GameEntity> {
  start() {
    // 监听游戏状态变化
    this.node.on("gameStateChanged", this.updateUI, this);
    this.node.on("scoreUpdated", this.updateScore, this);
    this.node.on("finalScore", this.showGameOver, this);
  }

  private updateUI(state: string) {
    console.log(`更新UI状态: ${state}`);
  }

  private updateScore(score: number) {
    console.log(`更新分数显示: ${score}`);
  }

  private showGameOver(finalScore: number) {
    console.log(`游戏结束，最终分数: ${finalScore}`);
  }

  onDestroy() {
    this.node.off("gameStateChanged", this.updateUI, this);
    this.node.off("scoreUpdated", this.updateScore, this);
    this.node.off("finalScore", this.showGameOver, this);
  }
}
```

## 最佳实践

### 1. 事件命名规范

```typescript
// 使用动词+名词的形式
this.node.emit("playerSpawn"); // ✅ 好的命名
this.node.emit("spawnPlayer"); // ✅ 好的命名
this.node.emit("player"); // ❌ 不好的命名

// 使用驼峰命名法
this.node.emit("scoreUpdate"); // ✅ 好的命名
this.node.emit("score_update"); // ❌ 不好的命名
```

### 2. 事件参数传递

```typescript
@apclass("EventParamsDemo")
class EventParamsDemo extends Component<GameEntity> {
  start() {
    // 传递单个参数
    this.node.emit("scoreChange", 100);

    // 传递多个参数
    this.node.emit("playerUpdate", {
      health: 100,
      position: { x: 0, y: 0, z: 0 },
      status: "active",
    });
  }
}
```

### 3. 事件清理

```typescript
@apclass("EventCleanupDemo")
class EventCleanupDemo extends Component<GameEntity> {
  private handlers: Map<string, Function> = new Map();

  start() {
    // 保存事件处理函数的引用
    const handler = (data: any) => {
      console.log(data);
    };

    this.node.on("myEvent", handler, this);
    this.handlers.set("myEvent", handler);
  }

  onDestroy() {
    // 清理所有事件监听
    this.handlers.forEach((handler, event) => {
      this.node.off(event, handler, this);
    });
    this.handlers.clear();
  }
}
```

## 注意事项

1. **内存泄漏防范**

   - 始终在组件销毁时清理事件监听
   - 使用 Map 或数组记录添加的事件监听器
   - 避免重复添加相同的事件监听

2. **性能优化**

   - 避免过于频繁的事件触发
   - 合理使用事件参数，避免传递过大的数据
   - 考虑使用事件节流或防抖

3. **事件传播**
   - 明确事件的作用范围
   - 合理选择使用节点事件还是全局事件
   - 避免事件循环调用

## 常见问题

### Q: 如何避免事件监听器重复添加？

A: 可以在添加前先移除旧的监听器：

```typescript
this.node.off("myEvent", this.handler, this);
this.node.on("myEvent", this.handler, this);
```

### Q: 全局事件和节点事件该如何选择？

A:

- 节点事件：组件间的局部通信
- 全局事件：跨节点的系统级通信

### Q: 如何处理事件监听器的内存泄漏？

A:

1. 在 onDestroy 中清理所有事件
2. 使用 once 代替 on 处理一次性事件
3. 保持对事件监听器的引用以便清理
