변경 사항
* 저번에 만든 ControlledPawnInterface 클래스 명을 PosableOneInterface로 바꿔주었다.
//헤더파일 명 변경
#include "PosableOneInterface.generated.h"
//첫번째 클래스 명 변경(U~)
class UPosableOneInterface : public UInterface
//두번째 클래스 명 변경(I~)
class GPC_CPP_12_API IPosableOneInterface
액션 매핑에 추가했던 Crouch 삭제 후, Pose A / Pose B 두 액션 매핑을 새로 추가했다.
C++ 인터페이스 구현하기
지금까지 블루프린트로 인터페이스 함수를 만들때 기본 구현을 미리 만들어둘 수 없었다.
하지만 C++ 인터페이스에서는 가능하다. 그래서 인터페이스 클래스 생성 시, cpp파일도 생성되는 것이다.
만약 블루프린트와 마찬가지로 기본 구현을 미리 만들지 않을 예정이라면 cpp파일을 지워버려도 된다.
* 순수 가상이 아닌 함수를 선언 시, 정의가 필요하다.
class GPC_CPP_12_API IPosableOneInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
virtual void BeginPoseA() PURE_VIRTUAL(IPosableOneInterface::BeginPoseA)
virtual void BeginPoseB() PURE_VIRTUAL(IPosableOneInterface::BeginPoseB)
virtual void EndPoseA() PURE_VIRTUAL(IPosableOneInterface::EndPoseA)
virtual void EndPoseB() PURE_VIRTUAL(IPosableOneInterface::EndPoseB)
};
순수 가상 함수는 뒤에 = 0; 을 붙이거나, abstract를 사용한다.
인터페이스에서는 변수를 만들지 않고, 엄격하게 보면 순수 가상 함수만 가지도록 해야한다.
하지만 언리얼에서 순수가상함수는 PURE_VIRTUAL(클래스명::함수이름)을 함수 뒤에 바로 붙여주면 만들어줄 수 있다.
PURE_VIRTUAL()을 사용하면 인스턴스를 생성하게 된다. 하지만 정의 부분이 구현된 상태가 아니므로 순수가상함수를 호출 시 오류가 발생한다.
다른 클래스에 인터페이스 적용하기
이제 GPCCharacter 클래스에 PosableOneInterface 인터페이스를 적용시켜 사용하기 위해 GPCCharacter의 헤더로 온다.
//GPCCharacter 클래스에 인터페이스 포함
#include "PosableOneInterface.h"
UCLASS(Abstract)
class GPC_CPP_12_API AGPCCharacter:
public ACharacter,
public IPosableOneInterface
//PosableOneInterface 인터페이스 상속
{
};
이제 GPCCharacter 클래스에서 PosableOneInterface 인터페이스를 상속하게 된다.
그런데 이로 인해 다중 상속의 문제가 발생하게 된다.
* 다중 상속의 문제(Diamond Problem)을 해결하기
1. 간단하게 다중 상속을 하지 않는 것이다.
2. 가상 상속을 활용한다.
하지만 이 부분의 다중 상속은 큰 문제 없이 허용된다.(특별히 같은 이름의 함수가 두 개나 있는 것이 아니라면 괜찮다.)
그리고 우리가 GPCCharacter 클래스로 인스턴스를 생성할 것이 아니기 때문에 AGPCCharacter 클래스에
UCLASS(Abstract)를 걸어두었다.
인터페이스 함수 정의
AGPCCharacter 클래스에 인터페이스 함수들을 오버라이드 해준다.
* 다중 상속중인 클래스이므로 각각의 함수가 어느것인지 표시해주기 위해 주석을 사용하거나
#pragma region ~ #pragma endregion으로 표시해줬다.
#pragma region IPosableOneInterface
private:
void BeginPoseA() override;
void BeginPoseB() override;
void EndPoseA() override;
void EndPoseB() override;
#pragma endregion
이제 함수들을 정의해주기 전에, 호출하는 부분 먼저 만들어준다.
때문에 AGPCPlayerController 클래스에서 PosableOneInterface 클래스 헤더를 포함해주고, 상속도 추가해준다.
class GPC_CPP_12_API AGPCPlayerController:
public APlayerController,
public IPosableOneInterface
{
GENERATED_BODY()
protected:
void SetupInputComponent() override;
.
.
.
#pragma region IPosableOneInterface
private:
void BeginPoseA() override;
void BeginPoseB() override;
void EndPoseA() override;
void EndPoseB() override;
#pragma endregion
};
마찬가지로 이곳에도 인터페이스 함수들을 오버라이드하여 추가해준다.
이제 GPCPlayerController 클래스의 cpp로 가서 정의해주었다.
void AGPCPlayerController::BeginPoseA()
{
if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
PosableOne->BeginPoseA();
}
void AGPCPlayerController::BeginPoseB()
{
if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
PosableOne->BeginPoseB();
}
void AGPCPlayerController::EndPoseA()
{
if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
PosableOne->EndPoseA();
}
void AGPCPlayerController::EndPoseB()
{
if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
PosableOne->EndPoseB();
}
이어서 GPCPlayerController 클래스의 SetupInputComponent 함수에서 해당 액션입력 시, 해당하는 함수를 호출한다.
void AGPCPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAxis("MoveFB", this, &ThisClass::MoveFB);
InputComponent->BindAxis("MoveLR", this, &ThisClass::MoveLR);
InputComponent->BindAxis("LookLR", this, &ThisClass::LookLR);
InputComponent->BindAxis("LookUD", this, &ThisClass::LookUD);
//여기서 입력 시 해당하는 함수를 부르게 된다.
InputComponent->BindAction("PoseA", EInputEvent::IE_Pressed, this, &ThisClass::BeginPoseA);
InputComponent->BindAction("PoseB", EInputEvent::IE_Pressed, this, &ThisClass::BeginPoseB);
InputComponent->BindAction("PoseA", EInputEvent::IE_Released, this, &ThisClass::EndPoseA);
InputComponent->BindAction("PoseB", EInputEvent::IE_Released, this, &ThisClass::EndPoseB);
}
이렇게 호출 부분을 작성했다면, GPCCharacter 클래스에서 정의했던 인터페이스 함수에 Logger::Print("함수이름");을
추가하여 빌드하고, 제대로 함수가 호출되는지 확인해볼 수 있다.
'언리얼엔진' 카테고리의 다른 글
[C++] ActableOneInterface / WeaponInterface (0) | 2023.09.20 |
---|---|
[C++] 스테이트 머신(Standing - Crouching) / 에임 오프셋 / Camera Lag Enable (0) | 2023.09.19 |
[C++] GameModeBase 설정 / Stand, Crouch 애니메이션 / 인터페이스 생성 (0) | 2023.09.14 |
[C++] Move, Look 함수 정의 / PlayerController 클래스 (3) | 2023.09.14 |
[C++] Character의 이동 구현 / SpringArm, Camera 컴포넌트 생성 (0) | 2023.09.12 |