[UE5] Cpp Multiplay Game <Cop And Robber> #4

_ dfdeer·2025년 9월 22일

Cop And Robber

목록 보기
4/7

자기장 축소 효과 및 메시 설정

생각해보니 만들어놓은 자기장에 줄어드는 효과를 적용하지 않았었다.
그리고 시각적 효과도 따로 만들어두지 않았다.

그래서 이번엔 그걸 적용하려한다.

//CRBlueZone.h

...

class COPANDROBBER_API ACRBlueZone : public AActor
{

	...
    
public:

	...
	
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	UStaticMeshComponent* ZoneMesh;
    
    ...
    
	UFUNCTION(BlueprintCallable, Category="Zone")
	void SetZoneRadius(float NewRadius);

	UFUNCTION()
	void OnRep_ZoneRadius();
	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
	
	
protected:
	UPROPERTY(EditAnywhere, Category="Zone")
	float ShrinkDelay = 10.f;

	UPROPERTY(EditAnywhere, Category="Zone")
	float ShrinkDuration = 10.f;

	UPROPERTY(EditAnywhere, Category="Zone")
	float TargetRadius = 2000.f;

	FTimerHandle ShrinkTimer;
	bool bIsShrinking = false;

	float StartRadius;
	float ElapsedTime = 0.f;
	
	void StartShrink();
    
	virtual void Tick( float DeltaSeconds ) override;
	
private:
	void SyncMeshToCapsuleRadius();
	void ApplyZoneMaterialOnce();

	UPROPERTY(EditDefaultsOnly, Category="Zone|Visual")
	UMaterialInterface* ZoneBaseMaterial = nullptr;

	UPROPERTY(Transient)
	UMaterialInstanceDynamic* ZoneMID = nullptr;
};

블루존 액터 헤더 파일이다.

자기장 축소와 관련된 변수, 함수들과 자기장의 메시를 설정하고 이를 콜리전과 연동하는 함수가 있다.

//CRBlueZone.cpp

static constexpr float kBaseCylinderRadius = 50.f;
static constexpr float kBaseCylinderHeight = 100.f;

ACRBlueZone::ACRBlueZone()
{
	...
    ZoneMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ZoneMesh"));
	ZoneMesh->SetupAttachment(SafeZone);
	ZoneMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    
    ...
    
	PrimaryActorTick.bCanEverTick = true;
}

소스 파일로 넘어와서,

이따가 실린더를 스태틱 메시로 사용할 것이기 때문에 기존 실린더의 크기를 정의해놓는다.

그리고 메시를 설정해주고 콜리젼을 없애주고,
틱을 쓸 것이기 때문에 활성화해준다.

void ACRBlueZone::SetZoneRadius(float NewRadius)
{
	ZoneRadius = NewRadius;
	
	SafeZone->SetCapsuleRadius(ZoneRadius);
	SyncMeshToCapsuleRadius();
	if (ZoneMID)
	{
		ZoneMID->SetScalarParameterValue(TEXT("ZoneRadius"), ZoneRadius);
	}
}

SetZoneRadius() 함수이다.

자기장의 크기를 새로운 크기로 변환시켜주는 함수로, 메시의 크기도 같이 연동해서 바꾸어준다.

void ACRBlueZone::OnRep_ZoneRadius()
{
	SafeZone->InitCapsuleSize(ZoneRadius, ZoneHalfHeight);

	const float XY = ZoneRadius / kBaseCylinderRadius;
	const float Z  = (ZoneHalfHeight * 2.f) / kBaseCylinderHeight;
	ZoneMesh->SetRelativeScale3D(FVector(XY, XY, Z));

	ApplyZoneMaterialOnce();
	if (ZoneMID)
	{
		ZoneMID->SetScalarParameterValue(TEXT("ZoneRadius"), ZoneRadius);
	}
}

void ACRBlueZone::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ACRBlueZone, ZoneRadius);
}

자기장의 크기를 복제해서 넣어주는 함수이다.

void ACRBlueZone::BeginPlay()
{
	...
    
    ApplyZoneMaterialOnce();
	
	SafeZone->InitCapsuleSize(ZoneRadius, ZoneHalfHeight);
	SyncMeshToCapsuleRadius();
	
    ...
    
	StartRadius = SafeZone->GetUnscaledCapsuleRadius();

	GetWorld()->GetTimerManager().SetTimer(
		ShrinkTimer,
		this,
		&ACRBlueZone::StartShrink,
		ShrinkDelay,
		false
	);
}

그리고 BeginPlay() 함수에서 머티리얼과 크기, 싱크를 모두 한 번씩 설정을 해준다.
그리고 자기장의 초기 크기를 저장해두고, ShrinkDelay 시간 이후에 StartShrink() 함수를 호출한다.

void ACRBlueZone::StartShrink()
{
	bIsShrinking = true;
	ElapsedTime = 0.f;
}

StartShrink() 함수에서는 bIsShrinking 을 true 로 바꾸고 ElapsedTime 을 0.f 로 다시 초기화해준다.

void ACRBlueZone::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if (bIsShrinking)
	{
		ElapsedTime += DeltaSeconds;
		float Alpha = FMath::Clamp(ElapsedTime / ShrinkDuration, 0.f, 1.f);

		float NewRadius = FMath::Lerp(StartRadius, TargetRadius, Alpha);

		SetZoneRadius(NewRadius);

		if (Alpha >= 1.f)
		{
			bIsShrinking = false;
		}
	}
}

Tick() 함수이다.

자기장이 줄어들고있다면 ElapsedTime 에 DeltaSeconds 를 담고,
Alpha 라는 새로운 플롯 변수에 최소 0부터 1까지의 값을 ElapsedTime / ShrinkDuration 으로 가져온다.

그리고 새로운 자기장 크기 NewRadius 에 Lerp 로 보간을 통해 크기를 바꿔준다.
또한 매 틱마다 그 새로운 크기를 SetZoneRadius() 함수를 통해 설정해주면 된다.

만약 자기장이 줄어드는게 끝나면 bIsShrinking 을 false 로 변경해준다.

void ACRBlueZone::SyncMeshToCapsuleRadius()
{
	if (!SafeZone || !ZoneMesh) return;
	
	const float CapsuleRadius = SafeZone->GetScaledCapsuleRadius();
	
	const float XYScale = CapsuleRadius / kBaseCylinderRadius;
	
	const FVector Cur = ZoneMesh->GetRelativeScale3D();
	ZoneMesh->SetRelativeScale3D(FVector(XYScale, XYScale, ZoneHalfHeight));
}

SyncMeshToCapsuleRadius() 함수이다.

말 그대로 메시를 자기장 콜리젼 크기에 싱크를 맞춰주는 함수이다.

void ACRBlueZone::ApplyZoneMaterialOnce()
{
	if (!ZoneMesh || ZoneMID) return;

	UMaterialInterface* Base = ZoneBaseMaterial ? ZoneBaseMaterial : ZoneMesh->GetMaterial(0);
	if (!Base || Base->GetBlendMode() != BLEND_Translucent) return;

	ZoneMID = UMaterialInstanceDynamic::Create(Base, this);
	ZoneMesh->SetMaterial(0, ZoneMID);
	ZoneMID->SetScalarParameterValue(TEXT("ZoneRadius"), ZoneRadius);
}

ApplyZoneMaterialOnce() 함수는 메시에 머티리얼을 한 번 적용시켜주는 함수이다.

탈락 처리

또 생각해보니 카운트 끝나면 탈락시키는 걸 주석 처리만 해놓고 구현을 안헀다.

캐릭터 GAS 쪽에 death 관련 태그가 있으니 그걸 부여하기만 하면 될 것 같다.

//CRZoneCountdownComponent.h

...

class COPANDROBBER_API UCRZoneCountdownComponent : public UActorComponent
{
	...
    
protected:
	
    ...
    
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<class UGameplayEffect> DeathEffect;
	
	...
    
};

먼저 헤더 파일에서 GE 를 선언해준다.

//CRZoneCountdownComponent.cpp

...

#include "Character/CRCharacter.h"
#include "GameFramework/Pawn.h"
#include "GAS/CRAbilitySystemComponent.h"

...

void UCRZoneCountdownComponent::TickOneSecond()
{
	...
    
	if (RemainingSeconds <= 0)
	{
		if (ACRCharacter* CRCharacter = Cast<ACRCharacter>(GetOwner()))
		{
			if (CRCharacter->GetCRAbilitySystemComponent() && DeathEffect)
			{
				CRCharacter->GetCRAbilitySystemComponent()->ApplyGameplayEffect(DeathEffect);
				UE_LOG(LogTemp, Warning, TEXT("Dead"));
			}
		}
		
        ...
        
	}
}

소스 파일에서는 TickOneSecond() 함수에서 탈락 처리라고 주석을 써놨던 곳에 Dead 태그를 활성화시켜준다.

캐릭터에 달아놓은 자기장 카운트 컴포넌트에 BP_GE_Dead 를 넣어주면 된다.

머티리얼 설정

머티리얼을 하나 만들어줬다.

Details 패널에서 Material > Blend Mode 를 Translucent 로,
Shading Model 은 Unlit,
Two Sided 는 체크를 해준다.

머티리얼 그래프에서는 다음과 같이 노드를 짜준다.

이후 BP_CRBlueZone 으로 가서 스태틱 메시를 Cylinder 로, 머티리얼을 방금 만든 M_BlueZone 으로 설정해준다.

학습 내용 요약 & 느낀점

일단 GAS 는 진짜 너무 어렵다.
함수들도 뭐가 뭔지 아직도 잘 모르겠다...

분명 열심히 만들고 있는데 잘 모르겠어서 지피티한테 도움을 받는게 대부분인 것 같아서 조금 아쉽다.
물론 코드 분석하면서 공부를 하고 있긴 하지만, 직접 떠올려서 한 게 아니라 더 분발해야겠다.

0개의 댓글