- 示例工程
【免费下载链接】godot-demo-projects
Demonstration and Template Projects
本文围绕 godot-demo-projects 仓库中的 misc/os_test 演示项目展开,它是官方为 Godot 4 提供的一套「操作系统能力体检」工具:通过 os_test.gd 在左侧面板罗列引擎从宿主系统采集到的几十项信息,通过 actions.gd 在右侧提供 14 个可直接点击的系统交互按钮,并可选择性地在 Mono/.NET 构建下通过 CSharpTest.cs 补充 C# 预处理器宏判定的平台信息。读完本文,你将掌握OS、DisplayServer、Time、AudioServer、RenderingServer等跨平台 API 的具体用法,并了解如何把它当作移植新平台或回归测试时的系统信息诊断工具。
项目定位:面向移植与回归验证的系统功能测试台
README 开篇明确了这一演示的用途:展示 Godot 中与操作系统相关的各种特性,可用于把 Godot 移植到新平台时的功能验证,也可用于检查引擎回归(regressions)。它本质上是一个「系统信息 + 系统操作」的双栏测试台:
- 左侧
Features(RichTextLabel)由 os_test.gd 驱动,逐条采集并展示操作系统信息; - 右侧
Actions(VBoxContainer + GridContainer)由 actions.gd 驱动,每个按钮对应一项系统级操作; - 在启用 Mono 的 Godot 构建中,还会加载 CSharpTest.cs,把由 C# 预处理器宏推断出的平台信息追加到左侧面板。
项目使用GDScript 为主、少量 C#(Operating System Testing.csproj 中 TargetFramework 为 net472,SDK 为 Godot.NET.Sdk/4.0.0-dev5),并且运行该演示并不要求 .NET 构建——C# 部分只是可选增强。渲染器方面,project.godot 中renderer/rendering_method与renderer/rendering_method.mobile均设置为"gl_compatibility",即使用兼容性渲染器(OpenGL 管线)。
核心原理:OS类作为平台无关的抽象层
README 指出,OS类为平台相关代码提供了一层抽象:OS 封装了与宿主操作系统通信的最常用功能,包括剪贴板、视频驱动、日期与时间、定时器、环境变量、二进制程序执行、命令行等。其典型用法是:游戏逻辑只调用统一的OS.xxx()方法,由引擎在 Windows、Linux、macOS、Android、iOS、Web 等平台上分别映射到各自的系统 API。
在演示代码中,「读取信息」主要由OS类承担,同时辅以DisplayServer(窗口、屏幕、剪贴板、全局菜单等显示服务器能力)、Time(系统日期时间)、AudioServer(音频设备)与RenderingServer(视频适配器信息)等单例。换句话说,这个 demo 是这些系统级 API 的一次「全覆盖冒烟测试」。
项目结构与运行方式
项目目录非常精简,全部文件如下:
| 文件 | 作用 |
|---|---|
| project.godot | 项目配置:名称、主场景、渲染器、窗口拉伸策略 |
| os_test.tscn | 主场景:双栏布局(信息面板 + 按钮网格)+ 空CSharpTest节点 |
| os_test.gd | 左侧信息采集逻辑(挂载在根节点OSTest上) |
| actions.gd | 右侧按钮逻辑(挂载在HBoxContainer/Actions上) |
| CSharpTest.cs | C# 平台判定脚本(运行时可动态加载到CSharpTest节点) |
Operating System Testing.csproj /.sln | .NET 工程文件(仅 Mono 构建需要) |
screenshots/ | 运行截图(top-hidpi.png、mono.png) |
project.godot中值得注意的配置项:
[application] config/name="Operating System Testing" config/description="This demo showcases various OS-specific features in Godot. ..." config/tags=PackedStringArray("demo", "official", "porting") run/main_scene="res://os_test.tscn" config/features=PackedStringArray("4.7") run/low_processor_mode=true [display] window/stretch/mode="canvas_items" window/stretch/aspect="expand" [rendering] renderer/rendering_method="gl_compatibility" renderer/rendering_method.mobile="gl_compatibility"- 主场景为
res://os_test.tscn,直接用 Godot 打开 misc/os_test 目录即可运行(如godot --path misc/os_test); run/low_processor_mode=true会让引擎在窗口未聚焦等场景下主动降低处理器占用,可以推断是为长时间挂机诊断设计的省电选项;canvas_items拉伸模式配合expand纵横比,使双栏 UI 在不同分辨率下自适应缩放。
os_test.tscn的场景树结构为:根节点OSTest(Panel,挂os_test.gd)→HBoxContainer→ 左侧Features(RichTextLabel,bbcode_enabled = true)与右侧Actions(VBoxContainer,挂actions.gd,内含 2 列GridContainer的 14 个 Button,以及一个独立的空节点CSharpTest)。按钮的pressed信号全部通过场景连接(connection)绑定到actions.gd的_on_*_pressed方法。
左侧信息面板:os_test.gd 的系统信息采集
os_test.gd在_ready()中按 15 个分类逐条填充左侧面板(见 os_test.gd)。输出到 RichTextLabel 的同时,它还会用print_rich()把同样内容打印到终端——脚本注释明确说明这是为了便于复制粘贴以及 headless(无头)场景使用,例如在 CI 中通过godot --path misc/os_test --headless跑一遍即可在终端拿到全部系统信息做回归比对。
面板渲染层面有两个辅助函数:
add_header():以 24 号字体、蓝色(#5cf)输出分类标题;add_line():按「key: value」格式输出,奇数行/偶数行交替使用bgcolor=#8883底纹以增强可读性;布尔值会被着色(绿true/ 红false),空值显示为(empty);同时把对应行以终端友好颜色(青色键、绿色/红色布尔值)打印到标准输出。
各分类及所用 API 汇总如下:
| 分类 | 采集内容与调用方法 |
|---|---|
| Audio | 混音采样率AudioServer.get_mix_rate();输出延迟AudioServer.get_output_latency()(×1000 转毫秒);输出/采集设备列表get_output_device_list()、get_input_device_list();已连接的 MIDI 输入OS.get_connected_midi_inputs()(配合OS.open_midi_inputs()/OS.close_midi_inputs(),见下方说明) |
| Date and time | 本地/UTC 日期时间Time.get_datetime_string_from_system(utc, with_seconds);日期get_date_string_from_system();时间get_time_string_from_system();时区Time.get_time_zone_from_system();UNIX 时间戳Time.get_unix_time_from_system()。另提供datetime_to_string()把带year/month/day/hour/minute/second的字典格式化为YYYY-MM-DD HH:MM:SS(含pad_zeros补零) |
| Display | 屏幕数量DisplayServer.get_screen_count();DPIscreen_get_dpi();缩放因子screen_get_scale()/screen_get_max_scale();起始屏幕位置/尺寸/刷新率screen_get_position()、screen_get_size()、screen_get_refresh_rate();安全区矩形get_display_safe_area();屏幕方向screen_get_orientation(),脚本按枚举顺序映射为 Landscape / Portrait / Landscape (reverse) / Portrait (reverse) / Landscape (defined by sensor) / Portrait (defined by sensor) / Defined by sensor |
| Engine | 引擎版本Engine.get_version_info()["string"];架构Engine.get_architecture_name();命令行参数OS.get_cmdline_args();是否调试构建OS.is_debug_build();可执行文件路径OS.get_executable_path();用户数据目录OS.get_user_data_dir();用户文件系统是否持久OS.is_userfs_persistent();进程 PIDOS.get_process_id();主线程/调用线程 IDget_main_thread_id()、get_thread_caller_id();内存信息OS.get_memory_info()、静态内存用量与峰值get_static_memory_usage()、get_static_memory_peak_usage() |
| Environment | 环境变量OS.get_environment("PATH")与OS.get_environment("path")——同时查询大小写两种写法,用于跨平台探测环境变量是否大小写敏感 |
| Hardware | 设备型号OS.get_model_name();处理器型号OS.get_processor_name();逻辑核数OS.get_processor_count();设备唯一 IDOS.get_unique_id() |
| Input | 是否有触摸屏DisplayServer.is_touchscreen_available();是否支持虚拟键盘DisplayServer.has_feature(DisplayServer.FEATURE_VIRTUAL_KEYBOARD);若支持则读取虚拟键盘高度virtual_keyboard_get_height() |
| Localization | 区域OS.get_locale()(如en_US);语言OS.get_locale_language()(如en) |
| Mobile | 已授予权限列表OS.get_granted_permissions()(Android 等平台) |
| .NET (C#) | 通过ResourceLoader.exists("res://CSharpTest.cs")检测 Mono 模块是否启用;启用则set_script()动态加载脚本并调用OperatingSystem()、PlatformType()(详见下文 C# 小节) |
| Software | 系统名OS.get_name()(如 X11、Windows);版本OS.get_version();发行版名OS.get_distribution_name();是否支持/启用深色模式DisplayServer.is_dark_mode_supported()、is_dark_mode();系统强调色DisplayServer.get_accent_color();系统字体数量OS.get_system_fonts();具体字体路径OS.get_system_font_path("sans-serif"),以及get_system_font_path_for_text()分别用英文Hello、中文你好、日文こんにちは测试多语言字体回退 |
| Security | 是否沙箱环境OS.is_sandboxed();随机熵OS.get_entropy(8)(8 字节随机数);系统 CA 证书OS.get_system_ca_certificates()(有/无及字节数) |
| Engine directories | 数据目录OS.get_data_dir();配置目录OS.get_config_dir();缓存目录OS.get_cache_dir() |
| System directories | OS.get_system_dir()配合SYSTEM_DIR_DESKTOP / DCIM / DOCUMENTS / DOWNLOADS / MOVIES / MUSIC / PICTURES / RINGTONES枚举,读取桌面、相册、文档、下载、影片、音乐、图片、铃声等系统目录 |
| Video | GPU 适配器名/厂商RenderingServer.get_video_adapter_name()、get_video_adapter_vendor();适配器类型get_video_adapter_type()(Other/Integrated/Discrete/Virtual/CPU)——仅在非 Compatibility 渲染器下查询,脚本用get_current_rendering_method() != "gl_compatibility"做了守卫;图形 API 版本get_video_adapter_api_version();驱动名与版本OS.get_video_adapter_driver_info()返回数组的[0]、[1]元素 |
其中scan_midi_inputs()包含一个平台细节:若DisplayServer.get_name() == "headless"直接返回空字符串,这是对 godotengine/godot#52821 一类问题的规避(headless 下打开 MIDI 输入可能异常)——注释中保留了对上游 issue 的引用。从源码结构看,这也再次印证该 demo 被设计为可在无头环境运行。
右侧操作面板:actions.gd 的 14 项系统操作
actions.gd的 14 个按钮覆盖了「与操作系统交互」的常见诉求(actions.gd):
| 按钮 | 实现要点 |
|---|---|
| Open Shell (web) | OS.shell_open("https://example.com"),用系统默认浏览器打开 URL |
| Open Shell (folder) | 先读HOME(Windows 回退USERPROFILE)环境变量,macOS 下加file://前缀,再调用OS.shell_show_in_file_manager(path)在文件管理器中显示 |
| Change Window Title | DisplayServer.window_set_title(),标题内嵌多字节 Unicode 字符é € × Ù ¨用于测试标题编码 |
| Change Window Icon | 先检查DisplayServer.has_feature(DisplayServer.FEATURE_ICON),不支持则OS.alert()提示;支持时用Image.create(128, 128, false, Image.FORMAT_RGB8)+fill(Color(1, 0.6, 0.3))生成橙色纯色图,再DisplayServer.set_icon(image) |
| Move Window to Foreground | await get_tree().create_timer(5).timeout延迟 5 秒后DisplayServer.window_move_to_foreground(),随后把标题恢复为ProjectSettings.get_setting("application/config/name")——为测试留出「先切走窗口」的时间 |
| Request Attention | 同样延迟 5 秒后DisplayServer.window_request_attention()(任务栏闪烁等),并恢复标题 |
| Vibrate Device (200 ms) | Input.vibrate_handheld(200) |
| Vibrate Device (1000 ms) | Input.vibrate_handheld(1000) |
| Add Global Menu Items | 先检查FEATURE_GLOBAL_MENU;向主菜单栏_main与 Dock 右键菜单_dock注入子菜单/菜单项:global_menu_add_submenu_item()、global_menu_add_item()(含点击/快捷键回调 lambda、KEY_MASK_META \| KEY_1快捷键)、global_menu_add_separator() |
| Remove Global Menu Item | 按索引逆序global_menu_remove_item()清空_main、_dock下刚添加的菜单项 |
| Get Clipboard Contents | 检查FEATURE_CLIPBOARD后DisplayServer.clipboard_get(),用OS.alert()弹窗展示剪贴板内容 |
| Set Clipboard Contents | DisplayServer.clipboard_set("Modified clipboard contents. ..."),写入含 Unicode 的剪贴板文本 |
| Display Alert | OS.alert("Hello from Godot! ...")弹出模态系统对话框 |
| Kill Current Process | OS.kill(OS.get_process_id())——直接结束当前进程,是压力测试进程管理能力的按钮 |
Web 平台的兼容性处理
actions.gd的_ready()专门针对 Web 导出做了一次「能力裁剪」:当OS.has_feature("web")为真时,以下 8 个按钮会被置灰并追加(not supported on Web)提示:OpenShellFolder(文件管理器)、MoveWindowToForeground、RequestAttention、VibrateDeviceShort、VibrateDeviceLong、AddGlobalMenuItems、RemoveGlobalMenuItem、KillCurrentProcess。这是展示「先用OS.has_feature()/DisplayServer.has_feature()探测平台能力,再决定是否调用」这一跨平台最佳实践的典型范例——OS.alert()也在多处充当不支持时的用户提示手段。
Mono/.NET 扩展:CSharpTest.cs 与预处理器宏
README 说明:在启用 Mono 的 Godot 版本中,Godot 会把 C# 脚本加载进对应节点,然后由 C# 预处理器宏判定的信息会追加到左侧面板。(README 中描述为MonoTest节点,当前仓库的实际实现为CSharpTest.cs与CSharpTest节点。)
检测与加载逻辑位于 os_test.gd:用ResourceLoader.exists("res://CSharpTest.cs")判断 Mono 模块是否启用,显示Mono module enabled: Yes/No;若存在则通过csharp_test.set_script(load("res://CSharpTest.cs"))动态挂脚本,再调用两个公开方法:
OperatingSystem():用GODOT_WINDOWS / GODOT_LINUXBSD / GODOT_MACOS / GODOT_ANDROID / GODOT_IOS / GODOT_WEB / GODOT / else宏链返回Windows / Linux/*BSD / macOS / Android / iOS / Web / Other / Unknown;PlatformType():用GODOT_PC / GODOT_MOBILE / GODOT_WEB / GODOT宏链返回PC / Mobile / Web / Other / Unknown。
由于这些宏只在对应目标平台编译时被定义,因此它们能反映「当前构建产物」的真实平台属性,与运行时通过OS.get_name()拿到的信息互为印证。这也解释了为何 demo 特意保留.csproj/.sln工程文件(Godot.NET.Sdk/4.0.0-dev5、net472):需要 C# 信息时用 .NET 版 Godot 打开即可,纯 GDScript 运行完全不受影响。
回归测试价值与扩展方向
从源码结构可以梳理出该 demo 的两种典型用法:
- 移植验证:把 Godot 移植到一个新平台后,运行本 demo 逐一核对左侧 15 个分类的输出是否合理、14 个按钮是否生效,即可快速发现该平台上未实现或行为异常的
OS/DisplayServer能力; - 回归检查:由于所有信息同时通过
print_rich()输出到终端,可以无头运行并抓取终端文本做 diff,自动比对不同版本、不同平台间的行为差异(脚本注释亦明确指向这一用途)。
若要在自己的项目里复用,只需把OS.get_*()、DisplayServer.*、Time.*等调用模式照搬到对应业务逻辑中,并沿用has_feature()/OS.has_feature()的「先探测、后调用」写法即可保证跨平台健壮性——这正是 os_test 作为一个「官方、porting 标签」演示项目最值得借鉴的工程范式。
延伸阅读
- 项目级说明:根目录 README(2D/3D/音频/计算/GUI 等各分类演示索引)与 CONTRIBUTING.md;
- 本演示的配置与场景:project.godot、os_test.tscn;
- 核心实现:os_test.gd(信息采集)、actions.gd(操作按钮)、CSharpTest.cs(C# 宏判定);
- 仓库中其他与系统/平台能力相关的演示:
misc/window_management(窗口管理)、misc/multiple_windows(多窗口)、misc/joypads(手柄输入),可作交叉参考。
- 示例工程
【免费下载链接】godot-demo-projects
Demonstration and Template Projects
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考