Skip to content

GFramework.Core 核心框架

一个基于 CQRS、MVC 和事件驱动的轻量级游戏开发架构框架

目录

框架概述

本框架是一个与平台无关的轻量级架构,它结合了多种经典设计模式:

  • MVC 架构模式 - 清晰的层次划分
  • CQRS 模式 - 命令查询职责分离
  • IoC/DI - 依赖注入和控制反转
  • 事件驱动 - 松耦合的组件通信
  • 响应式编程 - 可绑定属性和数据流
  • 阶段式生命周期管理 - 精细化的架构状态控制

重要说明:GFramework.Core 是与平台无关的核心模块,不包含任何 Godot 特定代码。Godot 集成功能在 GFramework.Godot 包中实现。

核心特性

  • 清晰的分层架构 - Model、View、Controller、System、Utility 各司其职
  • 类型安全 - 基于泛型的组件获取和事件系统
  • 松耦合 - 通过事件和接口实现组件解耦
  • 易于测试 - 依赖注入和纯函数设计
  • 可扩展 - 基于接口的规则体系
  • 生命周期管理 - 自动的注册和注销机制
  • 模块化 - 支持架构模块安装
  • 平台无关 - Core 模块可以在任何 .NET 环境中使用

核心概念

五层架构

┌─────────────────────────────────────────┐
│             View / UI                    │  UI 层:用户界面
├─────────────────────────────────────────┤
│            Controller                    │  控制层:连接 UI 和业务逻辑
├─────────────────────────────────────────┤
│             System                       │  逻辑层:业务逻辑
├─────────────────────────────────────────┤
│              Model                       │  数据层:游戏状态
├─────────────────────────────────────────┤
│             Utility                      │  工具层:无状态工具
└─────────────────────────────────────────┘

横切关注点

Command ──┐
Query   ──┼──→  跨层操作(修改/查询数据)
Event   ──┘

架构阶段

初始化:Init → BeforeUtilityInit → AfterUtilityInit → BeforeModelInit → AfterModelInit → BeforeSystemInit → AfterSystemInit → Ready
销毁:Destroy → Destroying → Destroyed

架构图

整体架构

                     ┌──────────────────┐
                     │   Architecture   │ ← 管理所有组件
                     └────────┬─────────┘

         ┌────────────────────┼────────────────────┐
         │                    │                    │
     ┌───▼────┐          ┌───▼────┐          ┌───▼─────┐
     │ Model  │          │ System │          │ Utility │
     │  层    │          │  层    │          │  层     │
     └───┬────┘          └───┬────┘          └────────┘
         │                   │
         │    ┌─────────────┤
         │    │             │
     ┌───▼────▼───┐    ┌───▼──────┐
     │ Controller │    │ Command/ │
     │    层      │    │  Query   │
     └─────┬──────┘    └──────────┘

     ┌─────▼─────┐
     │   View    │
     │    UI     │
     └───────────┘

数据流向

用户输入 → Controller → Command → System → Model → Event → Controller → View 更新

查询流程:Controller → Query → Model → 返回数据

快速开始

本框架采用"约定优于配置"的设计理念,只需 4 步即可搭建完整的架构。

为什么需要这个框架?

在传统开发中,我们经常遇到这些问题:

  • 代码耦合严重:UI 直接访问游戏逻辑,逻辑直接操作 UI
  • 难以维护:修改一个功能需要改动多个文件
  • 难以测试:业务逻辑和 UI 混在一起无法独立测试
  • 难以复用:代码紧密耦合,无法在其他项目中复用

本框架通过清晰的分层解决这些问题。

1. 定义架构(Architecture)

作用:Architecture 是整个应用的"中央调度器",负责管理所有组件的生命周期。

csharp
using GFramework.Core.architecture;

public class GameArchitecture : Architecture
{
    protected override void Init()
    {
        // 注册 Model - 游戏数据
        RegisterModel(new PlayerModel());
        
        // 注册 System - 业务逻辑
        RegisterSystem(new CombatSystem());
        
        // 注册 Utility - 工具类
        RegisterUtility(new StorageUtility());
    }
}

优势

  • 依赖注入:组件通过上下文获取架构引用
  • 集中管理:所有组件注册在一处,一目了然
  • 生命周期管理:自动初始化和销毁
  • 平台无关:可以在任何 .NET 环境中使用

2. 定义 Model(数据层)

作用:Model 是应用的"数据库",只负责存储和管理状态。

csharp
public class PlayerModel : AbstractModel
{
    // 使用 BindableProperty 实现响应式数据
    public BindableProperty<int> Health { get; } = new(100);
    public BindableProperty<int> Gold { get; } = new(0);
    
    protected override void OnInit()
    {
        // Model 中可以监听自己的数据变化
        Health.Register(hp =>
        {
            if (hp <= 0) this.SendEvent(new PlayerDiedEvent());
        });
    }
}

// 也可以不使用 BindableProperty
public class PlayerModel : AbstractModel
{
    public int Health { get; private set; }
    public int Gold { get; private set; }
    
    protected override void OnInit()
    {
        Health = 100;
        Gold = 0;
    }
}

优势

  • 数据响应式:BindableProperty 让数据变化自动通知监听者
  • 职责单一:只存储数据,不包含复杂业务逻辑
  • 易于测试:可以独立测试数据逻辑

3. 定义 System(业务逻辑层)

作用:System 是应用的"大脑",处理所有业务逻辑。

csharp
public class CombatSystem : AbstractSystem
{
    protected override void OnInit()
    {
        // System 通过事件驱动,响应游戏中的各种事件
        this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
    }
    
    private void OnEnemyAttack(EnemyAttackEvent e)
    {
        var playerModel = this.GetModel<PlayerModel>();
        
        // 处理业务逻辑:计算伤害、更新数据
        playerModel.Health.Value -= e.Damage;
        
        // 发送事件通知其他组件
        this.SendEvent(new PlayerTookDamageEvent { Damage = e.Damage });
    }
}

优势

  • 事件驱动:通过事件解耦,不同 System 之间松耦合
  • 可组合:多个 System 协同工作,每个专注自己的领域
  • 易于扩展:新增功能只需添加新的 System 和事件监听

4. 定义 Controller(控制层)

作用:Controller 是"桥梁",连接 UI 和业务逻辑。

csharp
public class PlayerController : IController
{
    // 通过依赖注入获取架构
    private readonly IArchitecture _architecture;
    
    public PlayerController(IArchitecture architecture)
    {
        _architecture = architecture;
    }
    
    // 监听模型变化
    public void Initialize()
    {
        var playerModel = _architecture.GetModel<PlayerModel>();
        
        // 数据绑定:Model 数据变化自动更新 UI
        playerModel.Health.RegisterWithInitValue(OnHealthChanged);
    }
    
    private void OnHealthChanged(int hp)
    {
        // 更新 UI 显示
        UpdateHealthDisplay(hp);
    }
    
    private void UpdateHealthDisplay(int hp) { /* UI 更新逻辑 */ }
}

优势

  • 自动更新 UI:通过 BindableProperty,数据变化自动反映到界面
  • 分离关注点:UI 逻辑和业务逻辑完全分离
  • 易于测试:可以通过依赖注入模拟架构进行测试

完成!现在你有了一个完整的架构

这 4 步完成后,你就拥有了:

  • 清晰的数据层(Model)
  • 独立的业务逻辑(System)
  • 灵活的控制层(Controller)
  • 统一的生命周期管理(Architecture)

下一步该做什么?

  1. 添加 Command:封装用户操作(如购买物品、使用技能)
  2. 添加 Query:封装数据查询(如查询背包物品数量)
  3. 添加更多 System:如任务系统、背包系统、商店系统
  4. 使用 Utility:添加工具类(如存档工具、数学工具)
  5. 使用模块:通过 IArchitectureModule 扩展架构功能

包说明

包名职责文档
architecture架构核心,管理所有组件生命周期查看
constants框架常量定义本文档
model数据模型层,存储状态查看
system业务逻辑层,处理业务规则查看
controller控制器层,连接视图和逻辑(在 Abstractions 中)
utility工具类层,提供无状态工具查看
command命令模式,封装写操作查看
query查询模式,封装读操作查看
events事件系统,组件间通信查看
property可绑定属性,响应式编程查看
iocIoC 容器,依赖注入查看
rule规则接口,定义组件约束查看
extensions扩展方法,简化 API 调用查看
logging日志系统,记录运行日志查看
environment环境接口,提供运行环境信息查看

组件联动

1. 初始化流程

创建 Architecture 实例
    └─> Init()
        ├─> RegisterModel → Model.SetContext() → Model.Init()
        ├─> RegisterSystem → System.SetContext() → System.Init()
        └─> RegisterUtility → Utility 注册到容器

2. Command 执行流程

Controller.SendCommand(command)
    └─> command.Execute()
        └─> command.OnDo()  // 子类实现
            ├─> GetModel<T>()    // 获取数据
            ├─> 修改 Model 数据
            └─> SendEvent()      // 发送事件

3. Event 传播流程

组件.SendEvent(event)
    └─> TypeEventSystem.Send(event)
        └─> 通知所有订阅者
            ├─> Controller 响应 → 更新 UI
            ├─> System 响应 → 执行逻辑
            └─> Model 响应 → 更新状态

4. BindableProperty 数据绑定

Model: BindableProperty<int> Health = new(100);
Controller: Health.RegisterWithInitValue(hp => UpdateUI(hp))
修改值: Health.Value = 50 → 触发所有回调 → 更新 UI

最佳实践

1. 分层职责原则

每一层都有明确的职责边界,遵循这些原则能让代码更清晰、更易维护。

Model 层

csharp
// 好:只存储数据
public class PlayerModel : AbstractModel
{
    public BindableProperty<int> Health { get; } = new(100);
    protected override void OnInit() { }
}

// 坏:包含业务逻辑
public class PlayerModel : AbstractModel
{
    public void TakeDamage(int damage)  // 业务逻辑应在 System
    {
        Health.Value -= damage;
        if (Health.Value <= 0) Die();
    }
}

System 层

csharp
// 好:处理业务逻辑
public class CombatSystem : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<AttackEvent>(OnAttack);
    }
    
    private void OnAttack(AttackEvent e)
    {
        var target = this.GetModel<PlayerModel>();
        int finalDamage = CalculateDamage(e.BaseDamage, target);
        target.Health.Value -= finalDamage;
    }
}

2. 通信方式选择指南

通信方式使用场景优势
Command用户操作、修改状态可撤销、可记录
Query查询数据、检查条件明确只读意图
Event通知其他组件松耦合、可扩展
BindableProperty数据变化通知自动化、不会遗漏

3. 生命周期管理

为什么需要注销?

忘记注销监听器会导致:

  • 内存泄漏:对象无法被 GC 回收
  • 逻辑错误:已销毁的对象仍在响应事件
csharp
// 使用 UnRegisterList 统一管理
private IUnRegisterList _unregisterList = new UnRegisterList();

public void Initialize()
{
    this.RegisterEvent<Event1>(OnEvent1)
        .AddToUnregisterList(_unregisterList);
    
    model.Property.Register(OnPropertyChanged)
        .AddToUnregisterList(_unregisterList);
}

public void Cleanup()
{
    _unregisterList.UnRegisterAll();
}

4. 性能优化技巧

csharp
// 低效:每帧都查询
var model = _architecture.GetModel<PlayerModel>();  // 频繁调用

// 高效:缓存引用
private PlayerModel _playerModel;

public void Initialize()
{
    _playerModel = _architecture.GetModel<PlayerModel>();  // 只查询一次
}

设计理念

框架的设计遵循 SOLID 原则和经典设计模式。

1. 单一职责原则(SRP)

  • Model:只负责存储数据
  • System:只负责处理业务逻辑
  • Controller:只负责协调和输入处理
  • Utility:只负责提供工具方法

2. 开闭原则(OCP)

  • 通过事件系统添加新功能,无需修改现有代码
  • 新的 System 可以监听现有事件,插入自己的逻辑

3. 依赖倒置原则(DIP)

  • 所有组件通过接口交互
  • 通过 IoC 容器注入依赖
  • 易于替换实现和编写测试

4. 接口隔离原则(ISP)

csharp
// 小而专注的接口
public interface ICanGetModel : IBelongToArchitecture { }
public interface ICanSendCommand : IBelongToArchitecture { }
public interface ICanRegisterEvent : IBelongToArchitecture { }

// 组合需要的能力
public interface IController :
    ICanGetModel,
    ICanSendCommand,
    ICanRegisterEvent { }

5. 组合优于继承

通过接口组合获得能力,而不是通过继承。

框架核心设计模式

设计模式应用位置解决的问题带来的好处
工厂模式IoC 容器组件的创建和管理解耦创建逻辑
观察者模式Event 系统组件间的通信松耦合通信
命令模式Command封装操作请求支持撤销重做
策略模式System不同的业务逻辑易于切换策略
依赖注入整体架构组件间的依赖自动管理依赖
模板方法Abstract 类定义算法骨架统一流程规范

平台无关性

  • GFramework.Core:纯 .NET 库,无任何平台特定代码
  • GFramework.Godot:Godot 特定实现,包含 Node 扩展、GodotLogger 等
  • 可以轻松将 Core 框架移植到其他平台(Unity、.NET MAUI 等)

版本: 1.0.0 许可证: Apache 2.0

基于 Apache 2.0 许可证发布