1. 개요
예제에서 만들고 있는 쿼터뷰 액션 RPG 게임에서, 적을 마우스로 가리키면 적의 외각선이 빨간색으로 드러나는 기능을 구현하고자 한다.
플레이어의 조작은 플레이어 컨트롤러에서 대응하고 있으므로 플레이어 컨트롤러에서 구현할 수는 있으나, 그럴 경우 플레이어 컨트롤러 클래스가 적 AI 액터 클래스와 엮여버리는 의존성 문제가 발생한다.
위의 그림처럼 Enemy 액터에 IEnemyInterface를 도입하여 Enemy 액터의 Highlight 함수를 호출하는 방식을 사용하면 플레이어 컨트롤러와 Enemy 액터와의 의존성을 제거할 수 있어 코드가 유연해진다.
또한, Enemy 클래스를 상속받는 자식들도 Highlight 함수를 오버라이딩 하여 사용할 수 있기 때문에 다형성을 확보할 수 있다.
2. 코드
(1) EnemyInterface.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "EnemyInterface.generated.h"
UINTERFACE(MinimalAPI)
class UEnemyInterface : public UInterface
{
GENERATED_BODY()
};
class AURA_API IEnemyInterface
{
GENERATED_BODY()
public:
// 순수 추상화
virtual void HighlightActor() = 0;
virtual void UnHighlightActor() = 0;
};
Enemy 액터들이 HighlightActor와 UnHighlightActor를 오버라이딩해서 사용할 수 있도록 추상화(Abstract) 한 함수를 만든다.
(2) AuraEnemy.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Character/AuraCharacterBase.h"
#include "Interaction/EnemyInterface.h"
#include "AuraEnemy.generated.h"
/**
*
*/
UCLASS()
class AURA_API AAuraEnemy : public AAuraCharacterBase, public IEnemyInterface
{
GENERATED_BODY()
public:
virtual void HighlightActor() override;
virtual void UnHighlightActor() override;
};
해당 인터페이스를 상속받도록 추가하고 include 또한 수정한다.
(3) AuraPlayerController.h
class AURA_API AAuraPlayerController : public APlayerController
{
GENERATED_BODY()
public:
AAuraPlayerController();
virtual void PlayerTick(float DeltaTime) override;
private:
void CursorTrace();
TScriptInterface<IEnemyInterface> LastActor;
TScriptInterface<IEnemyInterface> ThisActor;
이제 매 틱마다 플레이어 컨트롤러에서 마우스가 적 위에 위치하는지 감지하는 함수를 작성한다.
LastActor는 이전까지 마우스가 가리키던 적의 포인터, ThisActor는 지금 틱에 마우스가 가리키는 적의 포인터를 말한다.
3. 커서 충돌
(1) 커서 충돌의 다섯가지 상황
커서가 Enemy 액터와 충돌할 때 발생하는 여러가지 상황을 생각한다.
1) LastActor가 nullptr, ThisActor가 nullptr
- 적이 아닌 액터
- 아무일도 일어나지 않음
2) LastActor가 nullptr, ThisActor가 유효
- 적에게 처음으로 마우스를 가져다 댐
- ThisActor->HighlightActor();
3) LastActor가 유효, ThisActor가 nullptr
- 적을 가리키다가 더 이상 마우스가 위치하지 않음
- LastActor->UnHighlightActor();
4) LastActor와 ThisActor 모두 유효, LastActor != ThisActor
- 적을 가리키다가 다른 적을 가리키게 됨
- LastActor->UnHighlightActor();
- ThisActor->HighlightActor();
5) LastActor와 ThisActor 모두 유효, LastActor == ThisActor
- 이미 Highlight 함수가 발동
- 아무 일도 일어나지 않음
일어날 수 있는 모든 상황을 생각한 후에 코드를 작성한다.
void AAuraPlayerController::CursorTrace()
{
FHitResult CursorHit;
// 트레이스 채널, 단순 충돌 확인, 반환되는 FHitResult 구조체의 주소
GetHitResultUnderCursor(ECC_Visiblity, false, &CursorHit);
if (!CursorHit.bBlockingHit)
return;
LastActor = ThisActor;
// 마우스 커서와 충돌한 액터 꺼내오기
ThisActor = CursorHit.GetActor()
if (LastActor == nullptr)
{
if (ThisActor != nullptr)
{
// 2번 케이스
ThisActor->HighlightActor();
}
else
{
// 1번 케이스
}
}
else // LastActor가 유효
{
if (ThisActor == nullptr)
{
// 3번 케이스
LastActor->UnHighlightActor();
}
else
{
if (LastActor == ThisActor)
{
// 5번 케이스
}
else
{
// 4번 케이스
LastActor->UnHighlightActor();
ThisActor->HightlightActor();
}
}
}
}
Enemy Class에 bHighlight 불리언을 만들고 HighLight 함수가 발동되면 True, UnHighLight 함수가 발동되면 false가 되도록 설정한다.
이제 BP_Goblin_Spear에서 bHighlight를 받아 디버그 구를 소환하도록 하면 마우스를 가져다 댔을 때 정상적으로 작동하는 것을 볼 수 있다.
(2) 모든 적에게 적용하기
BP_Goblin_Spear 뿐 아닌 다른 적에게도 해당 기능을 적용할 수 있도록 모든 적이 상속받는 부모 블루프린트 클래스를 만든다.
모든 적들의 블루프린트 클래스 세팅에 들어가서 부모를 새로 생성한 BP_EnemyBase로 설정한다
'UE 5 스터디 > Gameplay Ability System(GAS)' 카테고리의 다른 글
2-1. 게임플레이 어빌리티 시스템(Gameplay Ability System - GAS) (0) | 2024.08.14 |
---|---|
1-6. 마우스로 적 외곽선 표시하기 (2) - 포스트 프로세스 볼륨 (Post-Process Volume) (0) | 2024.08.14 |
1-4. 기본 캐릭터 클래스 (Base Character Class) (2) (0) | 2024.08.13 |
1-3. 기본 캐릭터 클래스 (Base Character Class) (1) (0) | 2024.08.13 |
1-2. 디버그 설정 (0) | 2024.08.13 |