Unreal Engine/C++

[Unreal Engine 4 C++] TimeStop (시공단열/초산 회피)

한돌이 2023. 8. 2. 18:04

https://lykanstudio.tistory.com/84

 

[Unreal Engine 4 Blueprint] TimeStop (시공단열/초산 회피)

몇몇 액션 게임에선 적을 공격을 알맞은 타이밍에 회피하면 시간이 멈춘 것처럼 캐릭터가 멈추거나 느리게 행동합니다. 이런 기능을 언리얼 엔진으로 만드는 법은 다음과 같습니다. ActorComponent

lykanstudio.tistory.com

 위의 포스팅으로 구현 블루프린트를 C++로 구현한 글입니다.

 

// TimeStopSystemComponent.h

UCLASS()
class PROJECTREPLICA_API UTimeStopSystemComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UTimeStopSystemComponent();

public:
	/** TimeStop을 실행하는 함수입니다. */
	UFUNCTION(BlueprintCallable, Category = "TimeStop")
	void ActivateTimeStop();

	/** TimeStop을 비활성화하는 함수입니다. */
	UFUNCTION(BlueprintCallable, Category = "TimeStop")
	void DeactivateTimeStop();

protected:
	/**
	 * 입력받은 값으로 PostProcessSettings의 MotionBlurAmount 값을 설정하는 함수입니다.
	 *
	 * @param PostProcessSettings MotionBlurAmount 값을 설정할 PostProcessSettings입니다.
	 * @param NewMotionBlurAmount 설정할 MotionBlurAmount 값입니다. 
	 */
	UFUNCTION(BlueprintCallable, Category = "TimeStop")
	void ChangeMotionBlurAmount(UPARAM(ref) FPostProcessSettings& PostProcessSettings, float NewMotionBlurAmount);

protected:
	/** TimeStop의 실행을 나타내는 변수입니다. */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "TimeStop")
	bool bActivate;

	/** TimeStop 실행시 적용할 시간 흐름 속도입니다. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TimeStop")
	float TimeStopDilation;

	/** Owner의 시간 흐름 속도입니다. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TimeStop")
	float OwnerTimeDilation;

	/** TimeStop 실행시 화면의 블러 현상을 조절하기 위한 MotionBlur의 강도입니다. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "TimeStop")
	float BaseMotionBlurIntensity;
};

 

// TimeStopSystemComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "Components/TimeStopSystemComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"

UTimeStopSystemComponent::UTimeStopSystemComponent()
{
	TimeStopDilation = 0.0001f;
	OwnerTimeDilation = 1.0f;
	BaseMotionBlurIntensity = 0.5f;
}

void UTimeStopSystemComponent::ActivateTimeStop()
{
	if(IsValid(GetOwner()) == true)
	{
		bActivate = true;

		// 카메라의 모션블러를 설정합니다.
		ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
		if(PlayerCharacter != nullptr)
		{
			UCameraComponent* CameraComponent = Cast<UCameraComponent>(PlayerCharacter->GetComponentByClass(UCameraComponent::StaticClass()));
			ChangeMotionBlurAmount(CameraComponent->PostProcessSettings, TimeStopDilation * BaseMotionBlurIntensity);
		}
        
		UGameplayStatics::SetGlobalTimeDilation(GetWorld(), TimeStopDilation);
		
		float NewOwnerTimeDilation = OwnerTimeDilation / TimeStopDilation;
		FTimerHandle TimerHandle;
		FTimerDelegate TimerDelegate = FTimerDelegate::CreateUObject(this, &UPRTimeStopSystemComponent::SetOwnerCustomTimeDilation, NewOwnerTimeDilation);
		// Delay함수와 달리 타이머는 글로벌 시간 흐름 속도(Global Time Dilation)의 영향을 받습니다.
		// 이미 앞선 코드로 인해 클로벌 시간 흐름 속도가 줄어들었으므로 Delay시간을 배로 설정합니다.
		GetWorld()->GetTimerManager().SetTimer(TimerHandle, TimerDelegate, TimeStopDilation * TimeStopDilation, false);
		
	}
}

void UTimeStopSystemComponent::DeactivateTimeStop()
{
	if(IsValid(GetOwner()) == true)
	{
		bActivate = false;

		// 모션 블러의 양을 초기화합니다.
		ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
		if(PlayerCharacter != nullptr)
		{
			UCameraComponent* CameraComponent = Cast<UCameraComponent>(PlayerCharacter->GetComponentByClass(UCameraComponent::StaticClass()));
			ChangeMotionBlurAmount(CameraComponent->PostProcessSettings, 0.5f);
		}
			
		// 글로벌 시간 흐름 속도를 초기화합니다.
		UGameplayStatics::SetGlobalTimeDilation(GetWorld(), 1.0f);
		// UI사운드를 제외한 모든 사운드의 시간 흐름 속도 초기화합니다.
		UGameplayStatics::SetGlobalPitchModulation(GetWorld(), 1.0f, 0.0f);
		// Owner의 시간 흐름 속도를 초기화합니다.
		GetPROwner()->CustomTimeDilation = 1.0f;
	}
}

void UTimeStopSystemComponent::SetOwnerCustomTimeDilation(float NewCustomTimeDilation)
{
	if(IsValid(GetOwner()) == true)
	{
		GetOwner()->CustomTimeDilation = NewCustomTimeDilation;
	}
}

void UTimeStopSystemComponent::ChangeMotionBlurAmount(FPostProcessSettings& PostProcessSettings, float NewMotionBlurAmount)
{
	// true로 설정해야 변경된 MotionBlurAmount 값으로 설정됩니다.
	PostProcessSettings.bOverride_MotionBlurAmount = true;
	PostProcessSettings.MotionBlurAmount = NewMotionBlurAmount;
}