Blog

[UE4] SceneCaptureで取得した映像を全画面に写し、特定のアクターを画面に表示させない方法

執筆者 /M.M

はじめに

特定のアクターをゲーム画面では非表示にし、別カメラから撮影されているときは表示するといった演出をしたいときがあるかと思います。

例えば、ホラーゲームにてプレイヤー視点は何も見えないのに鏡を使うと見えないものが見えるようになるといった演出です。

それを表現する方法の一例を今回紹介したいと思います。

用意するもの

・Unreal Engine 4.23.1

・Visual Studio 2017

実装するもの

まずはどのようにして実装していくのかを先にざっくりと説明します。

通常のカメラと同じ位置にSceneCapture2Dを配置します。そのSceneCapture2Dで常時撮影を行い、非表示にしたいアクターをHiddenActorsに設定します。その撮影されたテクスチャをマテリアルに反映させ、画面いっぱいに設定されたウィジェットのImageにそのマテリアルを適応させるといった流れです。そうすることであたかもカメラから写しているかのようにでき、特定のアクターを非表示に見せることができます。

では以下より細かい実装手順を説明します。

実装手順

まずはカメラやSceneCapture2Dを配置させる処理を実装します。

今回は最小限に実装するためC++にてコンポーネントの設定をしていますが、ブループリント側で実装しても問題ないと思います。(画面全体に投影する関係上、解像度の取得だけはC++で行う必要がありそうです。)

MyWidgetBluePrint.cppとMyWidgetBluePrint.hを作成し以下のコードを書きます。

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Camera/CameraComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "CameraAndSceneComponentActor.generated.h"

UCLASS()
class MYPROJECT2_API ACameraAndSceneComponentActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACameraAndSceneComponentActor();

	UPROPERTY(VisibleAnywhere)
	USceneComponent* SceneRootComponent;
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	USceneCaptureComponent2D* scene_capture_component;
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UCameraComponent* camera_component;
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UTextureRenderTarget2D* texture_render;
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
};

・MyWidgetBluePrint.cpp

#include "CameraAndSceneComponentActor.h"

#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetRenderingLibrary.h"
#include "Engine/GameEngine.h"

// Sets default values
ACameraAndSceneComponentActor::ACameraAndSceneComponentActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ACameraAndSceneComponentActor::BeginPlay()
{
	Super::BeginPlay();

	// 根元コンポーネントを作成
	SceneRootComponent = NewObject< USceneComponent >(this);
	SceneRootComponent->RegisterComponent();
	SceneRootComponent->AttachToComponent(RootComponent, { EAttachmentRule::KeepRelative, false });
   // 位置を調整
	SceneRootComponent->SetWorldLocationAndRotation(FVector(165,0,120), FQuat(0, 0, 180, 0));

	// カメラコンポーネントを生成し、自身にアタッチ
	camera_component = NewObject<UCameraComponent>( this );
	camera_component->RegisterComponent();
	camera_component->AttachToComponent(SceneRootComponent, { EAttachmentRule::KeepRelative, false });

	// SceneCapture2Dを生成し、自身にアタッチ
	scene_capture_component = NewObject<USceneCaptureComponent2D>(this);
	scene_capture_component->RegisterComponent();
	scene_capture_component->AttachToComponent(SceneRootComponent, { EAttachmentRule::KeepRelative, false });

	// 解像度を取得
	FVector2D Result = FVector2D(1, 1);
	if (GEngine && GEngine->GameViewport)
	{
		GEngine->GameViewport->GetViewportSize( /*out*/Result);
	}

	// ゲーム画面の大きさに合わせてテクスチャを生成
	texture_render = UKismetRenderingLibrary::CreateRenderTarget2D(this, Result.X, Result.Y);
	texture_render->TargetGamma = 2.2f;
	scene_capture_component->TextureTarget = texture_render;

	// ゲーム中に表示するカメラを生成したコンポーネントに切り替え
	APlayerController*		player_controller = UGameplayStatics::GetPlayerController(this, 0);
	player_controller->SetViewTarget(this);
}

カメラとSceneCaptureが用意できましたので、次はSceneCaptureで撮影したものを投影する場を整えます。新規でマテリアルを作成します。「詳細」→「Material」→「Maaterial Domain」を「User Interface」に変更し、ブループリントにあるFinalColorから線を伸ばして「TextureSampleParameter2D」のノードをつなげます。

次にそのマテリアルを画面に表示するウィジェットを作成します。

パレットよりButtonとImageをCanvasに配置します。Imageは下図のようにスロットとマテリアルを設定し非表示にしておきます。

ブループリントは以下のように設定します。左上に配置したボタンでSceneCaptureとカメラを切り替えるようにしています。

最後にレベルブループリントに上記ウィジェットの生成と非表示したいアクターを設定します。

これで準備完了です。

実行

左上のボタンを押下するとアクターの非表示と表示を切り替えられていることが確認できます。

また、別のSceneCaptureを用意して別視点も追加することで、より別カメラには映るけどゲーム画面には映らないといった状態を作ることができます。

下図は上空から投影したものを追加した例です。左上にあるキャプチャ画面はアクターが表示され続けますが、ゲーム画面では表示状態が切り替わる事がわかります。

まとめ

SceneCaptureでしかできない特定のアクターを非表示にする機能を応用した例を紹介しました。この機能を使って思ったのですが、どうやらSceneCaptureで投影した映像はポストプロセスが外れているのか通常のカメラで撮影した映像に比べて若干暗いんですよね。一応SceneCaptureの設定でレンダーフォーマットやらレベル詳細などいろいろ調整できそうではあります。

この記事を見てゲーム制作の何かしらの参考になれば幸いです。

関連リンク