haruki's Blog

← 記事一覧

2026-06-14

UE5 でマルチカメラモニタリングシステムを作る

UE5 マルチカメラモニタリングシステム

こんにちは!haruki です。今回は UE5 で「バーチャルライブ配信向けマルチカメラモニタリングシステム」を作ってみたので、その実装ポイントを解説します。

ビデオスイッチャーのように、レベル内に置いた複数のシネマティクカメラの映像を別ウィンドウで同時にプレビューして、クリックで Preview / Program を切り替える、というものです。

UE5のエディタを立ち上げず実行ファイルなどでも使えるようにしたかったため、EUWではなく、UMGで作成しています。 UMG だけだと別ウィンドウを作る機能は用意されていないので、Slate を直接触る必要があります。

UIはまだ微妙ですが、機能的には出来ていると思うので良ければ参考にしてください。


やりたかったこと・要件

  • レベル内に複数のシネマティクカメラを配置
  • すべてのカメラ映像を独立したウィンドウで同時にプレビュー
  • クリックで Preview(次に出す予定)と Program(本番出力)を切り替え
  • 何番のカメラが選択されてるかわかるようにする
  • カメラの台数が増減してもコード変更なしでセットアップできるようにする

前提条件

  • Unreal Engine 5.5

システム全体像

機能は大きく3つに分解できます。

  • カメラを切り替える機能 — ボタンでPreview画面の切り替え / Preview→Program のスイッチングロジック
  • カメラを RenderTarget で焼きだす機能 — CineCameraActorの映像をテクスチャとして取り出す
  • 直感的に切り替える UI — 別ウィンドウでサムネを並べ、クリック操作で切り替える

データの流れはこんな感じです。

flowchart LR
    Scene["UEシーン<br/>"] --> Cam1["BP_CineCameraActor #1"]
    Scene --> Cam2["BP_CineCameraActor #2"]


    Cam1 -- SceneCapture --> RT1["RT_Camera_01"]
    Cam2 -- SceneCapture --> RT2["RT_Camera_02"]

    Cam1 -. FOV 同期 .-> RT1
    Cam2 -. FOV 同期 .-> RT2

    RT1 --> MI["MI_RenderTarget_01<br/>(M_RenderTarget)"]
    RT2 --> MI
    MI --> ViewW["WBP_CameraView (Image)"]
    ViewW --> Main["WBP_CameraMonitor_Main"]
    Main --> SWin["SWindow<br/>(セカンドウィンドウ)"]

各カメラが毎フレーム自分専用の RenderTarget に描画して、それをマテリアル経由で UMG の Image に表示、UMG 全体を Slate ウィンドウに載せて別ウィンドウで開く、という構成です。

以下、3つの機能ごとに実装を解説していきます。


機能①: カメラを RenderTarget で焼きだす

BP_CineCameraActor のコンポーネント構成と Tick での FOV 同期ノードグラフ

CineCameraActorの映像をテクスチャとして取り出す部分から作っていきます。

ここを実装するアセットが BP_CineCameraActor です。親クラスは CineCameraActor(UE 標準のシネマティクカメラ)で、そこに SceneCaptureComponent2D を追加しています。

追加した変数はシンプルです。

  • CameraID (int) — カメラ識別子
  • RenderTargetTexture (TextureRenderTarget2D) — 出力先の RT

やっていることは、シネカメラの絵作り(焦点距離・センサーサイズ・被写界深度)を保ちつつ、その視点をそのまま RT に複製すること。普通の CameraActor ではなくシネカメラを採用しているのは、トラッキングフォーカスなど映像制作で使いたい機能がデフォルトで揃っているからです。

FOV の同期

ここでは CineCamera と SceneCapture の FOV を連動させる仕組みを実装します(画像右)。

SceneCapture と CineCamera は別物なので、CineCamera 側で焦点距離やセンサーサイズを変更しても、SceneCapture の絵作りは追従してくれません。これを放置するとモニタープレビューと本番カメラの絵がずれて、配信用途では致命的です。

解決策は単純で、毎 Tick で CineCamera の現在の FOV を取得して SceneCapture に流し込むだけです。

これだけで、シネカメラのレンズ設定変更が SceneCapture 側にも自動追従するようになります。

データ駆動でカメラを管理する

DT_CameraConfig DataTable に各カメラの名前と RenderTarget を登録した一覧

カメラの追加・削除を楽にするために、設定情報は DataTable に寄せています。

  • ST_CameraConfig (Struct)
    • CameraName (FString)
    • RenderTarget (TextureRenderTarget2D)
  • DT_CameraConfig (DataTable)
    • RowStruct: ST_CameraConfig
    • Row: RT_Camera_01RT_Camera_06 を登録

こうしておくと、

  • カメラの追加・削除が DataTable 編集だけで済む
  • 名前と RT の参照を一元化できる
  • Blueprint からのハードコードを排除できる

というメリットがあります。マテリアルは M_RenderTarget(ベース)+ MI_RenderTarget_01(インスタンス)を Image ウィジェットに割り当てて RT を表示しています。


機能②: カメラを切り替える

スイッチャー UI で Preview と Program のカメラを表示している様子

次に、Preview / Program を切り替えるスイッチングロジックを実装します。

スイッチャーは「今、本番に出ているカメラ(Program)」と「次に出す予定のカメラ(Preview)」の2系統を持つのが基本です。テレビ局のスイッチャーと同じ考え方で、いきなり Program に切り替えると放送事故のリスクがあるため、一度 Preview にセットしてから Take 操作で Program に反映する、という流れを採っています。 Preview → Take → Program の切替フロー図解

切替フロー

  1. グリッドの中からカメラサムネをクリック → Preview に乗る(ゲーム画面はまだ切り替わらない)
  2. Take ボタンを押すと Program に反映され、ゲーム画面のカメラが切り替わる
  3. 直前まで Program だったカメラは次の Preview 候補へ

クリック即切替は配信現場ではやらないので、ここはハード要件として最初から組み込んでいます。

内部で持っている状態

UI 側(WBP_CameraMonitor_Main)で次の状態を管理しています。

  • PreviewCamera — 現在 Preview にセットされている CameraID
  • ProgramCamera — 現在 Program に出ている CameraID
  • OnCameraClicked — 個別カメラビューのクリックハンドラ

クリックされた CameraID を PreviewCamera に書き込んで色を更新、Take ボタンが押されたら ProgramCamera に転送して、ゲームのアクティブカメラを切り替える、という流れです。


機能③: 直感的に切り替える UI

最後に、ここまでの機能をオペレータが触るための UI 部分です。今回の実装で一番ボリュームがあるところでもあります。

UI は次の2段構成になっています。

  • WBP_CameraMonitor_Main — カメラビューをグリッド表示するルートウィジェット
  • BP_SecondWindowActor — Slate で独立した第2ウィンドウを開いて、上記の UMG を載せる

ゲームのメインビューと同じウィンドウに UI を出してしまうと配信本番の映像とぶつかってしまうので、完全に独立した OS ウィンドウとして表示しています。

メイン UI: WBP_CameraMonitor

WBP_CameraMonitor_Main で複数のカメラビューをグリッド表示しているレイアウト

第2ウィンドウのルートウィジェットです。複数の WBP_CameraView をグリッド表示します。

主な変数とイベントはこんな感じです。

  • CameraContainerWBP_CameraView を並べるコンテナ
  • CameraMap (Map<int, WBP_CameraView>) — CameraID から個別ビューを引くテーブル
  • InitCamera(CameraID) — 起動時にカメラ一覧を構築

初期化フロー

ポイントは GetAllWidgetsOfClass(WBP_CameraView) で配置済みのカメラビューを動的に集めてくるところです。各 WBP_CameraView に対して InitCamera(CameraID) を呼んで関連付けすれば、カメラ台数の増減にコード変更なしで追従できます。

個別カメラビュー WBP_CameraView

WBP_CameraView の構成(RT 表示用 Image、カメラ名テキスト、選択状態を示す枠色)

  • IMG_CameraFeed — RT をマテリアル MI_RenderTarget_01 経由で表示
  • TXT_CameraNameDT_CameraConfig から表示名を取得
  • CameraViewButtonColor — 選択状態を色でフィードバック
  • クリック → OnCameraClicked 発火 → スイッチャー側で Preview / Program を更新

「いま何番のカメラが Preview / Program なのか」を色枠で示すことで、オペレータが目で見て即判断できるようにしています。

BP_SecondWindowActor

C++ で作成したクラスを継承した Blueprint です。MonitorWidgetClassWBP_CameraMonitor_Main を設定して、レベルに1つ置くだけで第2ウィンドウが立ち上がります。

ライフサイクル

  • BeginPlay → 自動オープン
  • EndPlayCloseWindow() で確実に後片付け(PIE 終了時にウィンドウが残らない)
  • ウィンドウ側の × クリック → HandleWindowClosedSpawnedWindow を Reset

まとめ

マルチカメラモニタリングシステムは、機能で分解すると「RenderTarget への焼き出し」「Preview / Program のスイッチング」「第2ウィンドウ上の UI」の3つに分けて整理できます。どれも UE 標準機能の組み合わせで実現できるので、慣れてしまえばそこまで複雑ではないかなと思います。

「UMG を別ウィンドウに出す」機能をプラグイン化しておくと色々な場面で使い回せるようにしておくと便利です。また、レンダーターゲットは描画負荷が高いので、それを下げる方法なども残していく予定です。

同じようなマルチカメラビュー機能を作りたい方の参考になれば幸いです。

最後まで読んでいただきありがとうございました。次回もぜひお楽しみに。


参考