AIでストリートファイターを実況する技術

AIに格ゲー実況させたい

AI Tuberというものが気になってます。
AIが感想をコメントしながら格闘ゲームをしている姿が見てみたいです。
しかし、格闘ゲームをプレイするにはリアルタイムな応答性が必要です。
現時点で格闘ゲームをLLM、VLMにプレイさせるのは難しいですが、人間やCPUがプレイした動画にあたかもAI自身がプレイしたかのようなコメントを入れることは可能なのでは?と考え、実験してみました。

今回の対象ソフトはストリートファイターVです。

実況コメントするには盤面の変化を適切に捉え、喜怒哀楽を表現しなくてはなりませんがうまくいくのでしょうか?

マルチモーダルモデルに放り込む

まず思いつくのが動画をマルチモーダルなモデルに入力し、キャラ設定を渡してコメントさせる手法。
実はgemini-2.5-proが出たあたりで一度実験したのですが、自キャラと対戦相手の区別が付かないという問題がありました。
gemini-3-proが出た今なら何とかなるんじゃないの?というのが今回再実験を始めたきっかけでもあります。

結果

gemini-3-proでも敵味方の区別に失敗します。自キャラに必殺技が当たって大喜びする様子を見るのはなんとも悲しい気持ちになります。

失敗の原因分析:

  • 動画全体を渡すと、VLMは「どの瞬間を見ればいいか」がわからない
  • 格闘ゲームは1秒間に何度も状況が変わるため、漠然と見せても正確な判断ができない
  • 「P1が攻撃した」「P2が攻撃した」の区別は、VLMには判断が難しい

ゲージを見ろ

次の作戦として、マルチモーダルなモデルに体力ゲージだけを注視させます。
まずは体力ゲージの推移ログを作り、ログと試合動画を見比べながらコメントをさせる作戦です。
これならVLMの役割がシンプルになるので成功しそうです。

結果

これも失敗。
ゲージだけ注視しろと言っても無視したり、ゲージから正確な割合を判定できないという問題がありました。

失敗の原因分析:

  • VLMは「ゲージが何%か」を正確に読み取るのが苦手
  • プロンプトで「ゲージだけ見ろ」と言っても、画面全体を見てしまう

体力のログは自力で作る(フェーズ1)

体力ゲージのログを取るだけならVLMじゃなくてもできます。
OpenCVという画像処理ライブラリを使い、体力ゲージの様子を100ms単位でログに出力させます。

工夫したポイント:

  • 体力ゲージは「赤→黒」でダメージが確定する仕様を考慮
  • キャラクターがジャンプするとゲージが隠れる問題→前フレームの値を維持
  • ラウンド開始時の発光演出→異常値として除外
  • 体力は増えないという制約を利用したノイズ除去

結果

これはうまくいきました。 キャラクターがジャンプすると体力ゲージが隠れたり、ラウンド開始時の発光など、イレギュラーなイベントもありましたが、「体力ゲージは増えない」、「赤→黒でダメージが確定する」など細かい仕様を加えることで、ある程度正確なログが取れています。 この体力ログ生成はフェーズ1として採用します。

ダメージイベント抽出(フェーズ2)

体力の推移ログから、実際にダメージが発生したタイミングを抽出します。
これにより100ms単位で存在していたログを圧縮します。

  • Phase1の体力ログ(100ms単位)を読み込み
  • 体力の減少を検出してダメージイベントに変換
  • 500ms以内の連続ダメージは1イベントにマージ
  • イベントタイプを分類(p1_damage, p2_damage, exchange, ko, round_start)

結果

timestamp_ms,event_type,target,damage,p1_health,p2_health
7500,round_start,-,0,100,100
11600,p2_damage,p2,3.2,100,96.8
12300,p1_damage,p1,2.1,97.9,96.8
...

ダメージが発生したタイミングをまとめた、タイムラインの作成に成功しました。

具体的な試合内容を記録する(フェーズ3)

タイムラインからはダメージが入ったタイミングとダメージ量が読み取れます。
また、ダメージが入っていない時間からは読みあい、牽制などで試合が硬直していたことが読み取れます。
VLMにはタイムライン上の各イベントが、具体的にどのような内容だったかを確認させ、より具体的な試合内容を記録させます。

フレーム抽出方式

動画全体を渡すのではなく、各イベントのタイムスタンプ周辺の5フレーム(0.5秒間)だけを画像として抽出してVLMに渡します。

この方式のメリット:

  • VLMが「どの瞬間を見ればいいか」が明確になる
  • 処理時間の短縮(動画全体を処理する必要がない)
  • 攻撃の前後を見ることで、何が起きたかを判断。

イベントの判定にも体力のログを使う

体力のログから判定できることはそちらで判定し、VLMは視覚的な特徴のみを担当させました。

判定項目 担当 理由
誰が攻撃したか CV 体力が減った側が被弾者
コンボかどうか CV イベント間隔で判定可能
ダメージ量 CV 体力の差分から計算
投げか打撃か VLM 視覚的な判断が必要
飛び道具か VLM 視覚的な判断が必要
対空か VLM 視覚的な判断が必要

様子見イベントの挿入

イベント間の空白時間が長いと、実況が沈黙してしまいます。これを解決するため、3秒以上の空白時間に「様子見」イベントを挿入します。

結果

タイムラインを渡したおかげで、VLMが敵味方を間違えることがなく、試合の詳しい情報を取得できました。

フェーズ3の出力例:

timestamp_ms,time,situation,emotion,intensity,attacker,is_p1_attacking
7500,7.5,ラウンド開始,緊張,medium,,
11600,11.6,牽制ヒット,淡々,low,p1,True
45600,45.6,コンボ被弾開始,悔しさ,high,p2,False
51500,51.5,コンボ開始,興奮,high,p1,True
70900,70.9,KO勝利,歓喜,high,p1,True

コメントを付ける(フェーズ4)

具体的なイベントを記録したタイムラインができました。
次は、タイムライン上のイベントごとにLLMに適切なコメントをさせます。
コメントの内容はキャラ付け次第になります。今回はお嬢様系実況者という設定でコメントしてもらいました。

発話時間に基づく文字数制限

コメントとコメントの間隔が短いと、発話が終わる前に次のコメントが始まってしまいます。これを防ぐため、次のイベントまでの時間から最大文字数を計算します。

CHARS_PER_SECOND = 5  # 日本語の発話速度(余裕を持たせた値)

for scene in scenes:
    gap_sec = next_event_time - scene_time
    max_chars = max(3, min(25, int(gap_sec * CHARS_PER_SECOND)))
    scene['max_chars'] = max_chars

プロンプトでの指示はこのようになります

[11.6秒] 【自分が攻撃】 牽制ヒット (感情: 淡々, 次まで0.7秒, 最大3文字)
[12.3秒] 【相手が攻撃】 被弾 (感情: 焦り, 次まで2.9秒, 最大14文字)

キャラクター設定のカスタマイズ

今回実験したキャラクター設定:

1. LLM専門用語キャラ

- 攻撃成功: 「推論的中!」「重み最適!」「loss低下!」
- 被弾: 「勾配爆発!」「NaNエラー!」「過学習!」
- コンボ: 「連鎖推論!」「テンソル全開!」「並列化!」
- ピンチ: 「VRAM残りわずか!」「GPU温度上昇中…」

2. お嬢様キャラ

- 攻撃成功: 「参ります!」「お見事」「華麗に」
- 被弾: 「まあ!」「失礼な!」「無礼な!」
- コンボ: 「舞いますわ!」「優雅に」
- 勝利: 「おほほ、当然の結果ですわ!」

他のキャラクター案

  • 株トレーダーAI: 「利確!」「損切り!」「ボラ上昇!」
  • 軍師AI: 「作戦通り」「陽動成功」「撤退せよ」
  • 厨二病AI: 「我が力を解放…」「闇が疼く」
  • 体育会系AI: 「押せ押せ!」「根性!」「気合いだ!」
  • 医療AI: 「バイタル安定」「容態急変!」「オペ成功」

結果

どのキャラ設定でも問題なく機能しました。
ただ、キャラが濃すぎると何を言っているのかわからないことが多くなりますね…

お嬢様キャラの出力例:

timestamp_ms,time,situation,comment,emotion
7500,7.5,ラウンド開始,幕開けですわね。,緊張
15900,15.9,飛び込みヒット,華麗ですわ,喜び
21000,21.0,被弾,きゃっ、失礼な!,焦り
45600,45.6,コンボ被弾開始,無礼な!,悔しさ
51500,51.5,コンボ開始,舞いますわ!,興奮
70900,70.9,KO勝利,おほほ、当然の結果ですわ!,歓喜

動画とコメントを同期表示する

実際のAI Tuberとして稼働するならTTSで音声としてコメントを動画に載せると思いますが、今回はコメントとして同期表示させました。
動画とコメントを指定することで同期表示されるhtmlを用意しました。

パイプライン全体図

これで動画にAIのコメントが表示できます。
ここまでの流れをまとめます。

入力: 試合動画
    ↓
Phase1: 体力ゲージ解析(OpenCV)
  - 100ms単位で体力%を記録
  - ノイズ除去(ジャンプ、発光演出)
    ↓
Phase2: ダメージイベント抽出
  - 体力減少タイミングを検出
  - イベントタイプ分類(damage/exchange/ko)
    ↓
Phase3: シーン解析(VLM: gemini-3-pro-preview)
  - 各イベントの前後5フレーム抽出(-200ms〜+200ms)
  - コンボ判定(CV: 3ヒット以上、1500ms以内)
  - 様子見イベント挿入(3秒以上の空白)
  - 視覚的特徴の判定(投げ/飛び道具/対空)
    ↓
Phase4: コメント生成(LLM: gemini-2.5-flash)
  - キャラクター設定に基づくコメント生成
  - 発話時間に基づく文字数制限(5文字/秒)
  - 攻撃方向に応じた感情表現
    ↓
出力: タイムライン付きコメント(CSV/JSON形式)

完成したもの

最終的に完成した動画がこちら。

youtu.be

そこそこ実況っぽくなっています! リアルタイムにコメントできない、実際にプレイできるわけではない。技の解説もたぶんできない、ということで実用的ではないですが、とりあえずコメントさせることに成功しました。

今後の発展可能性

リアルタイム化への課題

  • 現状: 動画全体を処理してからコメント生成(オフライン処理)
  • 課題: VLM/LLMのレイテンシ(数秒)が格闘ゲームのテンポに合わない
  • 可能性: モデルの進化に期待…

TTS連携

  • VOICEVOXなどのTTSと連携
  • 感情パラメータ(emotion)をTTSの感情制御に活用

まとめ

学んだこと: 1. VLMに全てを任せない: CVで確定できることはCVで処理し、VLMの役割を明確に絞る 2. フレーム抽出が効果的: 動画全体より、特定タイムスタンプのフレームを渡す方が精度が高い 3. 発話時間の考慮: 実況では「何を言うか」だけでなく「どれくらいの長さで言うか」も重要

AI Tuberはある日突然登場するというより、こういった技術の積み重ねで洗練されていくものだと思っています。この記事も何かの役に立てばいいなーと思います。