こんにちは。今回は私が取り組んでいる、「DQ7 TAS」の動画で、どのようにメモリの内容を動画に反映させているかをここで紹介したいと思います。
まずはどのように動画上で表示されているのか、こちらの動画を御覧ください(ダイマ)。
この動画の右下のように、
- フレーム数
- 時間
- 乱数
- 戦闘キャラクターの位置
を動画に表示する方法を説明します。
環境
まずはDQ7 TASはどのような環境で制作が行われているか説明します。 以下の環境で制作が行われています。
- Windows10(2004)
- Bizhawk 2.5.2
- Aviutl 1.10
TAS制作においてWindows以外のOSは人権が無いので、Windows環境を用意して下さい。 また、Bizhawkはバージョン2系あたりからパフォーマンスが劇的に向上して安定性も増しているので、特に理由がなければ最新版を使用することをお勧めします。 Aviutlはスクリプトを受け付けてくれる数少ないフリーの動画編集ソフトなので使用していますが、他にスクリプトを使用できる動画編集ソフトがあれば代替できます。(というかあったらぜひ教えていただきたいです。)
全体の流れ
では実際にどのようにして表示しているかを説明します。流れとしては、
- Bizhawkでのゲーム画面収録と同時に、RAM内容をテキストに書き出す(今回はCSV)Luaスクリプトを動作させる。
- AviutlでLuaスクリプトを使って上記のテキストファイルを読み込み、動画に反映する。
以上の内容となっています。順を追って説明します。
RAM内容を書き出す
Bizhawkでは下のように、 TooldからLuaコンソールを呼び出すことができ、ここからLuaスクリプトを起動することができます。 ここで実際に実行しているコードは以下になります。
require "utils"
START_FRAME = 0
STOP_FRAME = 91440
local function start(frame)
if frame == START_FRAME then
return false
end
return true
end
local function stop(frame)
if frame == STOP_FRAME then
return false
end
return true
end
local function mainloop()
console.log("start frame: " .. START_FRAME)
console.log("stop frame: " .. STOP_FRAME)
local frame = emu.framecount()
while start(frame) do
emu.frameadvance()
end
local file = io.open("movie.csv", "w")
console.log("file open")
while stop(frame) do
frame = emu.framecount()
local time = getTime()
local r_number = getRNum()
local pos_x = getPosX()
local pos_z = getPosZ()
file:write(frame .. "," .. time .. "," .. r_number .. "," .. pos_x .. "," .. pos_z .. "\n")
emu.frameadvance()
end
file:close()
console.log("file close")
end
mainloop()
コード中に出てくる、getTime,getPosX等はそれぞれのゲームに合わせたメモリ値を返す関数です。自分が使用するゲームに合わせて変更して下さい。また、このスクリプトでは、最初にあるSTART_FRAME,STOP_FRAMEの値を変更することで、記録するフレームの範囲を指定することができます。 Bizhawkでは様々なLuaの組み込み関数が使用できるので、以下のサイトも参考にしてみて下さい。
http://tasvideos.org/Bizhawk/LuaFunctions.html
Aviutlでテキストを読み込ませる
BizhawkでRAMの内容をCSVに書き出せたところで、これをAviutlで読み込んで表示する方法を説明します。
Aviutlは拡張編集のプラグインを導入するところまで進めて下さい。
そして拡張編集でメディアオブジェクトの追加からテキストを生成します。
Aviutlのテキストでは、<? ?>
で囲んだ部分はスクリプトとみなされるため、この中にスクリプトを書きます。
実際に使っているスクリプトは以下になります。
CSV_PATH = ".\\script\\movie.csv"
local function split(str, d)
local s = str
local t = {}
local p = "%s*(.-)%s*" .. d .. "%s*"
local f = function(v)
table.insert(t, v)
end
if s ~= nil then
string.gsub(s, p, f)
f(string.gsub(s, p, ""))
end
return t
end
function main()
local csv_row = {}
local row_num = 0
for line in io.lines(CSV_PATH) do
csv_row[row_num] = line
row_num = row_num + 1
end
local row = split(csv_row[obj.frame], ",")
local frame = string.format("%7d", row[1])
local time = row[2]
local r_num = string.format("%8s", row[3])
local pos_x = string.format("%5d", tonumber(row[4]))
local pos_z = string.format("%5d", tonumber(row[5]))
local text = "Frame: " .. frame .. "\nTime: " .. time .. "\nR_NUM: " .. r_num
local position = "\nX: " .. pos_x .. " Z: " .. pos_z
obj.mes(text .. position)
end
main()
先程Bizhawkから生成したCSVをAviutlのscriptフォルダの中に配置するか、スクリプトの1行目のCSV_PATHを変更するかして、CSVを読み込めるようにします。すると、 このようにCSVの内容を表示することができます。 あとは収録したゲーム動画を一緒に配置するだけです。
BizhawkのAV/WAV
基本的にBizhawkでゲーム画面を収録するときには、Bizhawkに実装されているAV/WAVを使用します。 最近のBizhawkでは収録にffmpegが使用できるので、私は容量も考えてffmpegを使ってmp4で収録しています。 収録の際は右上のAlternate Syncを切る必要があります。
Bizhawkで収録したPS1の動画は昔はフレームレートが59.29だったので非常に扱いにくかったのですが、バージョン2.5.0から修正され、現在は一般的な59.94で収録できるようになっています。 なぜフレームレートの話をしているかというと、先程のRAMの画面表示をYoutubeでの再生と合わせるためにはフレームレートを合わせることが重要だからです。Youtubeやニコニコに動画を上げることを考えると、ある程度一般的なフレームレートで動画を作成しないと、画面出力とのズレが大きくなってしまいます。 というわけで
- Bizhawkで一般的なフレームレートで収録する
- Aviutlで一般的なフレームレートで動画を作成する
- 動画をYoutubeやニコニコに上げる
ということが必要です。これが満たされると、Youtubeでの再生時間と、画面表示の時間がだいたい合うようになります。 実際は、Youtubeで59.94fpsの動画を上げると60fpsとみなされ、少しずれてしまうのですが、59.29fpsのときはかなりずれていたので改善されてよかったと思います。
また、私が普段使っているLuaスクリプトはGithubで公開しています。 よかったらご覧になって下さい。
参考
http://gocha.hatenablog.com/entry/20090923/RAMWatchOnVideoLua
以上です。お読みいただきありがとうございます。またよろしくお願いします。
2021.01.25 14:00 2021.01.25 14:00