← ラボに戻る

macOS で --headless を使わずに Godot の Movie Maker とスクリーンショットを撮る

godotapple-siliconguide

Mac で、Linux の CI と同じようにスクリプトから Godot にスクリーンショットや短い動画を書き出させたかった。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 ディスプレイを置くのが普通の回避策だ。macOS 版の Godot は 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 は出力をファイルに戻す。これがないと、Godot はシェルの子プロセスではないので、open が出力を捨ててしまう。フレームは 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

結果は 24 fps の 48 フレームで、長さは 2.0 秒だった。

未解決:フォーカス

no_focus はウィンドウがキーボードフォーカスを取るのを防ぐが、Godot はウィンドウを開くとき、やはり自分をアクティブなアプリにする。二回の実行のあいだ、最前面のアプリを繰り返し調べた。一枚だけ撮る実行では 21 回の測定のうち 1 回、約一秒かかった Movie Maker の実行では 4 回続けて Godot が前面にいて、Godot が終了するたびに、作業していたアプリが前面に戻った。9月には、LSUIElement を true にしたアプリバンドルのコピーも効かなかった。もっと長い録画のあいだ Godot がどれだけ前面に居続けるかは、測っていない。

制限

macOS で --headless を使わずに Godot の Movie Maker とスクリーンショットを撮る · Rodion Kazennov