관리엔 질서가 필요하다.
질서가 생기면 효율이 생긴다.
질서는 명시되어야하고, 공유되어야한다.
그리고 경우에 따라 강제되어야하기도 한다.
대상이 자아가 있는 인격체라면 예민한 문제지만
생명이 없는 기계장치라면 이야기가 달라진다.
협업을 진행할 때 물론 규칙을 정해놓고 사용할 수도 있지만,
인터페이스를 사용한다면, 어긋나는 경우가 없을 것이다.
인터페이스(Interface)
- 여러 클래스에서 공통적으로 사용할 함수를 명시해놓는 기능.
- 일종의 코딩 설명서 또는 설계도
- 중복코드를 감소시키고, 다형성과 확장성이 좋아지고, 독립성을 증가시켜 유지보수에 이점을 가져온다.
- 인터페이스를 상속받은 클래스는 모두 인터페이스의 함수를 구현해야한다. (구현하지 않으면 에러발생)
작성 방법
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ItemInterface.generated.h"
UINTERFACE(MinimalAPI)
class UItemInterface : public UInterface // 인터페이스가 리플렉션 시스템에 등록되는 부분
{
GENERATED_BODY()
};
class YOURGAME_API IItemInterface // 실제 인터페이스 기능이 작성되는 부분
{
GENERATED_BODY()
public:
// 아이템이 플레이어와 오버랩되었을 때 호출됨
virtual void OnItemOverlap(AActor* OverlapActor) = 0;
// 아이템을 사용했을 때 호출됨
virtual void UseItem() = 0;
};
- 인터페이스에선 함수는 순수가상함수로 작성되어 정의는 상속받은 클래스에서 하도록 한다.
// cpp 파일
#include "ItemInterface.h" //아무것도 정의하지 않는다.
상속받은 클래스에서 구현
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemInterface.h" // 인터페이스 포함
#include "BaseItem.generated.h"
UCLASS()
class YOURGAME_API ABaseItem : public AActor, public IItemInterface // 상속
{
GENERATED_BODY()
public:
ABaseItem();
/** IItemInterface 구현 */
virtual void OnItemOverlap(AActor* OverlapActor) override;
virtual void UseItem() override;
};
//cpp파일에서 함수 구현
#include "BaseItem.h"
#include "GameFramework/Actor.h"
ABaseItem::ABaseItem()
{
PrimaryActorTick.bCanEverTick = true;
}
void ABaseItem::OnItemOverlap(AActor* OverlapActor)
{
UE_LOG(LogTemp, Warning, TEXT("아이템과 플레이어가 충돌했습니다."));
}
void ABaseItem::UseItem()
{
UE_LOG(LogTemp, Warning, TEXT("아이템이 사용되었습니다."));
}
사용 예시
- 인터페이스를 사용하지 않으면 아래와 같은 경우에 비슷한 기능을 각자 따로 구현해주어야한다.
// 인터페이스를 사용하지 않으면
class AWeapon : public AActor
{
public:
void Equip();
};
class AArmor : public AActor
{
public:
void Wear();
};
class AConsumable : public AActor
{
public:
void Use();
};
- 장착 실행 함수
void AMyCharacter::InteractWithItem(AActor* ItemActor)
{
if (AWeapon* Weapon = Cast<AWeapon>(ItemActor))
{
Weapon->Equip();
}
else if (AArmor* Armor = Cast<AArmor>(ItemActor))
{
Armor->Wear();
}
else if (AConsumable* Consumable = Cast<AConsumable>(ItemActor))
{
Consumable->Use();
}
}
- 이러한 경우에 인터페이스를 이용해 하나로 통일해 줄 수 있다.
class IItemInterface
{
public:
virtual void UseItem() = 0; // 모든 아이템이 반드시 구현해야 하는 함수
};
class AWeapon : public AActor, public IItemInterface
{
public:
void UseItem() override;
};
class AArmor : public AActor, public IItemInterface
{
public:
void UseItem() override;
};
class AConsumable : public AActor, public IItemInterface
{
public:
void UseItem() override;
};
- 장착 실행 함수
void AMyCharacter::InteractWithItem(AActor* ItemActor)
{
if (ItemActor->Implements<UItemInterface>()) // 인터페이스 상속 확인
{
IItemInterface* Item = Cast<IItemInterface>(ItemActor);
if (Item)
{
Item->UseItem(); // 아이템 종류와 관계없이 동일한 함수 호출
}
}
}
- 불필요한 조건문을 줄이고 새로운 아이템을 추가할 때도 기존 코드를 수정할 필요가 없다.
새로운 상속 클래스에서
- 새로운 상속 클래스를 생성할 때, 인터페이스에 있는 함수들을 일일이 작성할 필요 없이 자동완성 기능을 사용할 수 있다고 한다.
자동 완성 기능 활용법
- AMyActor의 클래스 정의 부분에서 public IMyInterface까지 입력한 후, 클래스 안에서 우클릭 > "Quick Actions and Refactorings..." 선택 (Ctrl + . 단축키)
- Implement pure virtual functions (순수 가상 함수 구현) 옵션 선택
- 필요한 함수들이 자동으로 생성
'게임 개발 공부 > 언리얼엔진' 카테고리의 다른 글
| UE5 | C++ - FPlatformTime::Seconds(); (0) | 2025.02.17 |
|---|---|
| UE5 | C++ - FTimerHandle (0) | 2025.02.13 |
| UE5 학습 - DataTable (1) | 2025.02.07 |
| UE5 학습 - 애니메이션 리타겟팅 (1) | 2025.02.06 |
| UE5 - 모델링모드 (2) | 2025.02.04 |