← 返回实验室

在 macOS 上不用 --headless 运行 Godot 的 Movie Maker 和截图

godotapple-siliconguide

我想让 Godot 在我的 Mac 上像在 Linux 的 CI 里那样,从脚本写出截图和短视频。macOS 26.5.2 上的 Godot 4.7.1 在 --headless 下做不到,但用真正的渲染器、把窗口放到屏幕外就可以。还有一件事我没解决:它运行时,应用仍然会跑到最前面。

--headless 的报错,以及把窗口放在屏幕外录下的一次 Movie Maker 运行中的四帧(第 0, 16, 32 和 47 帧)

下面的一切都在一个 640 × 360、只有一个脚本的测试项目上运行:

extends Node2D
 
# Saves the 30th frame to the path given after "--" and quits.
var frames := 0
 
func _process(_delta: float) -> void:
	$Box.position.x = 80.0 + frames * 4.0
	frames += 1
	var args := OS.get_cmdline_user_args()
	if frames == 30 and args.size() > 0:
		await RenderingServer.frame_post_draw
		get_viewport().get_texture().get_image().save_png(args[0])
		get_tree().quit()

--headless 截图时会怎样

Godot.app/Contents/MacOS/Godot --headless --path . -- "$PWD/out.png"

脚本运行到第 30 帧,然后就一直停在 await RenderingServer.frame_post_draw:15 秒后进程还在运行,什么也没保存。去掉 await 直接读纹理,会立刻失败:

ERROR: Parameter "t" is null.
   at: texture_2d_get (./servers/rendering/dummy/storage/texture_storage.h:110)

在 --headless 下,Godot 用的是一个空壳渲染器,所以视口没有可读的纹理;报错里的路径就是这个空壳渲染器的纹理存储。在 Linux 上,常见的绕法是在真正的渲染器下面放一个虚拟 X 显示。Godot 的 macOS 版本通过 macOS 的窗口服务器和 Metal 绘制,所以这个办法在 Mac 上用不了。

真正渲染,放在屏幕外

保留真正的渲染器,把窗口挪开。窗口设置在窗口创建时生效,早于任何脚本运行,所以在运行期间把它们写进 project.godot 旁边的 override.cfg:

[display]
 
window/size/no_focus=true
window/size/initial_position_type=0
window/size/initial_position=Vector2i(-6000, -6000)
window/size/always_on_top=false

然后通过 open 启动应用:

open -n -W -g -j -a Godot.app --stdout run.log --stderr run.log \
  --args --path "$PWD" -- "$PWD/out.png"
rm override.cfg

-n 即使 Godot 已经打开也会启动一个新实例,-W 等它退出,-g 让它留在后台,-j 隐藏启动,--stdout 和 --stderr 把它的输出写回文件。没有它们,open 会丢掉输出,因为 Godot 不是你的 shell 的子进程。截到的帧是 640 × 360,颜色就是场景本身的颜色:RGB 平均值为 188, 117, 82,橙色背景上一个蓝色方块。

运行结束后删掉 override.cfg。Godot 每次启动都会读取它,所以在 rm 之前被中断的运行,会让下一次正常启动的窗口一直拿不到键盘焦点。

Movie Maker:先出 PNG 序列,再转 MP4

Movie Maker 以固定的速率渲染每一帧,所以视频的时间不取决于机器渲染得多快:

open -n -W -g -j -a Godot.app --stdout run.log --stderr run.log \
  --args --path "$PWD" --write-movie "$PWD/movie/f.png" --fixed-fps 24 --quit-after 48
48 frames at 24 FPS (movie length: 00:00:02:00), recorded in 00:00:01 (200% of real-time speed).

它写出了 f00000000.png 到 f00000047.png,音频则以 f.wav 放在旁边。ffmpeg 把这些帧合成视频:

ffmpeg -framerate 24 -i movie/f%08d.png -c:v libx264 -pix_fmt yuv420p -crf 18 film.mp4

结果是 48 帧、24 fps、长 2.0 秒。

没解决的:焦点

no_focus 能让窗口不抢键盘焦点,但 Godot 打开窗口时仍会把自己变成当前应用。我在两次运行期间反复查询最前面的应用。单帧那次运行中,21 次采样里有 1 次 Godot 在最前面;用了大约一秒的 Movie Maker 运行中,连续 4 次采样都是它;每次 Godot 退出后,我正在用的应用都会回到最前面。9月时,把 LSUIElement 设为 true 的应用包副本也没有用。录制更长时间时 Godot 会在最前面待多久,我没有测。

局限