AYU MAX

プログラミングとか作ったもの

UE4 Windowsパッケージにおいてのアンフォーカス時の対応(Sound再生、GamePadの入力)

フォーカスがないときの挙動

私はお仕事でUE4をゲーム以外の用途で使っているのですが、Windowsで他のアプリケーションと一緒に使うことが多いです。

よくあるパターンがUE4で表示部分を作り、WPFでコントローラーのようなものを作り組み合わせて使うパターン。

このパターンではどうしてもフォーカスはWPFアプリケーション側にくるので、UE4のウィンドウにはフォーカスが無い状態になります。

そうすると以下のような困ったことが起こりました。

  • 音が聞こえない
  • GamePadの入力がとれない

これらについて中々対策が見つからなかったのですが、Engineソースを確認したところ2つとも対策が見つかりましたので覚書として書いておきます。

なお、この記事の対処をしたのは4.24の環境ですが、4.25でもその部分の実装は変わってなさそうなので同じ対応でよいのかと思っています(4.25では動作確認まではしてません)

アンフォーカス時の音の再生

WindowsPlatformApplicationMisc::PumpMessagesにて以下の記載を見つけました。

// if its our window, allow sound, otherwise apply multiplier
FApp::SetVolumeMultiplier( HasFocus ? 1.0f : FApp::GetUnfocusedVolumeMultiplier() );

SetVolumeMultiplierは音のボリュームをセットする関数のようですが、ここでフォーカス有りの場合は1.0を、そうでない場合はGetUnfocusedVolumeMultiplierの値を使っています。

そしてGetUnfocusedVolumeMultiplierが返す値を調べると0になっておりました。 なので聞こえないんだなあ。

ただ、この値の初期値はConfigからとられていることが分かります。

static bool GUnfocusedVolumeMultiplierInitialised = false;
float FApp::GetUnfocusedVolumeMultiplier()
{
    if (!GUnfocusedVolumeMultiplierInitialised)
    {
        GUnfocusedVolumeMultiplierInitialised = true;
        GConfig->GetFloat(TEXT("Audio"), TEXT("UnfocusedVolumeMultiplier"), UnfocusedVolumeMultiplier, GEngineIni);
    }
    return UnfocusedVolumeMultiplier;

ということで、以下の設定をConfig/DefaultEngine.iniに書いてUnfocusedVolumeMultiplierの値も1.0になるようにして動作を確認しました。

[Audio]
UnfocusedVolumeMultiplier=1.0

アンフォーカス時でも音が聞こえてきて成功です。

アンフォーカス時のGamePadの入力

UE4でGamePadの入力はWindowsではXInputというAPIを使っています。

なので、そこから検索するとXInputInterfaceというクラスが見つかりました。 中身を確認するとXInputのAPIを呼んでいるのでここがGamePadの入力を受け取っている可能性が高いです。

次にXInputInterfaceを使っている箇所を探し、FWindowsApplicationにて使われていることが分かりました。

するとFWindowsApplication::PollGameDeviceStateにてXInputの入力イベントを発火していそうです。 更にこの関数はFSlateApplication::PollGameDeviceState()から呼ばれています。

FSlateApplication::PollGameDeviceState()の中身を見るとifでガードされています。

if( ActiveModalWindows.Num() == 0 && !GIntraFrameDebuggingGameThread && (!bRequireFocusForGamepadInput || IsActive()))
{
    // Don't poll when a modal window open or intra frame debugging is happening
    PlatformApplication->PollGameDeviceState( GetDeltaTime() );
}

ここには3つの条件がありますが、最後のIsActive()はフォーカスを持つことのような気がします。 しかにここにbRequireFocusForGamepadInputというダイレクトで関わりが深そうな変数があります。

これの定義を調べるとコンソールコマンドで値が変えられそうでした。

static bool bRequireFocusForGamepadInput = false;
FAutoConsoleVariableRef CVarRequireFocusForGamepadInput(
    TEXT("Slate.RequireFocusForGamepadInput"),
    bRequireFocusForGamepadInput,
    TEXT("")
);

私の仕事の用途ではdevelopment buildしているのでCtrl+@で上記コマンドを入れてみたところアンフォーカスでもGamePadの入力がとれるようになりました。

ただし毎回打ち込むのもメンドクサイので(BluePrintから呼ぶ手もあるけどできればやりたくない)

ちょっと調べているとコンソールコマンドもiniファイルに定義できるみたいなので、その対応をすることにしました。

qiita.com

まとめ

UE4のアプリケーションはフォーカスを失うと色々できない事が起こるのですが、なんとか希望の動作をするように調整できました。

エンジンソースが見られるっていうのはこういった調査もやりやすいのでホント嬉しいです!