언리얼 5

231025 언리얼을 활용한 2D Game 제작 (카메라 락 트리거)

슬뷔 2023. 10. 25. 11:10

TriggerBox 를 상속 받은 C++ 클래스 CamLockTrigger 생성

#include "../Header/global.h"
#include "LockCameraActor.h"

#include "CoreMinimal.h"
#include "Engine/TriggerBox.h"
#include "CamLockTrigger.generated.h"

USTRUCT(Atomic, BlueprintType)
struct FTriggerArea
{
	GENERATED_BODY();
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	int32 Left;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	int32 Top;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	int32 Right;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	int32 Bottom;

public:
	int32 GetWidth() { return abs(Right - Left); }
	int32 GetHeight() { return abs(Bottom - Top); }
};

UCLASS()
class UNREAL_3TH_API ACamLockTrigger : public ATriggerBox
{
	GENERATED_BODY()
	
private:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	ALockCameraActor*	m_SwitchCam;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	FTriggerArea		m_Area;

public:
	virtual void BeginPlay() override;
	virtual void Tick(float _DT) override;

public:
	// OverlapDelegate
	UFUNCTION()
	void BeginTrigger(AActor* _TriggerActor, AActor* _OtherActor);

	UFUNCTION()
	void EndTrigger(AActor* _TriggerActor, AActor* _OtherActor);

public:
	ACamLockTrigger();
	~ACamLockTrigger();
};
#include "CamLockTrigger.h"

ACamLockTrigger::ACamLockTrigger()
{
	PrimaryActorTick.Target = this;
	PrimaryActorTick.bCanEverTick = true;
}

ACamLockTrigger::~ACamLockTrigger()
{
}

void ACamLockTrigger::BeginPlay()
{
	Super::BeginPlay();

	LOG(LogTemp, Warning, TEXT("ACamLockTrigger BeginPlay"));

	OnActorBeginOverlap.AddDynamic(this, &ACamLockTrigger::BeginTrigger);
	OnActorEndOverlap.AddDynamic(this, &ACamLockTrigger::EndTrigger);
}

void ACamLockTrigger::Tick(float _DT)
{
	Super::Tick(_DT);

	if (m_bOverlap)
	{
		if (IsValid(m_SwitchCam))
		{
			m_SwitchCam->PositionUpdate();
		}
	}
}

void ACamLockTrigger::BeginTrigger(AActor* _TriggerActor, AActor* _OtherActor)
{
	LOG(LogTemp, Warning, TEXT("BeginOverlap Trigger"));

		// 카메라의 시점 변경하기
	if (IsValid(m_SwitchCam))
	{
		APlayerController* pController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
		pController->SetViewTargetWithBlend(m_SwitchCam, 0.f);

		// trigger 로 들어온 actor 를 쫓아 다니게 설정
		m_SwitchCam->SetTarget(_OtherActor);
		m_SwitchCam->SetTrigger(this);
		m_bOverlap = true;

		m_SwitchCam->PositionUpdate();
	}
}

void ACamLockTrigger::EndTrigger(AActor* _TriggerActor, AActor* _OtherActor)
{
	LOG(LogTemp, Warning, TEXT("EndOverlap Trigger"));

	if (IsValid(m_SwitchCam))
{
	APlayerController* pController = UGameplayStatics::GetPlayerController(GetWorld(), 0);				
	pController->SetViewTargetWithBlend(pController->GetPawn(), 0.f);

	// 시점 카메라에 추적 대상을 해제
	m_SwitchCam->SetTarget(nullptr);
	m_SwitchCam->SetTrigger(nullptr);
	m_bOverlap = false;
}
}

프로젝트 세팅 -> 콜리전 -> 새 오브젝트 채널 -> TriggerGroup (ignore)

CameraActor 상속 받은 C++ 클래스 LockCameraActor 생성

 

PostEditChangeProperty 제작

UCLASS()
class UNREAL_3TH_API ACamLockTrigger : public ATriggerBox
{
	GENERATED_BODY()

private:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	FTriggerArea		m_Area;

	FTriggerArea		m_Area_Prev;

public:
#if WITH_EDITOR
	virtual void PostEditChangeProperty(FPropertyChangedEvent& _event) override;
#endif
}

------------------------------------------------------------------------------------------------

#if WITH_EDITOR
void ACamLockTrigger::PostEditChangeProperty(FPropertyChangedEvent& _event)
{
	Super::PostEditChangeProperty(_event);

	LOG(LogTemp, Warning, TEXT("CamLockTrigger"));

	UBoxComponent* pBox = Cast<UBoxComponent>(GetCollisionComponent());
	if (!IsValid(pBox))
		return;

	// 변화량
	float XChange = ((m_Area.Left - m_Area_Prev.Left) + (m_Area.Right - m_Area_Prev.Right));
	float ZChange = ((m_Area.Top - m_Area_Prev.Top) + (m_Area.Bottom - m_Area_Prev.Bottom));

	FVector vTriggerLocation = GetActorLocation();
	FVector vNewLocation = FVector(vTriggerLocation.X + XChange / 2.f
		, vTriggerLocation.Y
		, vTriggerLocation.Z + ZChange / 2.f);

	SetActorLocation(vNewLocation);
	pBox->SetBoxExtent(FVector(m_Area.GetWidth() / 2.f
		, pBox->GetUnscaledBoxExtent().Y
		, m_Area.GetHeight() / 2.f));

	m_Area_Prev = m_Area;
}
#endif

 

 

카메라 액터가 트리거의 우측을 벗어나지 못하게 카메라 락을 건다.

#include "../Header/global.h"

#include "CoreMinimal.h"
#include "Camera/CameraActor.h"
#include "LockCameraActor.generated.h"

class ACamLockTrigger;

UCLASS()
class UNREAL_3TH_API ALockCameraActor : public ACameraActor
{
	GENERATED_BODY()

private:
	UPROPERTY()
	AActor* m_Target;

	UPROPERTY()
	ACamLockTrigger* m_Trigger;

public:
	void SetTarget(AActor* _Target) { m_Target = _Target; }
	void SetTrigger(ACamLockTrigger* _Trigger) { m_Trigger = _Trigger; }

public:
	void PositionUpdate();
};
void ALockCameraActor::PositionUpdate()
{
	// 트리거로 진입한 타겟의 위치값을 알아낸다.
	FVector vLocation = m_Target->GetActorLocation();
	vLocation.Y += 200.f;

	// 카메라 본인의 직교 투영 범위를 체크한다
	float OrthoWidth = GetCameraComponent()->OrthoWidth;
	float OrthHeight = OrthoWidth / GetCameraComponent()->AspectRatio;

	// 트리거의 중심에서부터의 박스 크기 정보를 알아낸다.
	UBoxComponent* pBox = Cast<UBoxComponent>(m_Trigger->GetCollisionComponent());
	FVector BoxExtent = pBox->GetUnscaledBoxExtent();

	// 트리거의 중심 위치를 알아낸다.
	FVector vTriggerPos = m_Trigger->GetActorLocation();

	// 트리거의 중심에서 박스 크기를 더한 우측 지점보다, 
	// 타겟 위치에서 카메라가 투영시 투영의 범위 우측이 더 오른쪽으로 초과한 부분이 있다면
	if (vTriggerPos.X + BoxExtent.X < vLocation.X + (OrthoWidth / 2.f))
	{
		// 초과량을 계산 후, 원래 예정된 이동 위치(타겟의 위치)에서 빼주고 그 곳을 최종 이동 목적지로 한다.
		float fOver = vLocation.X + (OrthoWidth / 2.f) - (vLocation.X + BoxExtent.X);
		vLocation.X -= fOver;
	}

	SetActorLocation(vLocation);
}