浏览器窗口化游戏分辨率适配:CDP 外层尺寸 vs 内容区尺寸(含标题栏陷阱)

📅 2026/8/1 1:55:08 👁️ 阅读次数 📝 编程学习
浏览器窗口化游戏分辨率适配:CDP 外层尺寸 vs 内容区尺寸(含标题栏陷阱)

触发场景

用 Playwright/CDP 控制浏览器窗口化运行游戏(本文以 OpenYRWeb 红警2 浏览器引擎为例)时,会踩到一个隐蔽的分辨率坑:游戏窗口化(非全屏)后,主菜单和刚进游戏一切正常,但只要在游戏内点击场景或 UI,底部命令栏、菜单按钮就超出浏览器可视区,几乎点不到;截图看渲染范围比可视区大出一截。

排查后确认根因不在游戏渲染层,而在外部控制脚本的分辨率换算:脚本用 CDPBrowser.getWindowBounds拿到的是窗口外层尺寸(含标题栏/边框),直接调page.setViewportSize设置页面视口,导致页面 viewport 比浏览器可视区(内容区)大——主菜单是 DOM 自适应所以正常,游戏内 HUD 用像素定位(底部 UI 按viewport.height - 命令栏高计算坐标),自然被推到可视区之外。

另一个叠加因素是引擎对 viewport 有 800×600 最小钳制,窗口拖太小时二者冲突。本文给出完整修复:测量并缓存浏览器边框差(标题栏高度),把 CDP 外层尺寸换算成内容区尺寸再同步给页面。

谬误溯源:三个想当然的假设

第一个想当然:以为“Browser.getWindowBounds返回的就是页面可视区大小”

实际它返回浏览器窗口整体的 bounds,包含标题栏、标签页(垂直标签模式)、地址栏、边框——实测某窗口外层 1440×846,而页面可视区(内容区)是 1440×759,差约 87px(标题栏+标签栏)。直接用外层尺寸调setViewportSize,页面视口就比可视区大 87px。

第二个想当然:以为“动态算差值outer - inner就行”

实测setViewportSize强制页面视口等于设定值,之后window.innerWidth/innerHeight读到的就是你设的值,outer - inner差值会失真甚至变成负值,不能作为换算依据。

正确做法是首次测量并缓存边框差:chrome = {dx: window.outerWidth - window.innerWidth, dy: window.outerHeight - window.innerHeight},标题栏高度在窗口生命周期内恒定,只测一次。

第三个想当然:以为“游戏引擎会自动适应任意窗口大小”

实际上 OpenYRWeb 的Application.updateViewportSize()非全屏分支有Math.max(800, ...)/Math.max(600, ...)最小钳制——内容区小于 800×600 时引擎 viewport 被钳到 800×600,canvas 必然超出可视区;脚本侧还要加最小窗口保护,把窗口拉回安全尺寸。

源码验证与落地结论

验证点都在引擎源码与控制脚本里:

  • Application.ts.jsupdateViewportSize()(非全屏分支Math.min(window.innerWidth, config.viewport.width)+ 800×600 钳制)
  • Gui.handleViewportChange(viewport 同步给 renderer 与 UiScene)
  • gui/screen/game/component/Hud.tsHudFactory.create()(用uiScene.viewport布局侧边栏与命令栏,底部坐标按 viewport 高度计算)

落地修复分三步:

  1. 脚本首次启动时测量并缓存边框差chrome = {dx, dy}(标题栏高度恒定,实测 dy=87);
  2. 每次轮询用 CDPBrowser.getWindowBounds拿外层尺寸后换算成内容区cw = w - chrome.dxch = h - chrome.dy,并加Math.max(800, ...)/Math.max(600, ...)下限保护(与引擎钳制一致),再page.setViewportSize({width: cw, height: ch})
  3. 把「适应窗口」做成默认行为——引擎侧updateViewportSize非全屏分支改为直接用window.innerWidth/innerHeight(内容区),不再被config.viewport.width(如 1024)钳制,这样用户不用进图形设置手动调「分辨率→适应窗口」。

修复后日志确认换算正确(window 1440x846 → content 1440x759),窗口任意拉伸、拖动到最小尺寸,游戏内底部 UI 都完整可点。

经验总结:浏览器窗口化游戏的尺寸链路是「CDP 外层 → 边框差 → 内容区 → 引擎 viewport → HUD 像素定位」,任何一环拿错都会导致 UI 越界;标题栏差只测一次、800×600 钳制两边对齐,是这套方案的两个关键点。


人类开发者速查

四步自检:

  1. CDPgetWindowBounds是外层尺寸(含标题栏/标签页),不能直接当页面视口;
  2. 首测并缓存边框差(outer - inner,标题栏恒定,实测 dy≈87);
  3. 换算内容区cw=w-dx, ch=h-dy并加 800×600 下限,再setViewportSize
  4. 引擎非全屏分支默认用window.innerWidth/innerHeight实现「适应窗口」,免进图形设置。

对话式 AI 速查

提问模板:“playwright/CDP 窗口化游戏 UI 超出可视区 / 分辨率不对怎么修?”

应回答getWindowBounds返回外层尺寸含标题栏,需缓存边框差换算内容区再setViewportSize;动态outer-inner会失真别用;引擎有 800×600 最小 viewport 钳制,脚本要同步下限;默认分辨率想适应窗口就改updateViewportSizeinnerWidth/innerHeight

代码 Agent 速查

关键定位

  • Application.ts.jsupdateViewportSize(800×600 钳制、非全屏分支)
  • gui/screen/game/component/Hud.ts(像素定位)
  • gui/GameGui.ts.jshandleViewportChange

脚本侧Browser.getWindowBounds外层 → 缓存chrome={dx,dy}setViewportSize(cw, ch);改引擎默认分辨率时删除Math.min(innerWidth, config.viewport.width)钳制即可。