Happy My Life

日常とか技術とか

プラグインからUE4の内部処理を横取りする方法

巨大ランタイム(OS、ゲームエンジンなど)上で動くソフトをつくっていくと「この処理をHookして横取りしたいな」といったシチュエーションにたびたび出くわす。

UE4では、いくつかの処理でHookして横取りできるように、あらかじめDelegateが挟んである箇所があることに気づいた。どの程度あるかは調べてないが、わりと多そうな気がする。ここでは、スクリーンショットを例に、呼び出しかたを紹介してみる。

スクリーンショット画像をサーバに送る

UE4では標準でスクリーンショット機能が用意されている。

ただ、この機能はあくまでPCなどのローカルストレージに.pngファイルとして保存されるだけであり、このpngファイルを読み込んで、サーバに送信しようとすると割と大がかりなコードを書かなくてはならない(しかも、ローカルストレージのファイルを読むのでOS依存が激しくなる)。ので速攻却下。

別の方法を考える。ファイルにならなくても、pngデータがサーバに送れたらよいので、UE4のどこかで行われているスクリーンキャプチャから.pngファイルの保存までの処理を横取りして実現したらよいのではないかと。これだとOS依存もなくなるし。

UE4ソースを読んでいくと、スクリーンショットの処理の中にDelegateに関数が登録されていると、優先的に実行される仕組みが用意されていた。これを使う事で、手軽に生画像データと縦横の画像サイズが取得できた。

呼び出し側(プラグイン)のコードは次の通り。登録すると標準のスクリーンショット機能が無効になるので、必要に応じて付け外しするとよいかと。

// 横取りの登録
void UTwitterAPI::AttachScreenShot(){
    UGameViewportClient::OnScreenshotCaptured().AddUObject(this, &UTwitterAPI::OnScreenshotCapture);
}

void UTwitterAPI::OnScreenshotCapture(int32 ImageWidth, int32 ImageHeight, const TArray<FColor>& Bitmap) {
  
  // Bitmap変数を操作して画像編集
}

呼び出し元のソースコードは、Engine/Source/Runtime/Engine/Private/GameViewportClient.cppファイルの1251行目(UE 4.8.2の場合)にある。詳細はその前後のソースコードで確認してください。

if (bScreenshotSuccessful)
    {
      if (ScreenshotCapturedDelegate.IsBound())
      {
        ScreenshotCapturedDelegate.Broadcast(Size.X, Size.Y, Bitmap); <- 呼び出し元
      }
      else
      {
               …

今回はtwitterに画像つきtweetを実装するために使ったけど、WebAPIが用意されているBugTracker向けにWebAPIを通して直接に画像付きでバグ報告することも可能。

ちなみに

先のスクリーンショット周辺のコードを読むと、バグ報告用のスクリーンショットを生成するらしき関数があったりしたけど、その機能ってもう用意あるのかな?