游戏循环嵌入 XUI:场景渲染与 UI 渲染分层、输入路由、帧循环整合。
游戏/应用中 XGE 场景与 XUI 界面共存:
• 渲染顺序:先绘制游戏场景,再绘制 UI 覆盖层
• 输入路由:UI 优先消费输入,未消费传递给游戏
• 帧循环:Update → GameRender → UI Update → UI Render
• 场景栈:xgeSceneSet 管理游戏场景,XUI 独立于场景
• 暂停:UI 模态弹出时暂停游戏逻辑
| 阶段 | 操作 |
|---|---|
Input | xuiDispatchEvent → 游戏输入 |
GameUpdate | 物理/AI/动画(UI 模态时跳过) |
GameRender | xgeClear → 场景绘制 |
UIUpdate | xuiDispatchPendingEvents |
UIRender | XUI 控件树渲染(覆盖游戏画面) |
Present | xgePresent |
gcc -O2 -I. -o ch202.exe ch202_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch202.exe
void OnFrame(void) {
float fDT = xgeGetDelta();
// 1. 输入 → XUI 优先
int bConsumed = DispatchInputToUI();
// 2. 游戏逻辑(UI 模态时暂停)
if (!ModalActive() /* 用 xuiPopupIsModal/xuiMsgBoxIsModal 按实例查询 */) {
GameUpdate(fDT);
}
// 3. 游戏渲染
xgeClear(0x0A0A1AFF);
GameRender();
// 4. XUI 更新 + 渲染
xuiDispatchPendingEvents(pCtx);
xuiUpdate(pCtx, fDT);
xuiLayout(pCtx); /* 绘制由根控件经代理完成 */
// 5. 未消费输入 → 游戏
if (!bConsumed)
GameInput();
}将以下代码保存为 ch202_main.c,编译后运行即可看到游戏渲染 + XUI 覆盖的帧循环架构:
#include "xge.h"
#include "xui.h"
static xui_context g_pCtx;
static float g_fAngle = 0.0f;
static int onFrame(void* pUser) {
(void)pUser;
float dt = xgeGetDelta();
/* 1. 游戏逻辑 */
g_fAngle += dt * 90.0f;
/* 2. 游戏渲染 */
xgeClear(0x0a0a1a);
xgeShapeRectFill(270, 190, 100, 100, 0x4FD8C2FF);
/* 3. XUI 输入 + 更新 + 渲染(覆盖游戏画面) */
xui_vec2_t pos;
xgeMouseGet(&pos.x, &pos.y);
xuiInputPointerMove(g_pCtx, pos.x, pos.y);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
else
xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);
xuiUpdate(g_pCtx, dt);
xuiLayout(g_pCtx);
xuiRender(g_pCtx, NULL, NULL, 0);
return XGE_OK;
}
int main(void) {
xge_desc_t d = {0};
d.iWidth = 640; d.iHeight = 480;
d.sTitle = "ch202 - Game + UI Loop";
d.iFlags = XGE_INIT_VSYNC;
xgeInit(&d);
xuiCreate(&g_pCtx);
extern const xui_proxy_t tXgeProxy;
xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
xuiSetViewportSize(g_pCtx, 640, 480);
xui_widget pRoot;
xuiPanelCreate(g_pCtx, &pRoot, NULL);
xuiWidgetSetRect(pRoot, (xui_rect_t){0, 0, 640, 480});
/* HUD 按钮覆盖在游戏画面上 */
xui_widget pBtn;
xuiButtonCreate(g_pCtx, &pBtn, NULL);
xuiWidgetAddChild(pRoot, pBtn);
xuiLabelSetText(pBtn, "Pause");
xuiWidgetSetRect(pBtn, (xui_rect_t){540, 20, 80, 36});
xuiWidgetSetRect(pBtn, (xui_rect_t){0, 0, 80, 36});
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}