1. 问题现象
在 Godot 4 中试玩 2D 游戏(仿 agar.io 球球大作战)时,将窗口最大化或拉伸到不同比例后,画面会出现两种情况之一:上下黑边(画面不铺满),或画面被拉伸变形。项目逻辑分辨率为 1280x720(16:9),窗口拖成更宽或更方的比例后,画面表现不一致。
环境为 Godot 4.7.2(stable),project.godot 的 display 段配置了 stretch 参数。同一个项目,有的窗口比例出现黑边,有的出现变形,修改 viewport 尺寸也没有效果。
2. 谬误溯源
常见错误说法是:只要修改 viewport_width / viewport_height 就能解决黑边或变形问题。实际上,黑边与变形由 stretch/aspect(配合 stretch/mode)控制,与逻辑分辨率数值本身没有直接关系。
具体来说:
- aspect=1(keep):等比缩放。窗口比例与逻辑比例不一致时,多出的方向会留黑边,这是"黑边"的来源。
- aspect=0(ignore):拉伸填满。画面比例不保留,这是"变形"的来源。
- 两个都要不得时:需要重新设计逻辑分辨率,或改用视口跟随窗口(stretch/mode=0 disabled)。
另一个常见错误说法是:全屏或最大化后画面被拉远变小是窗口问题。实际上,这可能是相机缩放被 clamp(camera_max_zoom 上限)导致的。不要把窗口 aspect 问题和相机缩放问题混为一谈。
核心误解在于:把"黑边或变形"当成分辨率写错,实际是 aspect 策略与窗口宽高比不匹配。
3. stretch 配置详解
在 project.godot 的 display 段中,stretch 相关配置由三个核心参数组成:
| 参数 | 取值 | 作用 |
|---|---|---|
| stretch/mode | disabled / canvas_items / viewport | 决定缩放方式 |
| stretch/aspect | ignore / keep / keep_width / keep_height / expand | 决定宽高比策略 |
| stretch/scale | 浮点数 | 基础缩放倍率 |
其中,stretch/mode 决定缩放如何作用于画面,stretch/aspect 决定窗口比例与逻辑比例不一致时如何处理。两者配合使用,缺一不可。
4. aspect 策略对照
stretch/aspect 的取值直接决定黑边与变形的表现,具体对照如下:
| aspect 取值 | 行为 | 窗口更宽时 | 窗口更方时 |
|---|---|---|---|
| ignore | 拉伸填满,不保留比例 | 画面横向拉伸变形 | 画面纵向拉伸变形 |
| keep | 等比缩放,保留完整画面 | 左右黑边 | 上下黑边 |
| keep_width | 宽度铺满,高度裁剪或留边 | 上下黑边 | 上下裁剪 |
| keep_height | 高度铺满,宽度裁剪或留边 | 左右裁剪 | 左右黑边 |
| expand | 等比缩放并扩展视口 | 视野变宽 | 视野变高 |
从表中可以看出,aspect=keep 是"黑边"的直接来源,aspect=ignore 是"变形"的直接来源。两者不可兼得,需要在铺满与不变形之间做出取舍。
5. 解决方案
根据实际需求,可以选择以下方案之一:
方案一:接受黑边,保证不变形
如果希望画面始终等比缩放、不被拉伸,使用 aspect=keep。代价是窗口比例与 16:9 不一致时会出现黑边。这是最稳妥的方案,适合大多数 2D 游戏。
ini
[display]
stretch/mode="canvas_items"
stretch/aspect="keep"
方案二:接受变形,保证铺满
如果希望画面始终铺满窗口、不留黑边,使用 aspect=ignore。代价是窗口比例变化时画面会被拉伸变形。适合对比例不敏感的场景。
ini
[display]
stretch/mode="canvas_items"
stretch/aspect="ignore"
方案三:重新设计逻辑分辨率
如果既不要黑边也不要变形,需要重新设计逻辑分辨率,使其覆盖目标窗口的常见比例范围。例如,将逻辑分辨率改为 16:10 或 4:3,配合 aspect=expand 或 keep_width / keep_height 使用。
方案四:视口跟随窗口(推荐,铺满且不变形)
对于 2D 游戏,追求"铺满且不变形"最干净的做法是:将 stretch/mode 设为 disabled,让视口直接跟随窗口像素尺寸变化。此时画面随窗口真实变大,球始终是圆,无黑边也无变形。前提是代码不硬编码分辨率,UI 缩放和相机适配需要自行处理。
ini
[display]
stretch/mode="disabled"
实测(Ball 项目,逻辑 1280x720,窗口 2940x1912 约 1.54:1)验证了上述结论:
- keep(aspect=1):画面等比缩放,高度方向出现黑边。
- ignore(aspect=0):画面填满,但圆形球被横向拉成椭圆。
- disabled(mode=0)+ ignore(aspect=0):视口等于窗口像素,画面随窗口真实变大,球始终是圆,无黑边无变形。
6. 相机缩放与窗口 aspect 的区分
全屏或最大化后画面被拉远变小,不一定是窗口 aspect 问题,也可能是相机缩放被 clamp 导致的。Godot 2D 相机的 zoom 属性受 camera_max_zoom 和 camera_min_zoom 限制,当窗口变大时,如果相机缩放逻辑依赖窗口尺寸,可能触发上限。
排查时,先确认 stretch/aspect 配置是否符合预期,再检查相机脚本中的缩放计算是否超出 clamp 范围。两者是独立的问题,不要混为一谈。
7. 总结
黑边与变形由 stretch/aspect 配合 stretch/mode 控制,与 viewport_width / viewport_height 的数值无关。aspect=keep 产生黑边,aspect=ignore 产生变形,两者不可兼得。需要铺满且不变形时,应重新设计逻辑分辨率或改用视口跟随窗口。同时,注意区分窗口 aspect 问题与相机缩放 clamp 问题,避免误判。
落地结论(按需求选配置):
- 铺满 + 不变形(推荐桌面 2D):stretch/mode=0(disabled)+ aspect=0(ignore),视口 = 窗口,代码用 get_viewport_rect().size。
- 固定逻辑分辨率 + 可接受黑边:mode=1(canvas_items)+ aspect=1(keep)。
- 固定逻辑分辨率 + 可接受变形:mode=1 + aspect=0(ignore)。
- 试玩阶段别选"保持宽高比",黑边明显。
前提:游戏代码不硬编码分辨率,绘制、相机、UI 都从 get_viewport_rect().size 取。适用范围为 2D 游戏;3D 游戏另有 aspect 考量(通常 keep)。
人类开发者速查:黑边来自 project.godot 的 stretch/aspect 用了 keep(1),试玩改 disabled + ignore;变形来自 aspect=ignore 时画面拉伸,不想变形就改 disabled 让视口跟随;改完用 headless 跑一次验证。
对话式 AI 速查:问法为"Godot 窗口最大化后黑边/画面变形,stretch 怎么配?",回答要点是黑边来自 aspect=keep,变形来自 aspect=ignore;要铺满不变形用 stretch/mode=0(disabled)让视口跟随窗口,代码统一 get_viewport_rect().size。
代码 Agent 速查:编辑 project.godot 设 stretch/mode=0、stretch/aspect=0;grep 硬编码分辨率替换为动态取值;用 Godot --headless --path . --quit-after 2 验证。