GFramework.Godot
Godot 引擎深度集成 - 为 GFramework 框架提供原生的 Godot 支持
GFramework.Godot 是 GFramework 框架的 Godot 特定实现,将框架的架构优势与 Godot 引擎的强大功能完美结合。
📋 目录
概述
GFramework.Godot 提供了与 Godot 引擎的深度集成,让开发者能够在保持 GFramework 架构优势的同时,充分利用 Godot 的节点系统、信号机制和场景管理功能。
核心设计理念
- 无缝集成:框架生命周期与 Godot 节点生命周期自动同步
- 类型安全:保持 GFramework 的强类型特性
- 性能优化:零额外开销的 Godot 集成
- 开发效率:丰富的扩展方法简化常见操作
核心特性
🎯 架构生命周期绑定
- 自动将框架初始化与 Godot 场景树绑定
- 支持节点销毁时的自动清理
- 阶段式架构初始化与 Godot
_Ready周期同步
🔧 丰富的 Node 扩展方法
- 50+ 个实用扩展方法
- 安全的节点操作和验证
- 流畅的场景树遍历和查找
- 简化的输入处理
📡 流畅的信号 API
- 类型安全的信号连接
- 链式调用支持
- 自动生命周期管理
- Godot 信号与框架事件系统的桥接
🏊♂️ 高效的节点池化
- 专用的 Node 对象池
- 自动回收和重用机制
- 内存友好的高频节点创建/销毁
📦 智能资源管理
- 简化的 Godot 资源加载
- 类型安全的资源工厂
- 缓存和预加载支持
📝 Godot 原生日志
- 与 Godot 日志系统完全集成
- 框架日志自动输出到 Godot 控制台
- 可配置的日志级别
架构集成
Architecture 基类
csharp
using GFramework.Godot.architecture;
public class GameArchitecture : AbstractArchitecture
{
protected override void Init()
{
// 注册核心模型
RegisterModel(new PlayerModel());
RegisterModel(new GameModel());
// 注册系统
RegisterSystem(new CombatSystem());
RegisterSystem(new AudioSystem());
// 注册工具类
RegisterUtility(new StorageUtility());
RegisterUtility(new ResourceLoadUtility());
}
protected override void InstallModules()
{
// 安装 Godot 特定模块
InstallGodotModule(new InputModule());
InstallGodotModule(new AudioModule());
}
}Godot 模块系统
csharp
using GFramework.Godot.architecture;
[ContextAware]
[Log]
public partial class AudioModule : AbstractGodotModule
{
// 模块节点本身可以作为 Godot 节点
public override Node Node => this;
public override void Install(IArchitecture architecture)
{
// 注册音频相关系统
architecture.RegisterSystem(new AudioSystem());
architecture.RegisterUtility(new AudioUtility());
}
public override void OnAttach(Architecture architecture)
{
// 模块附加时的初始化
Logger.Info("Audio module attached to architecture");
}
public override void OnDetach(Architecture architecture)
{
// 模块分离时的清理
Logger.Info("Audio module detached from architecture");
}
// 响应架构生命周期阶段
public override void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
{
switch (phase)
{
case ArchitecturePhase.Ready:
// 架构准备就绪,可以开始播放背景音乐
PlayBackgroundMusic();
break;
}
}
}Controller 集成
csharp
using GFramework.Godot.extensions;
[ContextAware]
[Log]
public partial class PlayerController : Node, IController
{
private PlayerModel _playerModel;
public override void _Ready()
{
// 获取模型引用
_playerModel = Context.GetModel<PlayerModel>();
// 注册事件监听,自动与节点生命周期绑定
this.RegisterEvent<PlayerInputEvent>(OnPlayerInput)
.UnRegisterWhenNodeExitTree(this);
// 监听属性变化
_playerModel.Health.Register(OnHealthChanged)
.UnRegisterWhenNodeExitTree(this);
}
private void OnPlayerInput(PlayerInputEvent e)
{
// 处理玩家输入
switch (e.Action)
{
case "move_left":
MovePlayer(-1, 0);
break;
case "move_right":
MovePlayer(1, 0);
break;
case "attack":
Context.SendCommand(new AttackCommand());
break;
}
}
private void OnHealthChanged(int newHealth)
{
// 更新 UI
var healthBar = GetNode<ProgressBar>("UI/HealthBar");
healthBar.Value = newHealth;
// 播放音效
if (newHealth < _playerModel.PreviousHealth)
PlayHurtSound();
}
}Node 扩展方法
GFramework.Godot 提供了 50+ 个 Node 扩展方法,大大简化了 Godot 开发中的常见操作。
🔍 节点查找与验证
csharp
// 安全的节点获取
var player = GetNodeX<Player>("Player"); // 自动 null 检查和类型转换
var child = FindChildX<Player>("Player"); // 递归查找子节点
// 节点验证
if (IsValidNode(player))
{
// 节点有效且在场景树中
}
// 安全的节点遍历
this.ForEachChild<Node>(child => {
GD.Print($"Found child: {child.Name}");
});🌊 流畅的场景树操作
csharp
// 安全的添加子节点
var bullet = bulletScene.Instantiate<Bullet>();
AddChildX(bullet);
// 等待节点准备就绪
await bullet.WaitUntilReady();
// 获取父节点
var parent = GetParentX<GameLevel>();
// 安全的节点移除
bullet.QueueFreeX(); // 等效于 QueueFree() 但带有验证
bullet.FreeX(); // 立即释放(谨慎使用)🎮 输入处理简化
csharp
// 输入处理
SetInputAsHandled(); // 标记输入已处理
DisableInput(); // 禁用输入
EnableInput(); // 启用输入
// 输入状态检查
if (Input.IsActionJustPressed("jump"))
{
Jump();
}🔄 异步操作支持
csharp
// 等待信号
await ToSignal(this, SignalName.Ready);
// 等待条件满足
await WaitUntil(() => IsReady);
// 等待帧结束
await WaitUntilProcessFrame();
// 延迟执行
await WaitUntilTimeout(2.0f);信号系统
SignalBuilder 流畅 API
csharp
using GFramework.Godot.extensions;
// 基础信号连接
this.ConnectSignal(Button.SignalName.Pressed, OnButtonPressed);
// 流畅的信号构建
this.CreateSignalBuilder(Timer.SignalName.Timeout)
.WithFlags(ConnectFlags.OneShot) // 单次触发
.CallImmediately() // 立即调用一次
.Connect(OnTimerTimeout)
.UnRegisterWhenNodeExitTree(this);
// 多信号连接
this.CreateSignalBuilder()
.AddSignal(Button.SignalName.Pressed, OnButtonPressed)
.AddSignal(Button.SignalName.MouseEntered, OnButtonHover)
.AddSignal(Button.SignalName.MouseExited, OnButtonExit)
.UnRegisterWhenNodeExitTree(this);信号与框架事件桥接
csharp
[ContextAware]
[Log]
public partial class UIController : Node, IController
{
public override void _Ready()
{
// Godot 信号 -> 框架事件
this.CreateSignalBuilder(Button.SignalName.Pressed)
.Connect(() => {
Context.SendEvent(new UIButtonClickEvent { ButtonId = "start_game" });
})
.UnRegisterWhenNodeExitTree(this);
// 框架事件 -> Godot 信号
this.RegisterEvent<HealthChangeEvent>(OnHealthChanged)
.UnRegisterWhenNodeExitTree(this);
}
private void OnHealthChanged(HealthChangeEvent e)
{
// 更新 Godot UI
var healthBar = GetNode<ProgressBar>("HealthBar");
healthBar.Value = e.NewHealth;
// 发送 Godot 信号
EmitSignal(SignalName.HealthUpdated, e.NewHealth);
}
[Signal]
public delegate void HealthUpdatedEventHandler(int newHealth);
}节点池化
AbstractNodePoolSystem 使用
csharp
using GFramework.Godot.pool;
public class BulletPoolSystem : AbstractNodePoolSystem<string, Bullet>
{
private PackedScene _bulletScene;
public BulletPoolSystem()
{
_bulletScene = GD.Load<PackedScene>("res://scenes/Bullet.tscn");
}
protected override Bullet CreateItem(string key)
{
return _bulletScene.Instantiate<Bullet>();
}
protected override void OnSpawn(Bullet item, string key)
{
// 重置子弹状态
item.Reset();
item.Position = Vector3.Zero;
item.Visible = true;
}
protected override void OnDespawn(Bullet item)
{
// 隐藏子弹
item.Visible = false;
// 移除父节点
item.GetParent()?.RemoveChild(item);
}
protected override bool CanDespawn(Bullet item)
{
// 只有不在使用中的子弹才能回收
return !item.IsActive;
}
}池化系统使用
csharp
[ContextAware]
[Log]
public partial class WeaponController : Node, IController
{
private BulletPoolSystem _bulletPool;
protected override void OnInit()
{
_bulletPool = Context.GetSystem<BulletPoolSystem>();
}
public void Shoot(Vector3 direction)
{
// 从池中获取子弹
var bullet = _bulletPool.Spawn("standard");
if (bullet != null)
{
// 设置子弹参数
bullet.Direction = direction;
bullet.Speed = 10.0f;
// 添加到场景
GetTree().Root.AddChild(bullet);
// 注册碰撞检测
this.RegisterEvent<BulletCollisionEvent>(e => {
if (e.Bullet == bullet)
{
// 回收子弹
_bulletPool.Despawn(bullet);
}
}).UnRegisterWhenNodeExitTree(this);
}
}
}资源管理
ResourceLoadUtility 使用
csharp
using GFramework.Godot.assets;
[ContextAware]
[Log]
public partial class ResourceManager : Node, IController
{
private ResourceLoadUtility _resourceLoader;
protected override void OnInit()
{
_resourceLoader = new ResourceLoadUtility();
}
public T LoadResource<T>(string path) where T : Resource
{
return _resourceLoader.LoadResource<T>(path);
}
public async Task<T> LoadResourceAsync<T>(string path) where T : Resource
{
return await _resourceLoader.LoadResourceAsync<T>(path);
}
public void PreloadResources()
{
// 预加载常用资源
_resourceLoader.PreloadResource<Texture2D>("res://textures/player.png");
_resourceLoader.PreloadResource<AudioStream>("res://audio/shoot.wav");
_resourceLoader.PreloadResource<PackedScene>("res://scenes/enemy.tscn");
}
}自定义资源工厂
csharp
public class GameResourceFactory : AbstractResourceFactoryUtility
{
protected override void RegisterFactories()
{
RegisterFactory<PlayerData>(CreatePlayerData);
RegisterFactory<WeaponConfig>(CreateWeaponConfig);
RegisterFactory<LevelData>(CreateLevelData);
}
private PlayerData CreatePlayerData(string path)
{
var config = LoadJson<PlayerConfig>(path);
return new PlayerData
{
MaxHealth = config.MaxHealth,
Speed = config.Speed,
JumpForce = config.JumpForce
};
}
private WeaponConfig CreateWeaponConfig(string path)
{
var data = LoadJson<WeaponJsonData>(path);
return new WeaponConfig
{
Damage = data.Damage,
FireRate = data.FireRate,
BulletPrefab = LoadResource<PackedScene>(data.BulletPath)
};
}
}日志系统
GodotLogger 使用
csharp
using GFramework.Godot.logging;
[ContextAware]
[Log] // 自动生成 Logger 字段
public partial class GameController : Node, IController
{
public override void _Ready()
{
// 使用自动生成的 Logger
Logger.Info("Game controller ready");
try
{
InitializeGame();
Logger.Info("Game initialized successfully");
}
catch (Exception ex)
{
Logger.Error($"Failed to initialize game: {ex.Message}");
}
}
public void StartGame()
{
Logger.Debug("Starting game");
// 发送游戏开始事件
Context.SendEvent(new GameStartEvent());
Logger.Info("Game started");
}
public void PauseGame()
{
Logger.Info("Game paused");
Context.SendEvent(new GamePauseEvent());
}
}日志配置
csharp
public class GodotLoggerFactoryProvider : ILoggerFactoryProvider
{
public ILoggerFactory CreateFactory()
{
return new GodotLoggerFactory(new LoggerProperties
{
MinLevel = LogLevel.Debug,
IncludeTimestamp = true,
IncludeCallerInfo = true
});
}
}
// 在架构初始化时配置日志
public class GameArchitecture : AbstractArchitecture
{
protected override void Init()
{
// 配置 Godot 日志工厂
LoggerProperties = new LoggerProperties
{
LoggerFactoryProvider = new GodotLoggerFactoryProvider(),
MinLevel = LogLevel.Info
};
// 注册组件...
}
}完整示例
简单射击游戏示例
csharp
// 1. 定义架构
public class ShooterGameArchitecture : AbstractArchitecture
{
protected override void Init()
{
// 注册模型
RegisterModel(new PlayerModel());
RegisterModel(new GameModel());
RegisterModel(new ScoreModel());
// 注册系统
RegisterSystem(new PlayerControllerSystem());
RegisterSystem(new BulletPoolSystem());
RegisterSystem(new EnemySpawnSystem());
RegisterSystem(new CollisionSystem());
// 注册工具
RegisterUtility(new StorageUtility());
RegisterUtility(new ResourceLoadUtility());
}
}
// 2. 玩家控制器
[ContextAware]
[Log]
public partial class PlayerController : CharacterBody2D, IController
{
private PlayerModel _playerModel;
public override void _Ready()
{
_playerModel = Context.GetModel<PlayerModel>();
// 输入处理
SetProcessInput(true);
// 注册事件
this.RegisterEvent<PlayerDamageEvent>(OnDamage)
.UnRegisterWhenNodeExitTree(this);
}
public override void _Process(double delta)
{
var inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
Velocity = inputDir * _playerModel.Speed.Value;
MoveAndSlide();
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed("shoot"))
{
Shoot();
}
}
private void Shoot()
{
if (CanShoot())
{
var bulletPool = Context.GetSystem<BulletPoolSystem>();
var bullet = bulletPool.Spawn("player");
if (bullet != null)
{
var direction = GetGlobalMousePosition() - GlobalPosition;
bullet.Initialize(GlobalPosition, direction.Normalized());
GetTree().Root.AddChild(bullet);
Context.SendEvent(new BulletFiredEvent());
}
}
}
private void OnDamage(PlayerDamageEvent e)
{
_playerModel.Health.Value -= e.Damage;
if (_playerModel.Health.Value <= 0)
{
Die();
}
}
private void Die()
{
Logger.Info("Player died");
Context.SendEvent(new PlayerDeathEvent());
QueueFreeX();
}
}
// 3. 主场景
[ContextAware]
[Log]
public partial class MainScene : Node2D
{
private ShooterGameArchitecture _architecture;
public override void _Ready()
{
// 初始化架构
_architecture = new ShooterGameArchitecture();
_architecture.Initialize();
// 创建玩家
var playerScene = GD.Load<PackedScene>("res://scenes/Player.tscn");
var player = playerScene.Instantiate<PlayerController>();
AddChild(player);
// 注册全局事件
this.RegisterEvent<PlayerDeathEvent>(OnPlayerDeath)
.UnRegisterWhenNodeExitTree(this);
this.RegisterEvent<GameWinEvent>(OnGameWin)
.UnRegisterWhenNodeExitTree(this);
Logger.Info("Game started");
}
private void OnPlayerDeath(PlayerDeathEvent e)
{
Logger.Info("Game over");
ShowGameOverScreen();
}
private void OnGameWin(GameWinEvent e)
{
Logger.Info("Victory!");
ShowVictoryScreen();
}
private void ShowGameOverScreen()
{
var gameOverScene = GD.Load<PackedScene>("res://ui/GameOver.tscn");
var gameOverUI = gameOverScene.Instantiate<Control>();
AddChild(gameOverUI);
}
private void ShowVictoryScreen()
{
var victoryScene = GD.Load<PackedScene>("res://ui/Victory.tscn");
var victoryUI = victoryScene.Instantiate<Control>();
AddChild(victoryUI);
}
}最佳实践
🏗️ 架构设计最佳实践
1. 模块化设计
csharp
// 好的做法:按功能分组模块
public class AudioModule : AbstractGodotModule { }
public class InputModule : AbstractGodotModule { }
public class UIModule : AbstractGodotModule { }
// 避免的功能过于庞大的单一模块
public class GameModule : AbstractGodotModule // ❌ 太大
{
// 音频、输入、UI、逻辑全部混在一起
}2. 生命周期管理
csharp
// 好的做法:使用自动清理
this.RegisterEvent<GameEvent>(OnGameEvent)
.UnRegisterWhenNodeExitTree(this);
model.Property.Register(OnPropertyChange)
.UnRegisterWhenNodeExitTree(this);
// 避免手动管理清理
private IUnRegister _eventRegister;
public override void _Ready()
{
_eventRegister = this.RegisterEvent<GameEvent>(OnGameEvent);
}
public override void _ExitTree()
{
_eventRegister?.UnRegister(); // 容易忘记
}🎮 Godot 集成最佳实践
1. 节点安全操作
csharp
// 好的做法:使用安全扩展
var player = GetNodeX<Player>("Player");
var child = FindChildX<HealthBar>("HealthBar");
// 避免的直接节点访问
var player = GetNode<Player>("Player"); // 可能抛出异常2. 信号连接模式
csharp
// 好的做法:使用 SignalBuilder
this.CreateSignalBuilder(Button.SignalName.Pressed)
.UnRegisterWhenNodeExitTree(this)
.Connect(OnButtonPressed);
// 避免的原始方式
Button.Pressed += OnButtonPressed; // 容易忘记清理🏊♂️ 性能优化最佳实践
1. 节点池化策略
csharp
// 好的做法:高频创建对象使用池化
public class BulletPool : AbstractNodePoolSystem<string, Bullet>
{
// 为不同类型的子弹创建不同的池
}
// 避免的频繁创建销毁
public void Shoot()
{
var bullet = bulletScene.Instantiate(); // ❌ 性能问题
// ...
bullet.QueueFree();
}2. 资源预加载
csharp
// 好的做法:预加载常用资源
public override void _Ready()
{
var resourceLoader = new ResourceLoadUtility();
resourceLoader.PreloadResource<Texture2D>("res://textures/bullet.png");
resourceLoader.PreloadResource<AudioStream>("res://audio/shoot.wav");
}🔧 调试和错误处理
1. 日志使用策略
csharp
// 好的做法:分级别记录
Logger.Debug($"Player position: {Position}"); // 调试信息
Logger.Info("Game started"); // 重要状态
Logger.Warning($"Low health: {_playerModel.Health}"); // 警告
Logger.Error($"Failed to load resource: {path}"); // 错误
// 避免的过度日志
Logger.Debug($"Frame: {Engine.GetProcessFrames()}"); // 太频繁2. 异常处理
csharp
// 好的做法:优雅的错误处理
public T LoadResource<T>(string path) where T : Resource
{
try
{
return GD.Load<T>(path);
}
catch (Exception ex)
{
Logger.Error($"Failed to load resource {path}: {ex.Message}");
return GetDefaultResource<T>();
}
}性能特性
📊 内存管理
- 节点池化:减少 GC 压力,提高频繁创建/销毁对象的性能
- 资源缓存:自动缓存已加载的 Godot 资源
- 生命周期管理:自动清理事件监听器和资源引用
⚡ 运行时性能
- 零分配:扩展方法避免不必要的对象分配
- 编译时优化:Source Generators 减少运行时开销
- 类型安全:编译时类型检查,避免运行时错误
🔄 异步支持
- 信号等待:使用
await ToSignal()简化异步代码 - 条件等待:
WaitUntil()和WaitUntilTimeout()简化异步逻辑 - 场景树等待:
WaitUntilReady()确保节点准备就绪
依赖关系
mermaid
graph TD
A[GFramework.Godot] --> B[GFramework.Game]
A --> C[GFramework.Game.Abstractions]
A --> D[GFramework.Core.Abstractions]
A --> E[Godot.SourceGenerators]
A --> F[GodotSharpEditor]版本兼容性
- Godot: 4.6
- .NET: 6.0+
- GFramework.Core: 与 Core 模块版本保持同步