Raylib_cs加载字体到静态类成员后程序异常终止问题排查
我在Raylib的C#绑定(Raylib_cs)中尝试将字体加载到静态类的成员对象中,目标是通过单一命名空间的静态类实现项目全局访问。我使用Newtonsoft.Json库反序列化对象,由于直接反序列化到静态类存在困难,采用了如下实现方案:
using Raylib_cs; using Newtonsoft.Json; using System.Runtime.Serialization; namespace Game.Configs { public static class Configuration { /*Debug config*/ private static DebugConfig? _debug; public static DebugConfig Debug => _debug!; /*Graphics config*/ private static GraphicsConfig? _graphics; public static GraphicsConfig Graphics => _graphics!; public static void Load(string Path) { try { _debug = JsonConvert.DeserializeObject<DebugConfig>(File.ReadAllText($"{Path}dbg.json"))!; _graphics = JsonConvert.DeserializeObject<GraphicsConfig>(File.ReadAllText($"{Path}gfx.json"))!; } catch (Exception e) { // Log the exception Console.WriteLine($"Exception during deserialization: {e}"); } } } //these classes are in separate source files, but within the same namespace so I added them here public class DebugConfig { public string? FontPath {get; set;} [JsonIgnore] public Font DebugFont {get;set;} [OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) { if(!string.IsNullOrEmpty(FontPath)) DebugFont = Raylib.LoadFont(FontPath); } } public class GraphicsConfig { public string? Title {get; set;} public V2Di Resolution {get;set;} public bool Windowed {get;set;} public byte Framerate {get;set;} } }
Game类实现如下:
partial class Game { public Game() { Configuration.Load("res/cfg/"); } public void Initialize() { Raylib.InitWindow(Configuration.Graphics.Resolution.X,Configuration.Graphics.Resolution.Y,Configuration.Graphics.Title); if(!Configuration.Graphics.Windowed && !Raylib.IsWindowFullscreen()) Raylib.ToggleFullscreen(); Raylib.SetTargetFPS(Configuration.Graphics.Framerate); Start(); } }
程序入口首先调用Game构造函数,调试时,Raylib报告字体加载成功后程序立即终止,输出:
INFO: FILEIO: [res/fnt/_stmr.ttf] File loaded successfully The program '[24301] rayNet' has exited with code 0 (0x0).
将字体加载到Game类的静态变量(public static Font ttfTest = Raylib.LoadFont("res/fnt/_stmr.ttf");)可正常运行,Configuration.Graphics的加载与使用也符合预期。注释掉Configuration类中_debug = JsonConvert.DeserializeObject<DebugConfig>(File.ReadAllText($"{Path}dbg.json"))!;这一行后,程序可正常启动。编译无警告,终止时无异常抛出,请问为何Configuration.Graphics正常而Configuration.Debug会导致程序终止?
核心原因
Raylib的字体加载依赖已初始化的图形上下文,但你在Game构造函数中调用Configuration.Load()时,Raylib.InitWindow()还未执行——此时图形窗口和OpenGL上下文都未创建,直接调用Raylib.LoadFont()会触发Raylib内部的断言失败或上下文错误,导致程序静默终止。
你把字体加载放到Game类静态变量中能正常运行,是因为静态变量的初始化时机晚于窗口初始化(或刚好在窗口初始化之后);而DebugConfig的OnDeserialized方法在反序列化时立即执行,此时窗口还未创建,图形环境不具备加载字体的条件。Configuration.Graphics仅加载配置数据,不涉及图形资源操作,因此不受影响。
解决方案
调整字体加载时机:将字体加载逻辑从反序列化回调中移出,放到窗口初始化完成之后。
修改DebugConfig类:public class DebugConfig { public string? FontPath {get; set;} [JsonIgnore] public Font DebugFont {get; private set;} // 新增加载方法,在窗口初始化后调用 public void LoadFont() { if(!string.IsNullOrEmpty(FontPath) && !DebugFont.IsReady) DebugFont = Raylib.LoadFont(FontPath); } }然后在
Game.Initialize()中补充调用:public void Initialize() { Raylib.InitWindow(Configuration.Graphics.Resolution.X,Configuration.Graphics.Resolution.Y,Configuration.Graphics.Title); // 窗口初始化完成后加载字体 Configuration.Debug.LoadFont(); if(!Configuration.Graphics.Windowed && !Raylib.IsWindowFullscreen()) Raylib.ToggleFullscreen(); Raylib.SetTargetFPS(Configuration.Graphics.Framerate); Start(); }清理无效回调:删除
DebugConfig中的[OnDeserialized]标记和对应的OnDeserializedMethod方法,避免在图形上下文未就绪时触发资源加载。补充资源释放:程序退出时调用
Raylib.UnloadFont(Configuration.Debug.DebugFont),防止内存泄漏。
内容的提问来源于stack exchange,提问作者Boyfinn

