언리얼에서 인터페이스를 사용하는 방법에 대해 정리해보자.
커서를 가져다 대면 highlight 시켜주는 기능을 위해 인터페이스를 사용한다.
위 기능 구현을 위해 한번 간략하게 정리해보자.
- 커서 아래에 뭐가 있는지를 일단 추적한다.
- 액터가 있다면 액터의 인터페이스를 통해 highlight해준다.
EnemyInterface라는 C++ 인터페이스 클래스를 만든다.
<EnemyInterface.h>
// Copyright mumu
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "EnemyInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UEnemyInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class AURA_API IEnemyInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
// Pure Virtual function, Consider as abstract class
virtual void HighlightActor() = 0;
virtual void UnHighlightActor() = 0;
};
virtual void HighlightActor() = 0; 처럼 추상 메서드를 선언했다.
이렇게 추상 메서드로 선언을 해주는 경우 c++에서 따로 구현해주지 않아도 되는 장점이 있다.
이제 인터페이스를 상속해보자.
AuraEnemy클래스에서 상속
<AuraEnemy.h>
// Copyright mumu
#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;
UPROPERTY(BlueprintReadOnly)
bool bHighlighted = false;
};
상속 받은 클래스들에 순수 가상함수를 둘순 없으니 여기서는 c++로 구현을 해주자.
<AuraEnemy.cpp>
// Copyright mumu
#include "Character/AuraEnemy.h"
void AAuraEnemy::HighlightActor()
{
bHighlighted = true;
}
void AAuraEnemy::UnHighlightActor()
{
bHighlighted = false;
}
bHighlighted가 true면 블루프린트에서 Highlight가 되도록 처리해주면 될듯하다.
마우스 커서를 추적해 추적한 액터의 인터페이스를 통해 highlight를 해준다.
<AuraPlayerController.h>
// Copyright mumu
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "AuraPlayerController.generated.h"
class IEnemyInterface;
/**
*
*/
UCLASS()
class AURA_API AAuraPlayerController : public APlayerController
{
GENERATED_BODY()
public:
virtual void PlayerTick(float DeltaTime) override;
private:
void CursorTrace();
TScriptInterface<IEnemyInterface> LastActor;
TScriptInterface<IEnemyInterface> ThisActor;
};
TOjectPtr처럼 언리얼에서 공식으로 제공하는 키워드다.
원시 포인터 대신 TScriptInterface를 사용하는게 '권장'된다.
<AuraPlayerController.cpp>
// Copyright mumu
#include "Player/AuraPlayerController.h"
#include "Interaction/EnemyInterface.h"
AAuraPlayerController::AAuraPlayerController()
{
bReplicates = true;
}
void AAuraPlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
CursorTrace();
}
void AAuraPlayerController::CursorTrace()
{
FHitResult CursorHit;
GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);
if(!CursorHit.bBlockingHit) return;
// If you use old pointer, need to Cast IEnemyInterface from AActor. Instead, Set directly by TScriptInterface.
LastActor = ThisActor;
ThisActor = CursorHit.GetActor();
/*
* Line trace from cursor. There are several scenarios:
* A. LastActor is null && ThisActor is null
* - DO nothing.
* B. LastActor is null && ThisActor is valid
* - Highlight ThisActor
* C. LastActor is valid && ThisActor is null
* - UnHighlight LastActor
* D. Both actors are valid, but LastActor != ThisActor
* - UnHighlight LastActor, and Highlight ThisActor
* E. Both actors are valid, and are the same actor
* - Do nothing
*/
if(LastActor == nullptr)
{
if(ThisActor != nullptr)
{
// Case B
ThisActor->HighlightActor();
}
else
{
// Case A - both are null, do nothing
}
}
else // LastActor is valid
{
if(ThisActor == nullptr)
{
// Case C
LastActor->UnHighlightActor();
}
else // both actors are valid
{
if(LastActor != ThisActor)
{
// Case D
LastActor->UnHighlightActor();
ThisActor->HighlightActor();
}
else
{
// Case E - do nothing
}
}
}
}
FHitResult CursorHit;
GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);
if(!CursorHit.bBlockingHit) return;
// If you use old pointer, need to Cast IEnemyInterface from AActor. Instead, Set directly by TScriptInterface.
LastActor = ThisActor;
ThisActor = CursorHit.GetActor();
해당 부분을 보면 알 수 있는데, 원시 포인터는 AActor에서 Inaterface로 Cast를 해줘야하지만 TScriptInterface는 따로 Cast를 해줄 필요가 없다.
만약 다음과 같이
IEnemyInterface* LastActor;
IEnemyInterface* ThisActor;
원시포인터였다면..
ThisActor = Cast<IEnemyInterface>(CursorHit.GetActor());
이렇게 Cast를 해줘야 한다.
/*
* Line trace from cursor. There are several scenarios:
* A. LastActor is null && ThisActor is null
* - DO nothing.
* B. LastActor is null && ThisActor is valid
* - Highlight ThisActor
* C. LastActor is valid && ThisActor is null
* - UnHighlight LastActor
* D. Both actors are valid, but LastActor != ThisActor
* - UnHighlight LastActor, and Highlight ThisActor
* E. Both actors are valid, and are the same actor
* - Do nothing
*/
커서를 다른 적으로 옮기면 원래 highlight를 없애고 옮긴 적을 highlight 해주기 위해 if else문으로 케이스를 나눴다.
'언리얼' 카테고리의 다른 글
[UE5] Enhanced Input (1) | 2024.06.09 |
---|---|
[UE5] Animation Blueprint 기초 (0) | 2024.06.09 |