GeWuYou b835d3af67 docs(site): 实现多语言支持并重构文档站点配置
- 添加简体中文本地化配置和导航链接
- 配置本地搜索功能并设置中文翻译
- 添加 Catch-all 404 重定向中间件
- 重构侧边栏和导航结构以支持国际化
- 移除旧的英文文档内容并更新配置
- 添加页脚导航和面包屑文字本地化
2026-02-11 12:52:14 +08:00

12 KiB
Raw Blame History

Events 包使用说明

概述

Events 包提供了一套完整的事件系统实现了观察者模式Observer Pattern。通过事件系统可以实现组件间的松耦合通信支持无参和带参事件、事件注册/注销、以及灵活的事件组合。

核心接口

1. IEvent

基础事件接口,定义了事件注册的基本功能。

核心方法:

IUnRegister Register(Action onEvent);  // 注册事件处理函数

2. IUnRegister

注销接口,用于取消事件注册。

核心方法:

void UnRegister();  // 执行注销操作

3. [

IUnRegisterList](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegisterList.cs#L6-L10)

注销列表接口,用于批量管理注销对象。

属性:

IList<IUnRegister> UnregisterList { get; }  // 获取注销列表

4. IEventBus

事件总线接口,提供基于类型的事件发送和注册。

核心方法:

IUnRegister Register<T>(Action<T> onEvent);  // 注册类型化事件
void Send<T>(T e);                         // 发送事件实例
void Send<T>() where T : new();           // 发送事件(自动创建实例)

核心类

1. EasyEvent

无参事件类,支持注册、注销和触发无参事件。

使用示例:

// 创建事件
var onClicked = new EasyEvent();

// 注册监听
var unregister = onClicked.Register(() => 
{
    GD.Print("Button clicked!");
});

// 触发事件
onClicked.Trigger();

// 取消注册
unregister.UnRegister();

2. Event<T>

单参数泛型事件类,支持一个参数的事件。

使用示例:

// 创建带参数的事件
var onScoreChanged = new Event<int>();

// 注册监听
onScoreChanged.Register(newScore => 
{
    GD.Print($"Score changed to: {newScore}");
});

// 触发事件并传递参数
onScoreChanged.Trigger(100);

3. Event<T, TK>

双参数泛型事件类。

使用示例:

// 伤害事件:攻击者、伤害值
var onDamageDealt = new Event<string, int>();

onDamageDealt.Register((attacker, damage) =>
{
    GD.Print($"{attacker} dealt {damage} damage!");
});

onDamageDealt.Trigger("Player", 50);

4. EasyEvents

全局事件管理器,提供类型安全的事件注册和获取。

使用示例:

// 注册全局事件类型
EasyEvents.Register<GameStartEvent>();

// 获取事件实例
var gameStartEvent = EasyEvents.Get<GameStartEvent>();

// 注册监听
gameStartEvent.Register(() => 
{
    GD.Print("Game started!");
});

// 触发事件
gameStartEvent.Trigger();

5. EventBus

类型化事件系统,支持基于类型的事件发送和注册。

使用示例:

// 使用全局事件系统
var eventBus = new EventBus();

// 注册类型化事件
eventBus.Register<PlayerDiedEvent>(e => 
{
    GD.Print($"Player died at position: {e.Position}");
});

// 发送事件(传递实例)
eventBus.Send(new PlayerDiedEvent 
{ 
    Position = new Vector3(10, 0, 5) 
});

// 发送事件(自动创建实例)
eventBus.Send<PlayerDiedEvent>();

6. DefaultUnRegister

默认注销器实现,封装注销回调。

使用示例:

Action onUnregister = () => GD.Print("Unregistered");
var unregister = new DefaultUnRegister(onUnregister);

// 执行注销
unregister.UnRegister();

7. OrEvent

事件或运算组合器,当任意一个事件触发时触发。

使用示例:

var onAnyInput = new OrEvent()
    .Or(onKeyPressed)
    .Or(onMouseClicked)
    .Or(onTouchDetected);

// 当上述任意事件触发时,执行回调
onAnyInput.Register(() => 
{
    GD.Print("Input detected!");
});

8. UnRegisterList

批量管理注销对象的列表。

使用示例:

var unregisterList = new UnRegisterList();

// 添加到列表
someEvent.Register(OnEvent).AddToUnregisterList(unregisterList);

// 批量注销
unregisterList.UnRegisterAll();

9. ArchitectureEvents

定义了架构生命周期相关的事件。

包含事件:

  • ArchitectureLifecycleReadyEvent - 架构生命周期准备就绪
  • ArchitectureDestroyingEvent - 架构销毁中
  • ArchitectureDestroyedEvent - 架构已销毁
  • ArchitectureFailedInitializationEvent - 架构初始化失败

在架构中使用事件

定义事件类

// 简单事件
public struct GameStartedEvent { }

// 带数据的事件
public struct PlayerDiedEvent
{
    public Vector3 Position;
    public string Cause;
}

// 复杂事件
public struct LevelCompletedEvent
{
    public int LevelId;
    public float CompletionTime;
    public int Score;
    public List<string> Achievements;
}

Model 中发送事件

public class PlayerModel : AbstractModel
{
    public BindableProperty<int> Health { get; } = new(100);
    
    protected override void OnInit()
    {
        // 监听生命值变化
        Health.Register(newHealth =>
        {
            if (newHealth <= 0)
            {
                // 发送玩家死亡事件
                this.SendEvent(new PlayerDiedEvent
                {
                    Position = Position,
                    Cause = "Health depleted"
                });
            }
        });
    }
}

System 中发送事件

public class CombatSystem : AbstractSystem
{
    protected override void OnInit() { }
    
    public void DealDamage(Character attacker, Character target, int damage)
    {
        target.Health -= damage;
        
        // 发送伤害事件
        this.SendEvent(new DamageDealtEvent
        {
            Attacker = attacker.Name,
            Target = target.Name,
            Damage = damage
        });
    }
}

Controller 中注册事件

public partial class GameController : Node, IController
{
    private IUnRegisterList _unregisterList = new UnRegisterList();
    
    public IArchitecture GetArchitecture() => GameArchitecture.Interface;
    
    public override void _Ready()
    {
        // 注册多个事件
        this.RegisterEvent<GameStartedEvent>(OnGameStarted)
            .AddToUnregisterList(_unregisterList);
            
        this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
            .AddToUnregisterList(_unregisterList);
            
        this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted)
            .AddToUnregisterList(_unregisterList);
    }
    
    private void OnGameStarted(GameStartedEvent e)
    {
        GD.Print("Game started!");
    }
    
    private void OnPlayerDied(PlayerDiedEvent e)
    {
        GD.Print($"Player died at {e.Position}: {e.Cause}");
        ShowGameOverScreen();
    }
    
    private void OnLevelCompleted(LevelCompletedEvent e)
    {
        GD.Print($"Level {e.LevelId} completed! Score: {e.Score}");
        ShowVictoryScreen(e);
    }
    
    public override void _ExitTree()
    {
        _unregisterList.UnRegisterAll();
    }
}

高级用法

1. 事件链式组合

// 使用 Or 组合多个事件
var onAnyDamage = new OrEvent()
    .Or(onPhysicalDamage)
    .Or(onMagicDamage)
    .Or(onPoisonDamage);

onAnyDamage.Register(() => 
{
    PlayDamageSound();
});

2. 事件过滤

// 只处理高伤害事件
this.RegisterEvent<DamageDealtEvent>(e =>
{
    if (e.Damage >= 50)
    {
        ShowCriticalHitEffect();
    }
});

3. 事件转发

public class EventBridge : AbstractSystem
{
    protected override void OnInit()
    {
        // 将内部事件转发为公共事件
        this.RegisterEvent<InternalPlayerDiedEvent>(e =>
        {
            this.SendEvent(new PublicPlayerDiedEvent
            {
                PlayerId = e.Id,
                Timestamp = DateTime.Now
            });
        });
    }
}

4. 临时事件监听

public class TutorialController : Node, IController
{
    public override void _Ready()
    {
        // 只监听一次
        IUnRegister unregister = null;
        unregister = this.RegisterEvent<FirstEnemyKilledEvent>(e =>
        {
            ShowTutorialComplete();
            unregister?.UnRegister();  // 立即注销
        });
    }
}

5. 条件事件

public class AchievementSystem : AbstractSystem
{
    private int _killCount = 0;
    
    protected override void OnInit()
    {
        this.RegisterEvent<EnemyKilledEvent>(e =>
        {
            _killCount++;
            
            // 条件满足时发送成就事件
            if (_killCount >= 100)
            {
                this.SendEvent(new AchievementUnlockedEvent
                {
                    AchievementId = "kill_100_enemies"
                });
            }
        });
    }
}

生命周期管理

使用 UnRegisterList

public class MyController : Node, IController
{
    // 统一管理所有注销对象
    private IUnRegisterList _unregisterList = new UnRegisterList();
    
    public override void _Ready()
    {
        // 所有注册都添加到列表
        this.RegisterEvent<Event1>(OnEvent1)
            .AddToUnregisterList(_unregisterList);
            
        this.RegisterEvent<Event2>(OnEvent2)
            .AddToUnregisterList(_unregisterList);
    }
    
    public override void _ExitTree()
    {
        // 一次性注销所有
        _unregisterList.UnRegisterAll();
    }
}

使用 Godot 节点生命周期

public override void _Ready()
{
    // 当节点退出场景树时自动注销
    this.RegisterEvent<GameEvent>(OnGameEvent)
        .UnRegisterWhenNodeExitTree(this);
}

最佳实践

  1. 事件命名规范

    • 使用过去式:PlayerDiedEventLevelCompletedEvent
    • 使用 Event 后缀:便于识别
    • 使用结构体:减少内存分配
  2. 事件数据设计

    • 只包含必要信息
    • 使用值类型struct提高性能
    • 避免传递可变引用
  3. 避免事件循环

    • 事件处理器中谨慎发送新事件
    • 使用命令打破循环依赖
  4. 合理使用事件

    • 用于通知状态变化
    • 用于跨模块通信
    • 不用于返回数据(使用 Query
  5. 注销管理

    • 始终注销事件监听
    • 使用 IUnRegisterList 批量管理
    • 利用 Godot 节点生命周期
  6. 性能考虑

    • 避免频繁触发的事件(如每帧)
    • 事件处理器保持轻量
    • 使用结构体事件减少 GC

事件 vs 其他通信方式

方式 适用场景 优点 缺点
Event 状态变化通知、跨模块通信 松耦合、一对多 难以追踪调用链
Command 执行操作、修改状态 封装逻辑、可撤销 单向通信
Query 查询数据 职责清晰、有返回值 同步调用
BindableProperty UI 数据绑定 自动更新、响应式 仅限单一属性

相关包