【UE4】 Simulate In Editor (SIE) 中にゲームのUIを非表示にする
更新情報
UIを元に戻す処理を Shutdown() 関数で呼び出していましたが、タイミングによっては GameViewPort のポインタが取得できずに動作しない事があったので、PIEの終了デリゲート内で呼び出す様に変更しました。
はじめに
Unreal Engine のエディターにはプレイ中のアクターの状態を確認したり、変更したりできるシミュレーション状態があります。(Unityで言うところのシーンビューの様なもの)
これは大変便利な機能なのですが、困ったことにUMGで表示したゲームのUIが画面に残ってしまう為、場合によっては作業に支障をきたします。
たとえが画面のど真ん中にUIが表示されているとき

イジェクトボタンまたは、F8キーを押してシミュレーション状態に移行させてみると

カメラをどんな角度に変更しても、UIはビューポートに描き込まれるので常に画面内に表示されてしまいます。

この例だと微妙ですが、もっと画面を覆い隠すUIだったりすると、確認の邪魔でストレスなので、シミュレーション中はゲームのUIを非表示にできないか検証してみました。
解決策
今回の解決方法は、私の知る限りでは C++での拡張が必要です。
この点はご了承ください。
シミュレーション状態(SIE)に切り替わったことを検知する
UIの表示切替を作る前に、そもそもシミュレーション状態になったかどうかを検知できなければ仕方ありません。
まずはそれを検知するイベント(デリゲート)を探してみたところ
FEditorDelegates クラスに OnSwitchBeginPIEAndSIE というそれらしいきものが見つかりました。
エンジン内での呼び出し個所を検索してみると、void UEditorEngine::ToggleBetweenPIEandSIE( bool bNewSession ) 関数で呼ばれているのでどうやらビンゴです。
デリゲートについての解説は割愛しますが、こんな感じでデリゲートの追加、削除ができます。
エディター専用コードになるので、WITH_EDITOR で囲っています。
FDelegateHandle handleSwitchPIEAndSIE_;
//デリゲートの登録
#if WITH_EDITOR
/** delegate for a PIE event (begin, end, pause/resume, etc) (Params: bool bIsSimulating) */
handleSwitchPIEAndSIE_ = FEditorDelegates::OnSwitchBeginPIEAndSIE.AddUObject( this, &ThisClass::OnSwitchPIEAndSIE );
#endif
//デリゲートの削除
#if WITH_EDITOR
FEditorDelegates::OnSwitchBeginPIEAndSIE.Remove( handleSwitchPIEAndSIE_ );
#endif
UGameInstance を継承して自作クラスを作成する
問題はこのコードをどこに書くべきかですが、レベルに依存させたくないのでアクターから呼び出すのは避けたいです。
そこでレベルを跨いでも存在し続ける UGameInstance を継承した自作クラスを作成し、そこに書いてみます。
メニューの「ファイル / 新規C++ クラス」からC++クラスを作成


生成されたコードを開いて、UGameInstance の初期化と終了をオーバーライドします。
そこで先ほどのデリゲート処理を記述します。
デリゲートのbool型引数がポイントで trueの時=SIEに入った。falseの時=PIEに戻った。という意味の様です。
void UMyGameInstance::Init()
{
#if WITH_EDITOR
/** delegate for a PIE event (begin, end, pause/resume, etc) (Params: bool bIsSimulating) */
handleSwitchPIEAndSIE_ = FEditorDelegates::OnSwitchBeginPIEAndSIE.AddUObject(this, &ThisClass::OnSwitchPIEAndSIE);
#endif
}
void UMyGameInstance::Shutdown()
{
#if WITH_EDITOR
FEditorDelegates::OnSwitchBeginPIEAndSIE.Remove(handleSwitchPIEAndSIE_);
#endif
}
void UMyGameInstance::OnSwitchPIEAndSIE(const bool bIsSimulating)
{
//TODO:ここにゲームのUIを切り替えるコードを書く
}
ビルドする前準備
今回は エディター用の FEditorDelegatesクラスを使うので UnrealEd モジュールを有効化する必要があります。
これを有効にしないと、モジュールのリンクができずにビルドに失敗します。
また、今後のステップでUIのコアシステムである Slate モジュールも必要なので一緒に有効化してしまいましょう。
参考:インゲームでスレートを使用する
このページの「プロジェクト設定」の項目で解説されています。
ビルドの設定ファイルを開く
以下の様に [ProjectDir]/[ProjectName]/Source/[ProjectName]/[ProjectName].build.cs ファイルを開きます。

以下の様に編集して UnrealEd と Slate を有効化しましょう。
Slateはコメントアウトされたコードが既に記述されているので、コメントインするだけでOKです。
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
public class PlatformShooter : ModuleRules
{
public PlatformShooter(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PrivateDependencyModuleNames.AddRange(new string[] { });
//
// Slateを有効化
//
// Uncomment if you are using Slate UI
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
//
// UnrealEdを有効化
//
if (Target.Type == TargetRules.TargetType.Editor)
{
PublicDependencyModuleNames.Add("UnrealEd");
}
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}
これで一旦ビルドしてみましょう。
GameInstanceを自作クラスに差し替える
自作クラスを作った次のステップは、GameInstanceクラスの差し替えを行います。
プロジェクト設定を開き「プロジェクト / マップ&モード」で自作のGameInstanceクラスに変更します。

ゲームのUIの表示切替を実装する
これでSIEの切替が検知できる様になったので、次はゲームのUIを切り替えてみます。
UMGでAddViewportしたウィジェットは、Slate の SGameLayerManager 以下で管理される様なので、
このオブジェクトを取得して表示を切り替える関数を作成します。
void UMyGameInstance::SetGameLayerManagerVisibility(const UObject* WorldContextObject, bool bIsVisible)
{
const UWorld* world = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (world != nullptr && world->IsGameWorld())
{
const UGameViewportClient* gameViewportClient = world->GetGameViewport();
if (gameViewportClient != nullptr)
{
auto gameLayerManager = static_cast<SGameLayerManager*>(gameViewportClient->GetGameLayerManager().Get());
if (gameLayerManager)
{
const EVisibility newVisibility = bIsVisible ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed;
gameLayerManager->SetVisibility(newVisibility);
}
}
}
}
完成したコード
これで表示切替もできる様になったので、SIEの切替デリゲート内でこれを呼び出してあげれば完成です。
ただ、注意点としては PIE or SIE が終わった時にUIを表示状態に戻す必要がありますので、今回は Shutdown 関数内で戻す様にしています。
今回は EndPIE デリゲートでUIを戻す様にしています。
MyGameInstance.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"
/**
*
*/
UCLASS()
class PLATFORMSHOOTER_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
// 初期化
virtual void Init() override;
// 終了
virtual void Shutdown() override;
private:
// ゲームのUIを表示切替
void SetGameLayerManagerVisibility(const UObject* WorldContextObject, bool bIsVisible);
#if WITH_EDITOR
// PIE to SIE の切替イベント
UFUNCTION()
void OnSwitchPIEAndSIE(const bool bIsSimulating);
// PIE の終了
UFUNCTION()
void OnEndPIE(const bool bIsSimulating);
// デリゲートハンドル
FDelegateHandle handleSwitchPIEAndSIE_;
FDelegateHandle handleEndPIE_;
#endif
};
MyGameInstance.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Editor.h"
#include "Slate/SGameLayerManager.h"
/**
* @brief 初期化
*/
void UMyGameInstance::Init()
{
#if WITH_EDITOR
//
// デリゲートを登録する
//
handleSwitchPIEAndSIE_ = FEditorDelegates::OnSwitchBeginPIEAndSIE.AddUObject(this, &ThisClass::OnSwitchPIEAndSIE);
handleEndPIE_ = FEditorDelegates::EndPIE.AddUObject(this, &ThisClass::OnEndPIE);
#endif
}
/**
* @brief 終了
*/
void UMyGameInstance::Shutdown()
{
#if WITH_EDITOR
//
// デリゲートを抹消する
//
FEditorDelegates::OnSwitchBeginPIEAndSIE.Remove(handleSwitchPIEAndSIE_);
FEditorDelegates::EndPIE.Remove(handleEndPIE_);
#endif
}
/**
* @brief ゲームのUIを表示切替
* @param WorldContextObject
* @param bIsVisible
*/
void UMyGameInstance::SetGameLayerManagerVisibility(const UObject* WorldContextObject, bool bIsVisible)
{
const UWorld* world = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (world != nullptr && world->IsGameWorld())
{
const UGameViewportClient* gameViewportClient = world->GetGameViewport();
if (gameViewportClient != nullptr)
{
auto gameLayerManager = static_cast<SGameLayerManager*>(gameViewportClient->GetGameLayerManager().Get());
if (gameLayerManager)
{
const EVisibility newVisibility = bIsVisible ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed;
gameLayerManager->SetVisibility(newVisibility);
}
}
}
}
#if WITH_EDITOR
/**
* @brief PIE to SIE の切替イベント
* @param bIsSimulating
*/
void UMyGameInstance::OnSwitchPIEAndSIE(const bool bIsSimulating)
{
//
// シミュレーション状態じゃなければUIを表示する
//
const bool bIsVisible = !bIsSimulating;
SetGameLayerManagerVisibility(this, bIsVisible);
}
/**
* @brief PIE の終了イベント
* @param bIsSimulating
*/
void UMyGameInstance::OnEndPIE(const bool bIsSimulating)
{
//
// 次にプレイした時の為に表示状態に戻す
//
SetGameLayerManagerVisibility(this, true);
}
#endif
ディスカッション
コメント一覧
まだ、コメントがありません