VGUI 与本地化
CS:GO 的 HUD、主菜单与加载画面都用 Scaleform(见 HUD 与 Scaleform UI),但 Source 的 VGUI2 并没有退场:引擎用它搭建所有面板的根层级,开发者控制台、调试与性能面板、工具界面、专用服务器的 Windows 管理窗口都是 VGUI 面板,引擎的输入分发与绘制顺序也要在 VGUI 与 Scaleform 之间仲裁。本地化字符串则由独立的 localize 模块统一提供,VGUI 与 Scaleform 共用同一张表。本页说明这些模块如何装配、各自负责什么,以及本地化文件如何加载与查找。
相关页面:HUD 与 Scaleform UI · 输入与命令 · 专用服务器启动链 · 接口工厂与 AppSystem · 引擎总览
相关源码文件#
- VGUI2 运行时:
vgui2/src/vgui.cpp、vgui2/src/vgui_dll.vpc;控件库vgui2/vgui_controls/(82 个跟踪文件),代表vgui2/vgui_controls/consoledialog.cpp - 材质系统 surface:
vguimatsurface/MatSystemSurface.cpp、vguimatsurface/MatSystemSurface.h、vguimatsurface/Input.cpp - 引擎集成:
engine/vgui_baseui_interface.cpp、engine/console.cpp、engine/view.cpp - client.dll 的 GameUI 与控制台:
game/client/cstrike15/gameui/gameui_interface.cpp、game/client/cstrike15/gameui/gameconsole.cpp、game/client/cstrike15/gameui/gameconsoledialog.h - 本地化:
localize/localize.cpp、localize/localize.vpc、public/localize/ilocalize.h、public/vgui/ILocalize.h、interfaces/interfaces.cpp - Scaleform 翻译适配:
scaleformui/scaleformuiimpl/scaleformuiintegration.cpp、scaleformui/scaleformuiimpl/scaleformuitranslationimpl.cpp - 专用服务器 GUI:
dedicated/vgui/vguihelpers.cpp、dedicated/vgui/MainPanel.cpp、dedicated/sys_ded.cpp、dedicated/dedicated.vpc
1. 模块与装配#
| 模块(产物) | 职责 | 装配证据 |
|---|---|---|
vguimatsurface(DLL) | CMatSystemSurface:用材质系统实现 VGUI 的 ISurface(绘制、字体、光标、输入上下文) | 以 VGUI_SURFACE_INTERFACE_VERSION 与 SCHEME_SURFACE_INTERFACE_VERSION 暴露:vguimatsurface/MatSystemSurface.cpp:159-164;启动器把它排在 vgui2 之前,注释说明是为了替换 vgui2 自带的 surface:launcher/launcher.cpp:764-766 |
vgui2(vgui_dll 工程) | 面板树、消息、Scheme、输入状态;自带的 GDI Surface.cpp 只在 Windows 编译 | vgui2/src/vgui_dll.vpc:9-11、vgui2/src/vgui_dll.vpc:36-59;CVGui 声明依赖 localize 与 surface,并在 Connect 时要求拿到文件系统与本地化接口:vgui2/src/vgui.cpp:1103-1124 |
localize(DLL) | CLocalize:令牌 → 宽字符串表 | 以 LOCALIZE_INTERFACE_VERSION(Localize_001)暴露:localize/localize.cpp:57、localize/localize.cpp:187-190、public/interfaces/interfaces.h:131、localize/localize.vpc:8 |
vgui_controls(静态库) | Panel、Frame、CConsoleDialog 等控件 | 专用服务器工程直接链接:dedicated/dedicated.vpc:125 |
| 专用服务器 | Windows 模块表加载 vgui2.dll | dedicated/sys_windows.cpp:278 |
这些工程都在 csgo_partner 构建组中:vpc_scripts/groups.vgc:86-95。localize.dll 不在启动器的模块表里(launcher/launcher.cpp:727-770),而是作为 vgui2 的 GetDependencies 被 AppSystem 组自动补齐(依赖补齐机制见 接口工厂与 AppSystem)。
两个本地化指针指向同一实现:interfaces 库把 g_pLocalize(::ILocalize)与 g_pVGuiLocalize(vgui::ILocalize)都用 LOCALIZE_INTERFACE_VERSION 查询:interfaces/interfaces.cpp:147-148。vgui::ILocalize 只是为兼容保留的子接口,唯一声明的 FindAsUTF8 在基类中已有同签名纯虚函数,因此两者虚表布局一致(推断):public/vgui/ILocalize.h:15-28、public/localize/ilocalize.h:126。vgui2/src/LocalizedStringTable.cpp 是旧实现,没有被任何 .vpc 引用,且它使用的 VGUI_LOCALIZE_INTERFACE_VERSION 在公共头文件中没有定义:vgui2/src/LocalizedStringTable.cpp:53、vgui2/src/LocalizedStringTable.cpp:180。
2. 引擎内置 VGUI:CEngineVGui#
Host_Init 在材质系统、Scaleform 全屏影片与匹配框架之后、ClientDLL_Init 之前初始化引擎 VGUI:engine/host.cpp:5641-5661。CEngineVGui::Init 的主要步骤:
- 从 client.dll 的工厂取得
IGameUI与(PC 上)IGameConsole;命令行-gameuidll时改从独立的gameui.dll取:engine/vgui_baseui_interface.cpp:708-747。 - 在输入栈上建立 GameUI 与 VGUI 两个输入上下文:
engine/vgui_baseui_interface.cpp:749-755。 - 加载
Resource/SourceScheme.res(失败即Sys_Error),启动ivgui():engine/vgui_baseui_interface.cpp:776-800。 - 建立面板层级、引擎面板与本地化文件(下表与第 4 节),再初始化并启动 GameUI、初始化游戏控制台、激活 GameUI;带
-console、-toconsole、-rpt或-allowdebug时直接打开控制台:engine/vgui_baseui_interface.cpp:1024-1059。
| 面板(自下而上) | 用途 | 证据 |
|---|---|---|
staticPanel | 根面板,挂在 surface 的嵌入面板上 | engine/vgui_baseui_interface.cpp:824-827 |
staticClientDLLPanel 等 | client.dll 的面板根;默认不接收键盘输入 | engine/vgui_baseui_interface.cpp:833-847 |
staticGameUIPanel | GameUI 面板根 | engine/vgui_baseui_interface.cpp:852-857 |
staticGameDLLPanel / staticEngineToolsPanel | 工具框架与引擎工具面板根(-tools 时工具面板挂到 GameDLL 面板下) | engine/vgui_baseui_interface.cpp:860-878 |
staticDebugSystemPanel 等 | PC 上的调试系统、demo UI、雾效 UI、纹理查看、性能工具、调色工具 | engine/vgui_baseui_interface.cpp:880-914 |
| 过渡效果 / 焦点叠加层 | 最上层,焦点叠加层 Z 序 150 | engine/vgui_baseui_interface.cpp:916-937 |
面板的 Z 序随 TOOLFRAMEWORK_VGUI_REFACTOR 取不同值:engine/vgui_baseui_interface.cpp:807-822、engine/vgui_baseui_interface.cpp:853-866。PC 上还会在引擎工具面板下创建控制台叠加面板、实体报告、绘制树、纹理列表与 VProf 面板,然后按 scripts/EngineVGuiLayout.res 布局并隐藏 VProf 面板:engine/vgui_baseui_interface.cpp:939-980。其中 CConPanel 负责屏幕左上角的通知文本与 Con_NPrintf 调试区,创建后默认隐藏:engine/console.cpp:118-158、engine/console.cpp:1631-1638。
3. 开发者控制台#
toggleconsole在con_enable、developer、-console或-rpt之一成立时,调用EngineVGui()->ShowConsole();有应用级模态面板时不打开:engine/console.cpp:224-242、engine/console.cpp:257-270、engine/console.cpp:1645。ShowConsole/HideConsole只是转给IGameConsole的Activate/Hide:engine/vgui_baseui_interface.cpp:1415-1438。- CS:GO 的
IGameConsole实现在 client.dll 的cstrike15/gameui中:CGameConsole::Initialize创建CGameConsoleDialog(派生自vgui::CConsoleDialog)并放在屏幕右半部:game/client/cstrike15/gameui/gameconsole.cpp:32-71、game/client/cstrike15/gameui/gameconsoledialog.h:24-26。在CSTRIKE15下它不挂到staticGameUIPanel:engine/vgui_baseui_interface.cpp:1031-1040。 - 控制台对话框打开/关闭时维护
console_window_open,引擎按键分发据此让 ESC 优先关闭控制台:vgui2/vgui_controls/consoledialog.cpp:1298-1318、engine/keys.cpp:1344-1353。
4. 与 Scaleform 的分工#
| 方面 | VGUI | Scaleform | 证据 |
|---|---|---|---|
| 内容 | 控制台、调试/性能/工具面板、cstrike15/gameui 下的 VGUI 对话框、专用服务器 GUI | HUD、主菜单、开始画面、加载画面等 | engine/vgui_baseui_interface.cpp:880-956、game/client/cstrike15/Scaleform/loadingscreen_scaleform.cpp:183、game/client/cstrike15/Scaleform/createmainmenuscreen_scaleform.cpp:113 |
| 鼠标轴事件 | 先给 VGUI surface | 其后;vgui_panel_active 为真时跳过 | engine/sys_mainwind.cpp:424-437、engine/vgui_baseui_interface.cpp:145 |
| 手柄轴事件 | 其后 | 先给 Scaleform | engine/sys_mainwind.cpp:416-423 |
| 按键事件 | 按 Key_Event 优先级链(VGUI 先于 Scaleform,ESC/反引号例外),详见 输入与命令 | 同左 | engine/keys.cpp:1306-1363 |
| 菜单/加载时的绘制 | V_RenderVGuiOnly_NoSwap 中先画 Scaleform 全屏槽,再画 VGUI PAINT_UIPANELS,最后画 Scaleform 光标 | 同左 | engine/view.cpp:124-147 |
| 游戏中的绘制 | client.dll 先 VGui_Paint( PAINT_INGAMEPANELS ) | 再按分屏槽设置视口并绘制 Scaleform HUD 槽 | game/client/viewrender.cpp:3561-3578 |
| 绘制开关 | r_drawvgui 为假、计时 demo 时或 -noshaderapi 时不画 VGUI | — | engine/vgui_baseui_interface.cpp:2225-2236 |
VGUI 的输入走 CMatSystemSurface::HandleInputEvent:输入未启用时不处理,否则更新按键状态并交给当前输入上下文:vguimatsurface/MatSystemSurface.cpp:688-699、vguimatsurface/Input.cpp:59。
5. 本地化:ILocalize#
5.1 加载#
CLocalize::AddFile 处理带 %language% 占位符的文件名:localize/localize.cpp:492-581
- 拒绝绝对路径:
localize/localize.cpp:504-508。 - 总是先加载英文文件,保证不缺令牌。
- PC 上语言取自
-language参数,否则读注册表HKEY_CURRENT_USER\Software\Valve\Steam\Language;值为unknown时退回英文。主机平台取系统语言。 - 语言不是英文时再加载对应语言文件;后加载的同名令牌覆盖先前的值,并触发变更回调:
localize/localize.cpp:881-943。
-all_languages 时改为加载全部语言并保留最宽的字符串(用 surface 作为文字宽度查询器):localize/localize.cpp:226、localize/localize.cpp:521-524、localize/localize.cpp:918-934、vguimatsurface/MatSystemSurface.cpp:398。在 OSX 上,引擎会把 Steam 的当前游戏语言写入上述注册表键:engine/vgui_baseui_interface.cpp:759-766。
解析时,非英文文件中以 [english] 开头的键被跳过,值后面的 [$X360] 一类条件标签会被求值,条件不满足的条目不加入:localize/localize.cpp:371-376、localize/localize.cpp:446-475。
| 加载者 | 文件 | 证据 |
|---|---|---|
引擎 CEngineVGui::Init | valve_%language%、当前游戏的 <game>_%language%(注释说明 CS:GO 把字符串从 valve 文件移到了 csgo_%language%)、当前 mod 的 <mod>_%language%、低暴力版本的 _lv 覆盖文件 | engine/vgui_baseui_interface.cpp:994-1022 |
| client.dll GameUI | gameui_%language%、valve_%language%(GAME 路径,含回退路径)、platform_%language%、vgui_%language% | game/client/cstrike15/gameui/gameui_interface.cpp:287、game/client/cstrike15/gameui/gameui_interface.cpp:294、game/client/cstrike15/gameui/gameui_interface.cpp:532-533 |
| 通用客户端组件 | 聊天 chat_%language%、隐藏字幕 closecaption_%language% | game/client/hud_basechat.cpp:765、game/client/hud_closecaption.cpp:890 |
| 专用服务器 GUI | platform_%language%、vgui_%language%、Admin/server_%language% | dedicated/vgui/vguihelpers.cpp:77-80 |
5.2 查找与使用#
Find找不到令牌时返回NULL;FindSafe在非_CERT构建中返回#FIXME_LOCALIZATION_FAIL_MISSING_STRING,_CERT构建中返回空串:localize/localize.cpp:812-838。ConstructString支持用变参或KeyValues变量填充格式串:public/localize/ilocalize.h:113-115、public/localize/ilocalize.h:131-148。- Scaleform:Flash 中以
#开头的文本键经ScaleformTranslatorAdapter::Translate转成 UTF-8,交给ScaleformUIImpl::Translate,后者调用g_pVGuiLocalize->Find并把按键字形关键字替换成 HTML:scaleformui/scaleformuiimpl/scaleformuiintegration.cpp:151-169、scaleformui/scaleformuiimpl/scaleformuitranslationimpl.cpp:930、scaleformui/scaleformuiimpl/scaleformuitranslationimpl.cpp:1016-1028。 - 引擎:资源下载进度等文本直接用
FindSafe/ConstructString取本地化串:engine/client.cpp:1802、engine/client.cpp:1822。 - 服务器发往客户端的文本常以
#令牌形式出现(例如拒绝连接与断开原因#Valve_Reject_*、#GameUI_Disconnect_TooManyCommands),由客户端本地化后显示(推断,依据是这些字符串的格式):engine/baseserver.cpp:609、engine/sv_client.cpp:1547。
6. 专用服务器的 VGUI 管理界面#
- 何时启用:Windows 上默认使用 VGUI 界面,带
-console时改用文本控制台;Linux 只有文本控制台:dedicated/sys_ded.cpp:371-389。 - 启动:
ConsoleStartup调用StartVGUI,并在配置界面完成前循环RunVGUIFrame;用户在配置界面退出时返回失败:dedicated/sys_ded.cpp:284-301。之后服务器循环的每次迭代都会RunVGUIFrame:dedicated/sys_ded.cpp:187-191。 StartVGUI:加入MAIN/PLATFORM搜索路径,配置目录优先放在 Steam 安装目录下;创建CMainPanel作为 surface 的嵌入面板;加载 Scheme 与三个本地化文件;启动ivgui();然后加载AdminServer模块并查询IAdminServer与VGuiModuleAdminServer001:dedicated/vgui/vguihelpers.cpp:40-100。CMainPanel构造时创建“Config”页(CCreateMultiplayerGameServerPage):dedicated/vgui/MainPanel.cpp:90。AdminServer不在仓库中:构建脚本把它指向Tracker/AdminServer/AdminServer.vpc,而该目录不存在,仓库只保留接口头文件common/IAdminServer.h、common/IManageServer.h:vpc_scripts/projects.vgc:31-33、dedicated/vgui/MainPanel.cpp:28-31。StartVGUI对加载失败只打印错误并继续(dedicated/vgui/vguihelpers.cpp:86-94),因此用本仓库构建时管理界面推断只剩配置页。- 专用服务器工程编译
dedicated/vgui/下的三个源文件并链接vgui_controls:dedicated/dedicated.vpc:53-56、dedicated/dedicated.vpc:125。
约束与边界#
- 平台差异:vgui2 自带的 GDI surface 只在 Windows 编译,游戏进程中被
vguimatsurface取代;Linux/OSX 专用服务器没有 VGUI 界面:vgui2/src/vgui_dll.vpc:50、dedicated/sys_ded.cpp:380-383。 - 主机平台:控制台的显示/隐藏在主机平台上为空操作,Xbox 360 上
toggleconsole直接返回:engine/vgui_baseui_interface.cpp:1415-1418、engine/console.cpp:257-260。 - 失败路径:Scheme 文件缺失、取不到
IGameUI或(PC 上)IGameConsole都是致命错误:engine/vgui_baseui_interface.cpp:733-747、engine/vgui_baseui_interface.cpp:776-782。 - 失效代码:
LocalizedStringTable.cpp未参与构建(见第 1 节)。 - 本页未展开:
vgui_controls各控件实现、Scheme 与字体光栅化、vgui2/dme_controls、matsys_controls、game_controls等工具侧控件库、HTML 窗口、serverbrowser模块、cstrike15/gameui中各 VGUI 对话框的具体内容,以及vlocalize工具。