# 故障排除与调试
本指南帮助你诊断和解决 GFramework 使用中的常见问题。
## 如何使用本指南
1. **快速查找**:使用浏览器的搜索功能(Ctrl+F / Cmd+F)查找错误信息或关键词
2. **分类浏览**:根据问题类型(安装、架构、事件等)查看对应章节
3. **排查清单**:每个问题都提供了详细的排查步骤和解决方案
4. **代码示例**:所有解决方案都包含可运行的代码示例
## 安装问题
### NuGet 包安装失败
**问题**:无法安装 GFramework NuGet 包。
**错误信息**:
```
NU1101: Unable to find package GFramework.Core
```
**解决方案**:
1. **检查包源配置**:
```bash
# 查看当前包源
dotnet nuget list source
# 添加 NuGet.org 源
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
```
2. **清理 NuGet 缓存**:
```bash
dotnet nuget locals all --clear
```
3. **手动指定版本**:
```bash
dotnet add package GFramework.Core --version 1.0.0
```
### 依赖冲突
**问题**:安装 GFramework 后出现依赖版本冲突。
**错误信息**:
```
NU1107: Version conflict detected for Microsoft.Extensions.DependencyInjection
```
**解决方案**:
1. **检查项目文件**:
```xml
net8.0
```
2. **统一依赖版本**:
```xml
```
### .NET 版本不兼容
**问题**:项目无法编译,提示 .NET 版本不兼容。
**错误信息**:
```
error NETSDK1045: The current .NET SDK does not support targeting .NET 8.0
```
**解决方案**:
1. **检查 .NET SDK 版本**:
```bash
dotnet --version
```
2. **安装正确的 .NET SDK**:
- GFramework 需要 .NET 8.0 或更高版本
- 下载地址:https://dotnet.microsoft.com/download
3. **更新项目目标框架**:
```xml
net8.0
```
## 架构问题
### 架构初始化失败
**问题**:架构初始化时抛出异常。
**错误信息**:
```
InvalidOperationException: Architecture is already initialized
```
**原因**:重复调用 `Initialize()` 方法。
**解决方案**:
```csharp
// ❌ 错误:重复初始化
var arch = new GameArchitecture();
arch.Initialize();
arch.Initialize(); // 抛出异常
// ✅ 正确:只初始化一次
var arch = new GameArchitecture();
if (!arch.IsInitialized)
{
arch.Initialize();
}
// ✅ 更好:使用单例模式
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 注册组件
}
}
// 使用
var arch = GameArchitecture.Interface;
```
### 服务未注册
**问题**:尝试获取未注册的服务。
**错误信息**:
```
InvalidOperationException: No service for type 'IPlayerService' has been registered
```
**解决方案**:
```csharp
// ❌ 错误:未注册服务
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 忘记注册 IPlayerService
}
}
var service = arch.GetService<IPlayerService>(); // 抛出异常
// ✅ 正确:先注册服务
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册服务
RegisterService<IPlayerService, PlayerService>();
}
}
// ✅ 使用 IoC 容器注册
protected override void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IPlayerService, PlayerService>();
services.AddTransient<IGameService, GameService>();
}
```
### 组件注册顺序错误
**问题**:组件初始化时依赖的其他组件尚未注册。
**错误信息**:
```
NullReferenceException: Object reference not set to an instance of an object
```
**解决方案**:
```csharp
// ❌ 错误:SystemB 依赖 ModelA,但 ModelA 后注册
protected override void Init()
{
RegisterSystem(new SystemB()); // SystemB.OnInit() 中访问 ModelA 失败
RegisterModel(new ModelA());
}
// ✅ 正确:先注册依赖项
protected override void Init()
{
// 1. 先注册 Model
RegisterModel(new ModelA());
// 2. 再注册 System
RegisterSystem(new SystemB());
// 3. 最后注册 Utility
RegisterUtility<IConfigUtility>(new ConfigUtility());
}
// ✅ 更好:使用延迟初始化
public class SystemB : AbstractSystem
{
private ModelA _modelA;
protected override void OnInit()
{
// 在 OnInit 中获取依赖,此时所有组件已注册
_modelA = this.GetModel<ModelA>();
}
}
```
### 异步初始化问题
**问题**:异步架构初始化未正确等待。
**错误信息**:
```
InvalidOperationException: Architecture not fully initialized
```
**解决方案**:
```csharp
// ❌ 错误:未等待异步初始化完成
var arch = new AsyncGameArchitecture();
arch.InitializeAsync(); // 未等待
var model = arch.GetModel<PlayerModel>(); // 可能失败
// ✅ 正确:等待异步初始化
var arch = new AsyncGameArchitecture();
await arch.InitializeAsync();
var model = arch.GetModel<PlayerModel>(); // 安全
// ✅ 在 Godot 中使用
public partial class GameRoot : Node
{
private AsyncGameArchitecture _architecture;
public override async void _Ready()
{
_architecture = new AsyncGameArchitecture();
await _architecture.InitializeAsync();
// 现在可以安全使用架构
var model = _architecture.GetModel<PlayerModel>();
}
}
```
## 常见错误
### 1. 组件未注册错误
**错误信息**:`KeyNotFoundException: 未找到类型为 'XXX' 的组件`
**原因**:尝试获取未注册的组件。
**解决方案**:
```csharp
// ❌ 错误:未注册 PlayerModel
var arch = new GameArchitecture();
arch.Initialize();
var model = arch.GetModel(); // 抛出异常
// ✅ 正确:先注册再获取
public class GameArchitecture : Architecture
{
protected override void Init()
{
RegisterModel(new PlayerModel()); // 注册模型
}
}
```
### 2. 事件监听器未触发
**问题**:注册了事件监听器但没有被调用。
**原因**:
- 事件类型不匹配
- 监听器在事件发送前注销
- 事件发送时使用了错误的类型
- 事件传播被停止
**解决方案**:
```csharp
// ❌ 错误:事件类型不匹配
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
this.SendEvent(new PlayerAttackedEvent()); // 不会触发
// ✅ 正确:事件类型匹配
this.RegisterEvent<PlayerAttackedEvent>(OnPlayerAttacked);
this.SendEvent(new PlayerAttackedEvent()); // 正确触发
// ❌ 错误:过早注销
var unregister = this.RegisterEvent<GameEvent>(OnGameEvent);
unregister.UnRegister(); // 立即注销
this.SendEvent(new GameEvent()); // 不会触发
// ✅ 正确:在适当时机注销
private IUnRegister _eventUnregister;
public void Initialize()
{
_eventUnregister = this.RegisterEvent<GameEvent>(OnGameEvent);
}
public void Cleanup()
{
_eventUnregister?.UnRegister();
}
// ❌ 错误:事件传播被停止
this.RegisterEvent<GameEvent>(e =>
{
e.StopPropagation(); // 停止传播
});
this.RegisterEvent<GameEvent>(OnGameEvent); // 不会被调用
// ✅ 正确:使用优先级控制执行顺序
this.RegisterEvent<GameEvent>(OnGameEventFirst, priority: 100);
this.RegisterEvent<GameEvent>(OnGameEventSecond, priority: 50);
```
### 3. 内存泄漏
**问题**:应用内存持续增长。
**原因**:
- 未注销事件监听器
- 未注销属性监听器
- 未销毁 Architecture
- 循环引用
**解决方案**:
```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();
}
// ✅ 销毁架构
architecture.Destroy();
// ✅ 使用 EventListenerScope 自动管理生命周期
public partial class Player : Node
{
private EventListenerScope _eventScope;
public override void _Ready()
{
_eventScope = new EventListenerScope(this.GetArchitecture());
_eventScope.Register<PlayerDiedEvent>(OnPlayerDied);
_eventScope.Register<PlayerAttackedEvent>(OnPlayerAttacked);
}
public override void _ExitTree()
{
_eventScope.Dispose(); // 自动注销所有监听器
}
}
// ✅ 在 Godot 中使用 UnRegisterOnFree
public partial class GameController : Node
{
public override void _Ready()
{
this.RegisterEvent<GameEvent>(OnGameEvent)
.UnRegisterOnFree(this); // 节点释放时自动注销
}
}
```
### 4. 循环依赖
**问题**:两个系统相互依赖导致死循环。
**原因**:系统间直接调用而不是通过事件通信。
**解决方案**:
```csharp
// ❌ 错误:直接调用导致循环依赖
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
var systemB = this.GetSystem<SystemB>();
systemB.DoSomething(); // 可能导致循环
}
}
// ✅ 正确:使用事件解耦
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
this.SendEvent(new EventB()); // 发送事件
}
}
public class SystemB : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<EventB>(OnEventB);
}
}
// ✅ 使用 Command 模式
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
this.SendCommand(new ProcessDataCommand());
}
}
// ✅ 使用 Query 模式获取数据
public class SystemA : AbstractSystem
{
private void ProcessData()
{
var data = this.SendQuery(new GetPlayerDataQuery());
// 处理数据
}
}
```
## 事件系统问题
### 事件未触发
**问题**:发送事件后没有任何监听器响应。
**排查步骤**:
1. **检查事件类型是否正确**:
```csharp
// 确保发送和监听的是同一个事件类型
public struct PlayerDiedEvent : IEvent { }
// 注册
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
// 发送
this.SendEvent(new PlayerDiedEvent()); // 类型必须完全匹配
```
2. **检查是否在架构初始化后注册**:
```csharp
// ❌ 错误:在架构初始化前注册
var arch = new GameArchitecture();
arch.RegisterEvent<GameEvent>(OnGameEvent); // 可能失败
// ✅ 正确:在架构初始化后注册
var arch = new GameArchitecture();
arch.Initialize();
arch.RegisterEvent<GameEvent>(OnGameEvent);
```
3. **检查事件总线是否正确注册**:
```csharp
protected override void Init()
{
// 确保注册了事件总线
RegisterSystem(new EventBusModule());
}
```
### 事件执行顺序错误
**问题**:多个监听器的执行顺序不符合预期。
**解决方案**:
```csharp
// ✅ 使用优先级控制执行顺序(数值越大优先级越高)
this.RegisterEvent<GameEvent>(OnGameEventFirst, priority: 100);
this.RegisterEvent<GameEvent>(OnGameEventSecond, priority: 50);
this.RegisterEvent<GameEvent>(OnGameEventLast, priority: 0);
// ✅ 使用事件链
public struct FirstEvent : IEvent { }
public struct SecondEvent : IEvent { }
public struct ThirdEvent : IEvent { }
this.RegisterEvent<FirstEvent>(e =>
{
// 处理第一个事件
this.SendEvent(new SecondEvent()); // 触发下一个事件
});
this.RegisterEvent<SecondEvent>(e =>
{
// 处理第二个事件
this.SendEvent(new ThirdEvent()); // 触发下一个事件
});
```
### 事件传播控制
**问题**:需要在某些条件下停止事件传播。
**解决方案**:
```csharp
// ✅ 使用 StopPropagation
this.RegisterEvent<DamageEvent>(e =>
{
if (IsInvincible)
{
e.StopPropagation(); // 停止传播,后续监听器不会执行
return;
}
ApplyDamage(e.Amount);
}, priority: 100); // 高优先级先执行
// ✅ 使用条件过滤
this.RegisterEvent<DamageEvent>(e =>
{
if (e.Target != this)
return; // 不是针对自己的伤害,忽略
ApplyDamage(e.Amount);
});
```
## 协程问题
### 协程不执行
**问题**:启动协程后没有任何效果。
**原因**:
- 未正确启动协程调度器
- 协程方法没有返回 `IEnumerator`
- 忘记调用 `yield return`
**解决方案**:
```csharp
// ❌ 错误:未启动协程调度器
public partial class GameRoot : Node
{
public override void _Ready()
{
StartCoroutine(MyCoroutine()); // 不会执行
}
private IEnumerator MyCoroutine()
{
yield return new WaitForSeconds(1);
}
}
// ✅ 正确:在 Godot 中使用协程
public partial class GameRoot : Node
{
private CoroutineScheduler _scheduler;
public override void _Ready()
{
_scheduler = new CoroutineScheduler(new GodotTimeSource(this));
_scheduler.StartCoroutine(MyCoroutine());
}
public override void _Process(double delta)
{
_scheduler?.Update((float)delta);
}
private IEnumerator MyCoroutine()
{
yield return new WaitForSeconds(1);
GD.Print("Coroutine executed!");
}
}
// ❌ 错误:方法签名错误
private void MyCoroutine() // 应该返回 IEnumerator
{
// 不会作为协程执行
}
// ✅ 正确:返回 IEnumerator
private IEnumerator MyCoroutine()
{
yield return new WaitForSeconds(1);
}
// ❌ 错误:忘记 yield return
private IEnumerator MyCoroutine()
{
new WaitForSeconds(1); // 缺少 yield return
GD.Print("This executes immediately!");
}
// ✅ 正确:使用 yield return
private IEnumerator MyCoroutine()
{
yield return new WaitForSeconds(1);
GD.Print("This executes after 1 second!");
}
```
### 协程死锁
**问题**:协程永远不会完成。
**原因**:
- 等待条件永远不满足
- 循环等待
- 等待已停止的协程
**解决方案**:
```csharp
// ❌ 错误:等待条件永远不满足
private IEnumerator WaitForever()
{
yield return new WaitUntil(() => false); // 永远等待
}
// ✅ 正确:添加超时机制
private IEnumerator WaitWithTimeout()
{
float timeout = 5f;
float elapsed = 0f;
while (!condition && elapsed < timeout)
{
elapsed += Time.DeltaTime;
yield return null;
}
if (elapsed >= timeout)
{
GD.PrintErr("Timeout!");
}
}
// ✅ 使用 WaitForEventWithTimeout
private IEnumerator WaitForEventSafely()
{
yield return new WaitForEventWithTimeout<GameEvent>(
this.GetArchitecture(),
timeout: 5f
);
}
// ❌ 错误:循环等待
private IEnumerator CoroutineA()
{
yield return StartCoroutine(CoroutineB());
}
private IEnumerator CoroutineB()
{
yield return StartCoroutine(CoroutineA()); // 循环等待
}
// ✅ 正确:避免循环依赖
private IEnumerator CoroutineA()
{
yield return new WaitForSeconds(1);
this.SendEvent(new EventA());
}
private IEnumerator CoroutineB()
{
yield return new WaitForEvent<EventA>(this.GetArchitecture());
// 继续执行
}
```
### 协程提前停止
**问题**:协程在完成前被意外停止。
**解决方案**:
```csharp
// ✅ 保存协程句柄
private CoroutineHandle _coroutineHandle;
public void StartMyCoroutine()
{
_coroutineHandle = _scheduler.StartCoroutine(MyCoroutine());
}
public void StopMyCoroutine()
{
if (_coroutineHandle != null && _coroutineHandle.IsRunning)
{
_coroutineHandle.Stop();
}
}
// ✅ 检查协程状态
private IEnumerator MonitorCoroutine()
{
var handle = _scheduler.StartCoroutine(LongRunningCoroutine());
while (handle.IsRunning)
{
yield return null;
}
GD.Print($"Coroutine completed with state: {handle.State}");
}
// ✅ 使用 try-finally 确保清理
private IEnumerator CoroutineWithCleanup()
{
try
{
yield return new WaitForSeconds(1);
// 执行操作
}
finally
{
// 清理资源
Cleanup();
}
}
```
## 资源管理问题
### 资源加载失败
**问题**:无法加载资源文件。
**错误信息**:
```
FileNotFoundException: Could not find file 'res://assets/player.png'
```
**解决方案**:
```csharp
// ❌ 错误:路径错误
var texture = ResourceLoader.Load<Texture2D>("assets/player.png"); // 缺少 res://
// ✅ 正确:使用完整路径
var texture = ResourceLoader.Load<Texture2D>("res://assets/player.png");
// ✅ 检查资源是否存在
if (ResourceLoader.Exists("res://assets/player.png"))
{
var texture = ResourceLoader.Load<Texture2D>("res://assets/player.png");
}
else
{
GD.PrintErr("Resource not found!");
}
// ✅ 使用资源管理器
public class GameArchitecture : Architecture
{
protected override void Init()
{
RegisterSystem(new ResourceManager());
}
}
// 加载资源
var handle = this.GetSystem<ResourceManager>()
.LoadAsync<Texture2D>("res://assets/player.png");
await handle.Task;
if (handle.IsValid)
{
var texture = handle.Resource;
}
else
{
GD.PrintErr($"Failed to load resource: {handle.Error}");
}
```
### 资源内存泄漏
**问题**:加载的资源未释放,导致内存持续增长。
**解决方案**:
```csharp
// ❌ 错误:未释放资源
public void LoadTexture()
{
var texture = ResourceLoader.Load<Texture2D>("res://assets/player.png");
// 使用 texture
// 忘记释放
}
// ✅ 正确:使用资源句柄自动管理
private IResourceHandle<Texture2D> _textureHandle;
public void LoadTexture()
{
_textureHandle = resourceManager.Load<Texture2D>("res://assets/player.png");
}
public void Cleanup()
{
_textureHandle?.Release(); // 释放资源
}
// ✅ 使用自动释放策略
var handle = resourceManager.Load<Texture2D>(
"res://assets/player.png",
new AutoReleaseStrategy(timeToLive: 60f) // 60秒后自动释放
);
// ✅ 使用 using 语句
public async Task LoadAndUseResource()
{
using var handle = resourceManager.Load<Texture2D>("res://assets/player.png");
await handle.Task;
// 使用资源
var texture = handle.Resource;
// 离开作用域时自动释放
}
```
### 资源加载超时
**问题**:大型资源加载时间过长。
**解决方案**:
```csharp
// ✅ 使用异步加载
private async Task LoadLargeResourceAsync()
{
var handle = resourceManager.LoadAsync<PackedScene>("res://scenes/large_scene.tscn");
// 显示加载进度
while (!handle.IsDone)
{
GD.Print($"Loading: {handle.Progress * 100}%");
await Task.Delay(100);
}
if (handle.IsValid)
{
var scene = handle.Resource;
}
}
// ✅ 使用协程加载
private IEnumerator LoadLargeResourceCoroutine()
{
var handle = resourceManager.LoadAsync<PackedScene>("res://scenes/large_scene.tscn");
yield return new WaitForProgress(handle);
if (handle.IsValid)
{
var scene = handle.Resource;
}
}
// ✅ 预加载资源
public override void _Ready()
{
// 在游戏开始时预加载常用资源
PreloadResources();
}
private async void PreloadResources()
{
var resources = new[]
{
"res://assets/player.png",
"res://assets/enemy.png",
"res://sounds/bgm.ogg"
};
foreach (var path in resources)
{
await resourceManager.LoadAsync<Resource>(path).Task;
}
GD.Print("All resources preloaded!");
}
```
## Godot 集成问题
### 场景加载失败
**问题**:无法加载或实例化场景。
**错误信息**:
```
NullReferenceException: Object reference not set to an instance of an object
```
**解决方案**:
```csharp
// ❌ 错误:路径错误或场景未导出
var scene = GD.Load<PackedScene>("scenes/player.tscn"); // 缺少 res://
// ✅ 正确:使用完整路径
var scene = GD.Load<PackedScene>("res://scenes/player.tscn");
var instance = scene.Instantiate();
// ✅ 检查场景是否存在
if (ResourceLoader.Exists("res://scenes/player.tscn"))
{
var scene = GD.Load<PackedScene>("res://scenes/player.tscn");
var instance = scene.Instantiate<Player>();
AddChild(instance);
}
else
{
GD.PrintErr("Scene not found!");
}
// ✅ 使用场景路由器
public class GameSceneRouter : SceneRouterBase
{
protected override void RegisterScenes()
{
Register<MainMenuScene>("MainMenu", "res://scenes/main_menu.tscn");
Register<GameScene>("Game", "res://scenes/game.tscn");
}
}
// 导航到场景
await sceneRouter.NavigateToAsync("Game");
```
### 节点查找失败
**问题**:使用 `GetNode()` 查找节点返回 null。
**错误信息**:
```
NullReferenceException: Object reference not set to an instance of an object
```
**解决方案**:
```csharp
// ❌ 错误:路径错误
var player = GetNode<Player>("Player"); // 节点可能在子节点中
// ✅ 正确:使用完整路径
var player = GetNode<Player>("Level/Player");
// ✅ 使用 % 符号访问唯一节点(Godot 4.0+)
var player = GetNode<Player>("%Player");
// ✅ 检查节点是否存在
if (HasNode("Player"))
{
var player = GetNode<Player>("Player");
}
else
{
GD.PrintErr("Player node not found!");
}
// ✅ 使用 NodePath 缓存
[Export] private NodePath _playerPath;
private Player _player;
public override void _Ready()
{
if (_playerPath != null)
{
_player = GetNode<Player>(_playerPath);
}
}
```
### 信号连接失败
**问题**:信号未正确连接或触发。
**解决方案**:
```csharp
// ❌ 错误:信号名称错误
button.Connect("press", Callable.From(OnButtonPressed)); // 应该是 "pressed"
// ✅ 正确:使用正确的信号名称
button.Connect("pressed", Callable.From(OnButtonPressed));
// ✅ 使用类型安全的信号连接
button.Pressed += OnButtonPressed;
// ✅ 使用 GFramework 的流式 API
button.OnPressed()
.Subscribe(OnButtonPressed)
.AddToUnregisterList(_unregisterList);
// ✅ 检查信号是否存在
if (button.HasSignal("pressed"))
{
button.Connect("pressed", Callable.From(OnButtonPressed));
}
// ✅ 自动注销信号
public partial class GameController : Node
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
{
var button = GetNode<Button>("Button");
button.OnPressed()
.Subscribe(OnButtonPressed)
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
private void OnButtonPressed()
{
GD.Print("Button pressed!");
}
}
```
### 架构上下文丢失
**问题**:在 Godot 节点中无法访问架构。
**错误信息**:
```
InvalidOperationException: Architecture context not found
```
**解决方案**:
```csharp
// ❌ 错误:未设置架构锚点
public partial class Player : Node
{
public override void _Ready()
{
var model = this.GetModel<PlayerModel>(); // 失败:无架构上下文
}
}
// ✅ 正确:在根节点设置架构锚点
public partial class GameRoot : Node
{
private GameArchitecture _architecture;
public override void _Ready()
{
_architecture = new GameArchitecture();
_architecture.Initialize();
// 设置架构锚点
var anchor = new ArchitectureAnchor();
anchor.SetArchitecture(_architecture);
AddChild(anchor);
}
}
// 现在子节点可以访问架构
public partial class Player : Node
{
public override void _Ready()
{
var model = this.GetModel<PlayerModel>(); // 成功
}
}
// ✅ 使用 Godot 模块
public class GameArchitecture : AbstractArchitecture
{
protected override void Init()
{
// 注册 Godot 模块
this.RegisterGodotModule<PlayerModule>();
this.RegisterGodotModule<EnemyModule>();
}
}
```
### UI 页面导航问题
**问题**:UI 页面无法正确显示或切换。
**解决方案**:
```csharp
// ❌ 错误:未注册 UI 页面
var uiRouter = this.GetSystem<IUiRouter>();
await uiRouter.PushAsync("MainMenu"); // 失败:页面未注册
// ✅ 正确:先注册 UI 页面
public class GameArchitecture : Architecture
{
protected override void Init()
{
var uiRegistry = new GodotUiRegistry();
uiRegistry.Register("MainMenu", "res://ui/main_menu.tscn", UiLayer.Page);
uiRegistry.Register("Settings", "res://ui/settings.tscn", UiLayer.Modal);
RegisterUtility<IGodotUiRegistry>(uiRegistry);
RegisterSystem(new UiRouter(uiRegistry));
}
}
// 导航到页面
var uiRouter = this.GetSystem<IUiRouter>();
await uiRouter.PushAsync("MainMenu");
// ✅ 使用转场效果
await uiRouter.PushAsync("Settings", new UiTransitionOptions
{
TransitionType = UiTransitionType.Fade,
Duration = 0.3f
});
// ✅ 处理导航失败
try
{
await uiRouter.PushAsync("NonExistentPage");
}
catch (InvalidOperationException ex)
{
GD.PrintErr($"Navigation failed: {ex.Message}");
}
```
## 性能问题
### 事件处理缓慢
**问题**:事件处理耗时过长,导致游戏卡顿。
**诊断**:
```csharp
// 测量事件处理时间
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
arch.SendEvent(new HeavyEvent());
stopwatch.Stop();
GD.Print($"Event processing time: {stopwatch.ElapsedMilliseconds}ms");
// 使用性能分析器
public class PerformanceProfiler : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<IEvent>(e =>
{
var sw = Stopwatch.StartNew();
// 事件处理
sw.Stop();
if (sw.ElapsedMilliseconds > 16) // 超过一帧
{
GD.PrintErr($"Slow event: {e.GetType().Name} took {sw.ElapsedMilliseconds}ms");
}
});
}
}
```
**优化**:
```csharp
// ❌ 低效:在事件处理中进行复杂计算
private void OnEvent(HeavyEvent e)
{
for (int i = 0; i < 1000000; i++)
{
// 复杂计算
}
}
// ✅ 高效:异步处理
private async void OnEvent(HeavyEvent e)
{
await Task.Run(() =>
{
for (int i = 0; i < 1000000; i++)
{
// 复杂计算
}
});
}
// ✅ 使用协程分帧处理
private void OnEvent(HeavyEvent e)
{
this.StartCoroutine(ProcessHeavyEventCoroutine(e));
}
private IEnumerator ProcessHeavyEventCoroutine(HeavyEvent e)
{
const int batchSize = 1000;
for (int i = 0; i < 1000000; i += batchSize)
{
// 处理一批数据
for (int j = 0; j < batchSize && i + j < 1000000; j++)
{
// 计算
}
yield return null; // 下一帧继续
}
}
```
### 频繁的组件访问
**问题**:每帧都调用 `GetModel`、`GetSystem` 等方法导致性能下降。
**优化**:
```csharp
// ❌ 低效:每帧访问
public override void _Process(double delta)
{
var model = this.GetModel<PlayerModel>(); // 每帧调用
model.Health.Value -= 1;
}
// ✅ 高效:缓存引用
private PlayerModel _playerModel;
public override void _Ready()
{
_playerModel = this.GetModel<PlayerModel>(); // 只调用一次
}
public override void _Process(double delta)
{
_playerModel.Health.Value -= 1;
}
// ✅ 使用延迟初始化
private PlayerModel _playerModel;
private PlayerModel PlayerModel => _playerModel ??= this.GetModel<PlayerModel>();
public override void _Process(double delta)
{
PlayerModel.Health.Value -= 1;
}
```
### 内存占用过高
**问题**:游戏运行时内存持续增长。
**诊断**:
```csharp
// 监控内存使用
public class MemoryMonitor : Node
{
public override void _Process(double delta)
{
var memoryUsed = GC.GetTotalMemory(false) / 1024 / 1024;
GD.Print($"Memory used: {memoryUsed} MB");
if (memoryUsed > 500) // 超过 500MB
{
GD.PrintErr("High memory usage detected!");
}
}
}
```
**优化**:
```csharp
// ✅ 使用对象池
public class BulletPool : AbstractNodePoolSystem<Bullet>
{
protected override Bullet CreateInstance()
{
var scene = GD.Load<PackedScene>("res://entities/bullet.tscn");
return scene.Instantiate<Bullet>();
}
protected override void OnGet(Bullet bullet)
{
bullet.Show();
bullet.ProcessMode = ProcessModeEnum.Inherit;
}
protected override void OnRelease(Bullet bullet)
{
bullet.Hide();
bullet.ProcessMode = ProcessModeEnum.Disabled;
}
}
// 使用对象池
var bullet = bulletPool.Get();
// 使用完毕后归还
bulletPool.Release(bullet);
// ✅ 及时释放资源
public override void _ExitTree()
{
// 注销事件监听器
_unregisterList.UnRegisterAll();
// 释放资源句柄
_textureHandle?.Release();
// 停止协程
_coroutineHandle?.Stop();
// 销毁架构(如果是根节点)
_architecture?.Destroy();
}
// ✅ 定期执行垃圾回收
private float _gcTimer = 0f;
private const float GC_INTERVAL = 60f; // 每60秒
public override void _Process(double delta)
{
_gcTimer += (float)delta;
if (_gcTimer >= GC_INTERVAL)
{
_gcTimer = 0f;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
```
### 协程性能问题
**问题**:大量协程导致性能下降。
**优化**:
```csharp
// ❌ 低效:创建大量短期协程
for (int i = 0; i < 1000; i++)
{
StartCoroutine(ShortCoroutine());
}
// ✅ 高效:合并协程
StartCoroutine(BatchCoroutine(1000));
private IEnumerator BatchCoroutine(int count)
{
for (int i = 0; i < count; i++)
{
// 处理
if (i % 10 == 0)
yield return null; // 每10次操作暂停一次
}
}
// ✅ 使用协程池
private readonly Queue<IEnumerator> _coroutineQueue = new();
private IEnumerator CoroutinePoolRunner()
{
while (true)
{
if (_coroutineQueue.Count > 0)
{
var coroutine = _coroutineQueue.Dequeue();
yield return coroutine;
}
else
{
yield return null;
}
}
}
// 添加到队列而不是立即启动
_coroutineQueue.Enqueue(MyCoroutine());
```
## 调试技巧
### 1. 启用日志系统
```csharp
// 使用 GFramework 日志系统
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册日志工厂
RegisterUtility<ILoggerFactory>(new GodotLoggerFactory());
}
}
// 在组件中使用日志
public class PlayerSystem : AbstractSystem
{
private ILogger _logger;
protected override void OnInit()
{
_logger = this.GetUtility<ILoggerFactory>().CreateLogger<PlayerSystem>();
_logger.LogInfo("PlayerSystem initialized");
}
private void OnPlayerDied(PlayerDiedEvent e)
{
_logger.LogWarning($"Player died at position {e.Position}");
}
}
// 配置日志级别
var loggingConfig = new LoggingConfiguration
{
MinimumLevel = LogLevel.Debug,
Appenders = new[]
{
new ConsoleAppender(),
new FileAppender("logs/game.log")
}
};
```
### 2. 使用断点调试
```csharp
// 在关键位置添加断点
public override void _Ready()
{
var model = this.GetModel<PlayerModel>();
// 在这里设置断点,检查 model 的值
GD.Print($"Player health: {model.Health.Value}");
}
// 使用条件断点
private void OnDamage(DamageEvent e)
{
// 只在伤害大于50时中断
if (e.Amount > 50)
{
GD.Print("High damage detected!"); // 在这里设置断点
}
}
// 使用 GD.PushWarning 和 GD.PushError
if (player == null)
{
GD.PushError("Player is null!"); // 在输出面板显示错误
return;
}
```
### 3. 追踪事件流
```csharp
// 创建事件追踪系统
public class EventTracer : AbstractSystem
{
private ILogger _logger;
protected override void OnInit()
{
_logger = this.GetUtility<ILoggerFactory>().CreateLogger<EventTracer>();
// 使用反射监听所有事件(仅用于调试)
this.RegisterEvent<PlayerDiedEvent>(e =>
_logger.LogDebug($"Event: PlayerDiedEvent"));
this.RegisterEvent<PlayerAttackedEvent>(e =>
_logger.LogDebug($"Event: PlayerAttackedEvent - Damage: {e.Damage}"));
}
}
// 在架构中启用事件追踪
#if DEBUG
protected override void Init()
{
RegisterSystem(new EventTracer());
}
#endif
```
### 4. 性能分析
```csharp
// 使用 Godot 性能监视器
public override void _Process(double delta)
{
// 在编辑器中查看性能统计
Performance.GetMonitor(Performance.Monitor.TimeFps);
Performance.GetMonitor(Performance.Monitor.MemoryStatic);
}
// 自定义性能计数器
public class PerformanceCounter
{
private readonly Dictionary<string, Stopwatch> _timers = new();
public void StartTimer(string name)
{
if (!_timers.ContainsKey(name))
_timers[name] = new Stopwatch();
_timers[name].Restart();
}
public void StopTimer(string name)
{
if (_timers.TryGetValue(name, out var timer))
{
timer.Stop();
GD.Print($"{name}: {timer.ElapsedMilliseconds}ms");
}
}
}
// 使用
var counter = new PerformanceCounter();
counter.StartTimer("EventProcessing");
arch.SendEvent(new GameEvent());
counter.StopTimer("EventProcessing");
```
### 5. 单元测试调试
```csharp
[Test]
public void DebugPlayerDamage()
{
var arch = new TestArchitecture();
arch.Initialize();
var player = arch.GetModel<PlayerModel>();
// 打印初始状态
GD.Print($"Initial Health: {player.Health.Value}");
// 发送伤害事件
arch.SendEvent(new DamageEvent { Amount = 10 });
// 打印最终状态
GD.Print($"Final Health: {player.Health.Value}");
// 验证
Assert.AreEqual(90, player.Health.Value);
}
```
## 常见错误信息
### NU1101: Unable to find package
**错误类型**:NuGet 包安装错误
**完整错误信息**:
```
NU1101: Unable to find package GFramework.Core. No packages exist with this id in source(s): nuget.org
```
**原因**:
- 包源配置错误
- 包名拼写错误
- 网络连接问题
**解决方案**:
```bash
# 1. 检查包源
dotnet nuget list source
# 2. 添加 NuGet.org 源
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
# 3. 清理缓存
dotnet nuget locals all --clear
# 4. 重新安装
dotnet restore
```
### KeyNotFoundException: 未找到类型
**错误类型**:架构组件未注册
**完整错误信息**:
```
System.Collections.Generic.KeyNotFoundException: 未找到类型为 'PlayerModel' 的组件
```
**原因**:尝试获取未注册的组件
**解决方案**:
```csharp
// 在架构中注册组件
public class GameArchitecture : Architecture
{
protected override void Init()
{
RegisterModel(new PlayerModel());
RegisterSystem(new PlayerSystem());
RegisterUtility<IConfigUtility>(new ConfigUtility());
}
}
```
### InvalidOperationException: Architecture is already initialized
**错误类型**:重复初始化
**完整错误信息**:
```
System.InvalidOperationException: Architecture is already initialized
```
**原因**:多次调用 `Initialize()` 方法
**解决方案**:
```csharp
// 检查初始化状态
if (!architecture.IsInitialized)
{
architecture.Initialize();
}
// 或使用单例模式
public class GameArchitecture : Architecture<GameArchitecture>
{
// 自动处理单例
}
```
### NullReferenceException: Object reference not set
**错误类型**:空引用异常
**常见场景**:
1. **节点未找到**:
```csharp
// ❌ 错误
var player = GetNode<Player>("Player"); // 返回 null
player.Health = 100; // 抛出异常
// ✅ 正确
if (HasNode("Player"))
{
var player = GetNode<Player>("Player");
player.Health = 100;
}
```
2. **组件未注册**:
```csharp
// ❌ 错误
var model = this.GetModel<PlayerModel>(); // 返回 null
model.Health.Value = 100; // 抛出异常
// ✅ 正确:先注册
protected override void Init()
{
RegisterModel(new PlayerModel());
}
```
3. **架构上下文丢失**:
```csharp
// ❌ 错误:未设置架构锚点
var model = this.GetModel<PlayerModel>(); // 抛出异常
// ✅ 正确:设置架构锚点
var anchor = new ArchitectureAnchor();
anchor.SetArchitecture(architecture);
AddChild(anchor);
```
### InvalidCastException: Unable to cast object
**错误类型**:类型转换错误
**完整错误信息**:
```
System.InvalidCastException: Unable to cast object of type 'Node' to type 'Player'
```
**原因**:节点类型不匹配
**解决方案**:
```csharp
// ❌ 错误:强制转换
var player = (Player)GetNode("Player"); // 如果不是 Player 类型会抛出异常
// ✅ 正确:使用泛型方法
var player = GetNode<Player>("Player");
// ✅ 使用 as 操作符
var player = GetNode("Player") as Player;
if (player != null)
{
player.Health = 100;
}
// ✅ 使用 is 模式匹配
if (GetNode("Player") is Player player)
{
player.Health = 100;
}
```
### ArgumentException: An item with the same key has already been added
**错误类型**:重复注册
**完整错误信息**:
```
System.ArgumentException: An item with the same key has already been added. Key: PlayerModel
```
**原因**:重复注册同一类型的组件
**解决方案**:
```csharp
// ❌ 错误:重复注册
protected override void Init()
{
RegisterModel(new PlayerModel());
RegisterModel(new PlayerModel()); // 重复注册
}
// ✅ 正确:只注册一次
protected override void Init()
{
RegisterModel(new PlayerModel());
}
// ✅ 如果需要多个实例,使用不同的键
protected override void Init()
{
RegisterModel(new PlayerModel(), "Player1");
RegisterModel(new PlayerModel(), "Player2");
}
```
### TimeoutException: The operation has timed out
**错误类型**:操作超时
**常见场景**:
1. **资源加载超时**:
```csharp
// ✅ 增加超时时间
var handle = resourceManager.LoadAsync<Texture2D>(
"res://assets/large_texture.png",
timeout: 30f // 30秒超时
);
```
2. **事件等待超时**:
```csharp
// ✅ 使用带超时的等待
yield return new WaitForEventWithTimeout<GameEvent>(
architecture,
timeout: 5f
);
```
### StackOverflowException: Operation caused a stack overflow
**错误类型**:栈溢出
**原因**:
- 无限递归
- 循环依赖
**解决方案**:
```csharp
// ❌ 错误:无限递归
private void ProcessData()
{
ProcessData(); // 无限递归
}
// ✅ 正确:添加终止条件
private void ProcessData(int depth = 0)
{
if (depth > 100)
return;
ProcessData(depth + 1);
}
// ❌ 错误:循环依赖
public class SystemA : AbstractSystem
{
protected override void OnInit()
{
this.GetSystem<SystemB>().Initialize();
}
}
public class SystemB : AbstractSystem
{
protected override void OnInit()
{
this.GetSystem<SystemA>().Initialize(); // 循环依赖
}
}
// ✅ 正确:使用事件解耦
public class SystemA : AbstractSystem
{
protected override void OnInit()
{
this.SendEvent(new InitializeSystemBEvent());
}
}
```
### ObjectDisposedException: Cannot access a disposed object
**错误类型**:访问已释放的对象
**完整错误信息**:
```
System.ObjectDisposedException: Cannot access a disposed object. Object name: 'Architecture'
```
**原因**:在架构销毁后继续使用
**解决方案**:
```csharp
// ✅ 检查对象状态
if (architecture != null && !architecture.IsDisposed)
{
var model = architecture.GetModel<PlayerModel>();
}
// ✅ 在销毁前清理引用
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
_architecture = null; // 清理引用
}
// ✅ 使用弱引用
private WeakReference<GameArchitecture> _architectureRef;
public void UseArchitecture()
{
if (_architectureRef.TryGetTarget(out var arch))
{
var model = arch.GetModel<PlayerModel>();
}
}
```
### FileNotFoundException: Could not find file
**错误类型**:文件未找到
**完整错误信息**:
```
System.IO.FileNotFoundException: Could not find file 'res://assets/player.png'
```
**原因**:
- 文件路径错误
- 文件不存在
- 文件未导入到项目
**解决方案**:
```csharp
// ✅ 检查文件是否存在
if (ResourceLoader.Exists("res://assets/player.png"))
{
var texture = ResourceLoader.Load<Texture2D>("res://assets/player.png");
}
else
{
GD.PrintErr("File not found!");
}
// ✅ 使用正确的路径格式
// Godot 使用 res:// 协议
var texture = ResourceLoader.Load<Texture2D>("res://assets/player.png");
// ✅ 检查文件是否在 .import 文件中
// 确保文件已被 Godot 导入
```
### NotImplementedException: The method is not implemented
**错误类型**:方法未实现
**完整错误信息**:
```
System.NotImplementedException: The method or operation is not implemented
```
**原因**:抽象方法或接口方法未实现
**解决方案**:
```csharp
// ❌ 错误:未实现抽象方法
public class MySystem : AbstractSystem
{
// 缺少 OnInit 实现
}
// ✅ 正确:实现所有抽象方法
public class MySystem : AbstractSystem
{
protected override void OnInit()
{
// 实现初始化逻辑
}
}
// ✅ 实现接口方法
public class MyLoader : IResourceLoader
{
public async Task<T> LoadAsync<T>(string path) where T : class
{
// 实现加载逻辑
await Task.Delay(100);
return default;
}
}
```
## 常见问题排查清单
- [ ] 所有组件都已注册?
- [ ] 事件类型是否匹配?
- [ ] 是否正确注销了监听器?
- [ ] Architecture 是否已初始化?
- [ ] 是否有循环依赖?
- [ ] 内存使用是否持续增长?
- [ ] 事件处理是否过于复杂?
- [ ] 是否缓存了频繁访问的组件?
- [ ] 资源是否正确释放?
- [ ] 协程是否正确启动和停止?
- [ ] Godot 节点路径是否正确?
- [ ] 信号连接是否成功?
## 获取帮助
如果问题仍未解决:
1. 查看 [Core 文档](/zh-CN/core/) 了解更多细节
2. 查看 [架构组件](/zh-CN/core/architecture) 了解架构设计
3. 查看 [Godot 集成](/zh-CN/godot/) 了解 Godot 特定问题
4. 在 [GitHub Issues](https://github.com/GeWuYou/GFramework/issues) 提交问题
5. 查看 [教程](/zh-CN/tutorials/) 中的示例代码
6. 查看 [常见问题](/zh-CN/faq) 获取快速答案
---
**提示**:在提交 Issue 时,请提供:
- 错误信息和完整的堆栈跟踪
- 最小化的可复现代码示例
- 你的环境信息(.NET 版本、Godot 版本、操作系统等)
- 已尝试的解决方案
- 相关的配置文件(如有)