언리얼 엔진 5/개발 일지

[UE5] 블루아카이브 TPS게임 개발일지 (49) - 스크린 데미지 효과 적용

ciel45 2024. 1. 21. 00:22

현재 만들어져있는 포스트프로세스는 인게임에서 사용하려면 Material Instance로써 들어가야한다.

 

만든 포스트 프로세스에 우클릭을 하여 Create Material Instnace를 눌러 인스턴스를 만들어, 이름을 PPI_ScreenDamage로 지었다.

 

그리고 그 인스턴스가 들어갈 곳은 PostProcessVolume의 Post Process Materials 배열 내부이다.

 

C++ 코드에서 머티리얼 인스턴스를 저기에 집어넣고, 플레이어 체력에 따라 Radius를 조정하는 것까지 수행할 것이다.

 

KannaCharacter.h에 다음과 같은 변수를 추가하였다.

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = Material)
UMaterialInstance* ScreenDamage;
	
UPROPERTY()
UMaterialInstanceDynamic* ScreenDamageDynamic;

ScreenDamage에는 PPI_ScreenDamage가 들어갈 것이고, ScreenDamageDynamic은 ScreenDamage를 토대로 만들 다이나믹 인스턴스를 담을 변수이다.

 

런타임 중 PPI_ScreenDamage의 파라미터인 Radius를 바꿀 것이기 때문에, 그냥 UMaterialInstance가 아닌 UMaterialInstanceDynamic을 사용해야한다.

 

ScreenDamageDynamic은 BeginPlay에서 생성할 것이다.

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

	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(InputMappingContext, 0);
		}
	}

	PunchHitbox->OnComponentBeginOverlap.AddDynamic(this, &AKannaCharacter::OnHitboxOverlap);
	KickHitbox->OnComponentBeginOverlap.AddDynamic(this, &AKannaCharacter::OnHitboxOverlap);

	IsCameraAtRight = true;

	AimingDirection = EAimingDirection::EAD_Neutral;

	//위젯 초기화
	InitKannaTpsOverlay();

	// 화면 데미지 효과 포스트 프로세스 생성, 초기화
	ScreenDamageDynamic = UMaterialInstanceDynamic::Create(ScreenDamage, GetWorld());
	APostProcessVolume* Volume = Cast<APostProcessVolume>(UGameplayStatics::GetActorOfClass(GetWorld(), APostProcessVolume::StaticClass()));
	Volume->Settings.WeightedBlendables.Array.Add(FWeightedBlendable(1.f, ScreenDamageDynamic));
}

마지막 3줄이 해당하는 코드이다.

  1. ScreenDamage를 토대로 ScreenDamageDynamic을 만들고
  2. 인게임의 PostProcessVolume 액터에 해당하는 APostProcessVolume을 찾아내고,
  3. PostProcessMaterials 내 배열에 넣을 수 있는 FWeightedBlendable 구조체를 만들고, 그 구조체를 Settings->WeightedBlendables 내부의 Array에 추가하였다.

 

FWeightedBlendable을 만들 때 1.f를 넣은 파라미터는 InWeight로, 포스트 프로세스의 영향력을 나타낸다. 따라서 최댓값인 1로 해주었다.

 

 

솔직히 배열에 넣는 것을 C++ 코드로 어떻게 처리해야하는지 몰라 상당히 헤맸다.

에디터의 PostProcessMaterials가 C++ 코드에서의 FWeightedBlendables와 같다는 것이 중요하다.

Post Process Materials가  FWeightedBlendables과 같고, 배열의 요소의 타입은  FWeightedBlendable이다.

 

 

플레이어의 체력에 따라 Radius를 설정해주는 것은 Tick 함수에서 구현하였다.

// Called every frame
void AKannaCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// 엄폐중이고, 키보드에서 손을 뗐을 시 캐릭터의 방향 결정
	if (IsInCover && (GetVelocity().Length() == 0))
	{
		if (bIsCrouched)
		{
			FVector Direction = GetCharacterMovement()->GetPlaneConstraintNormal() * -1.f; //벽면을 바라봄
			AddMovementInput(Direction);
		}
	}

	//Radius 설정
	float ScreenDamageRadius =
		FMath::GetMappedRangeValueClamped(TRange<float>(0.f, 100.f), TRange<float>(0.3f, 1.f), Attributes->GetCurrentHealth());
	ScreenDamageDynamic->SetScalarParameterValue(FName("Radius"), ScreenDamageRadius);
}

플레이어의 체력은 0~100까지이고, Radius의 적정값은 0.3~1이다. (0.3 쪽이 화면이 붉고, 1 쪽이 화면이 깨끗해진다.)

 

따라서 GetMappedRangeValueClamped를 통해 플레이어의 현재 체력을 받은 뒤, 그 값을 손쉽게 0~100 범위에서 0.3~1 범위로 매핑할 수 있었다.

 

그리고 SetScalarParameterValue 함수를 통해 Radius를 설정해주었다.

 

테스트 영상:

https://www.youtube.com/watch?v=bnxy2w-6QIc&lc=UgyFxc_DjrvFGXQ0ts94AaABAg&ab_channel=Ciel45