인터페이스 기반 아이템 클래스 설계하기
인터페이스 이해하기
1️⃣ 인터페이스란?
- 인터페이스 (Interface)란 클래스 (또는 오브젝트)가 반드시 구현해야 할 함수 목록만을 미리 정의해 두고, 실제 함수의 동작(구현 내용)은 해당 클래스를 상속받거나 구현하는 쪽에서 자유롭게 작성할 수 있도록 하는 일종의 계약서입니다.
- C++에서는 UInterface를 상속받아 IItemInterface 같은 인터페이스를 만들 수 있으며, 언리얼 블루프린트에서도 “블루프린트 인터페이스”를 통해 비슷한 개념을 구현할 수 있습니다.
- 즉 중요한 것은 함수의 구체적인 행동은 클래스에서 알아서 처리하는 것
- 인터페이스 = 규격을 정해주는 것.
- 주의할 점: 미래를 예측해서 너무 많이 만들어 두지 말자
2️⃣ 상속 (Inheritance)과의 차이점
- 상속 Inheritance
- 부모 클래스의 모든 속성과 기능을 자식 클래스가 물려받는 구조입니다.
- 부모 클래스에 구현된 로직을 자식 클래스가 그대로 사용할 수 있으며, 필요하다면 자식 클래스에서 재정의 (오버라이딩)할 수도 있습니다.
- 인터페이스 Interface
- 인터페이스는 “이 함수를 반드시 만들어야 한다”라는 함수 원형 (함수 시그니처)만을 정의합니다.
- 실제 함수가 어떻게 동작할지는 각 자식 (또는 구현 클래스)에서 자유롭게 작성할 수 있습니다.
- 상속은 부모의 실제 구현을 가져다 쓰는 반면, 인터페이스는 “함수의 틀”만 빌려 쓰고, 그 안에 담길 코드는 직접 만들어야 합니다.
3️⃣ 인터페이스를 사용하면 좋은 점
- 결합도 (Coupling) 감소
- 클래스 간 구체적인 구현 내용을 공유하지 않고, 필요한 함수 목록만 약속하므로 클래스 간 의존도가 낮아집니다.
- 즉, 다른 클래스 내부가 어떻게 돌아가는지 몰라도, “이 함수를 이렇게 호출하면 된다” 정도만 알면 됩니다.
- 코드가 깔끔해진다.
- 확장성 (Extensibility) 향상
- 새로운 아이템 클래스를 만들 때, 이미 정의된 인터페이스를 구현하기만 하면 기존 시스템에 쉽게 편입할 수 있습니다.
- 다형성 (Polymorphism) 극대화
- TArray<IItemInterface*> Items;와 같은 인터페이스 포인터 배열로 관리하면, 아이템 종류가 무엇이든 같은 함수를 호출하여 다룰 수 있습니다.
인터페이스 정의 및 아이템 부모 클래스 구현하기
1️⃣ ItemInterface 인터페이스 정의하기
- 이번에는 IItemInterface라는 인터페이스를 정의하고, 아이템의 공통 부모 클래스인 ABaseItem(Actor 상속)을 만들어 적용해 보겠습니다.
- 작은 코인, 큰 코인, 지뢰, 힐링 아이템 등 다양한 아이템은 모두 “플레이어와 오버랩되거나”, “사용 (Activate)될 때” 특정 동작을 수행합니다.
- 이처럼 공통적으로 필요한 함수를 인터페이스로 묶어두면, 다른 아이템들도 쉽게 동일한 규칙 (함수 시그니처)을 갖출 수 있습니다.



언리얼에서 인터페이스는 특이하게 Class 두 개를 구현합니다. (예: class UItemInterface : public UInterface, class BC_CH3_ASSIGNMENT_5_API IItemInterface)
- UINTERFACE(MinimalAPI)
- 언리얼 엔진의 리플렉션 시스템 (Reflection)을 위해 사용하는 매크로입니다.
- 이렇게 선언해야 블루프린트나 다른 모듈에서도 해당 인터페이스를 인식하고 사용할 수 있습니다.
- 따로 수정하지 않습니다
- class UItemInterface : public UInterface
- 실제 객체(클래스) 관리를 위한 언리얼 측 클래스로, C++의 IItemInterface와 구분해 사용합니다.
- class BC_CH3_ASSIGNMENT_5_API IItemInterface
- 우리가 직접 구현해서 사용할 인터페이스 함수들을 정의합니다.
- = 0;으로 끝나는 순수 가상 함수(Pure Virtual Function) 형태이므로, 반드시 이를 구현(Override)해야 합니다.
- 그래서 인터페이스의 경우 =0; 을 붙여줍니다.
- 매개변수를 AActor* 로 하는 이유는 액터, 즉 가장 큰 부모 클래스를 가져다 놓으면 그때그때 캐스팅만 하면 돼서 확장성에 매우 유리하기 때문입니다.
- FName으로 하는 이유: 보통 FString을 많이 쓰지만 FName은 타입형을 알아낼 때는 훨씬 더 속도도 빠르고 메모리 절약도 할 수 있습니다
- FSting이 생각보다 무겁습니다.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ItemInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UItemInterface : public UInterface
{
GENERATED_BODY()
};
class BC_CH3_ASSIGNMENT_5_API IItemInterface
{
GENERATED_BODY()
public:
// 플레이어가 이 아이템의 범위에 들어왔을 때 호출
virtual void OnItemOverlap(AActor* OverlapActor) = 0;
// 플레이어가 이 아이템의 범위를 벗어났을 때 호출
virtual void OnItemEndOverlap(AActor* OverlapActor) = 0;
// 아이템이 사용되었을 때 호출
virtual void ActivateItem(AActor* Activator) = 0;
// 이 아이템의 유형(타입)을 반환 (예: "Coin", "Mine" 등)
virtual FName GetItemType() const = 0;
};
2️⃣ 아이템 부모 클래스 구현하기
- 이제 Actor를 상속받은 “부모 아이템 클래스”를 만들고, 동시에 IItemInterface를 구현(Implement) 함으로써, 인터페이스에서 정의한 함수들을 실제로 작성해 봅시다.
- FileName 을 "BaseItem" 으로 (Public) 생성합니다.

- ABaseItem
- 모든 아이템에 공통적으로 적용될 기능을 담는 부모 클래스입니다.
- 인터페이스의 함수들을 빈 함수로 구현해 두어, 필요한 아이템 (자식 클래스)에서 오버라이드(Override) 하여 실제 로직을 작성하게끔 합니다.
- DestroyItem()
- 아이템을 제거하는 함수입니다.
- 단순히 Destroy()로 끝낼 수도 있고, 사운드나 파티클 이펙트 등을 추가하여 연출을 강화할 수도 있습니다.
- ItemType
- 에디터에서 “Coin”, “Mine”, “Potion” 같은 식으로 입력하여, 각 아이템의 타입을 구분할 수 있습니다.
- GetItemType()를 통해 간단히 아이템 종류를 확인할 수 있습니다.
BaseItem.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemInterface.h" // 만들어둔 인터페이스 헤더 포함
#include "BaseItem.generated.h"
UCLASS()
class BC_CH3_ASSIGNMENT_5_API ABaseItem : public AActor, public IItemInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ABaseItem();
protected:
// 아이템 유형(타입)을 편집 가능하게 지정
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
FName ItemType;
virtual void OnItemOverlap(AActor* OverlapActor) override;
virtual void OnItemEndOverlap(AActor* OverlapActor) override;
virtual void ActivateItem(AActor* Activator) override;
virtual FName GetItemType() const override;
// 아이템을 제거하는 공통 함수 (추가 이펙트나 로직을 넣을 수 있음)
virtual void DestroyItem();
};
BaseItem.cpp
#include "BaseItem.h"
ABaseItem::ABaseItem()
{
// 틱(매 프레임 실행)은 필요 없으므로 끕니다.
PrimaryActorTick.bCanEverTick = false;
}
// 플레이어가 아이템 범위에 들어왔을 때 동작
void ABaseItem::OnItemOverlap(AActor* OverlapActor)
{
// 기본은 빈 함수 - 각 자식 클래스에서 구현
}
// 플레이어가 아이템 범위를 벗어났을 때 동작
void ABaseItem::OnItemEndOverlap(AActor* OverlapActor)
{
// 기본은 빈 함수 - 필요하다면 자식 클래스에서 활용
}
// 아이템이 사용(Activate)되었을 때 동작
void ABaseItem::ActivateItem(AActor* Activator)
{
// 기본은 빈 함수 - 자식 클래스에서 구현
}
// 아이템 유형을 반환
FName ABaseItem::GetItemType() const
{
return ItemType;
}
// 아이템을 파괴(제거)하는 함수
void ABaseItem::DestroyItem()
{
// AActor에서 제공하는 Destroy() 함수로 객체 제거
Destroy();
}
코인 아이템 클래스 구현하기
작은 코인 (10점), 큰 코인 (50점), 지뢰 (5초 후 폭발), 힐링(체력 회복) 등 게임에서 사용할 아이템을 하나씩 구현해 봅시다.
모든 아이템은 BaseItem 를 상속받으며, 이미 정의해 둔 인터페이스 함수를 각각 다르게 재정의(오버라이드)하는 과정을 살펴봅니다.

1️⃣ 코인 아이템 공통 부모 CoinItem

- 먼저, “코인” 계열 아이템들의 공통 기능을 담을 CoinItem 클래스를 만듭니다.
- 코인 아이템이 가져야 할 공통 로직 (예: 사용 시 소멸)
- 코인 점수(PointValue) 속성을 담고 있으며, 다른 코인 아이템들은 이를 상속받아 필요한 부분만 재정의 (오버라이드)합니다.
- PointValue는 UPROPERTY로 선언했기 때문에, 에디터에서 값을 수정하거나 블루프린트로 접근이 가능합니다.
- CoinItem 자체는 직접 사용하기보다는, 구체적인 아이템 (BigCoin, SmallCoin)의 부모 클래스로 활용됩니다.
CoinItem.h
#pragma once
#include "CoreMinimal.h"
#include "BaseItem.h"
#include "CoinItem.generated.h"
UCLASS()
class BC_CH3_ASSIGNMENT_5_API ACoinItem : public ABaseItem
{
GENERATED_BODY()
public:
ACoinItem();
protected:
// 코인 획득 시 얻을 점수 (자식 클래스에서 상속받아 값만 바꿔줄 예정)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
int32 PointValue;
};
Coin.cpp
#include "CoinItem.h"
ACoinItem::ACoinItem()
{
// 부모 생성자 로직 (필요 시)
}
! BaseItem 그리고 CoinItem 에서 아이템 타입을 지정하지 않는 이유는: 두개다 추상 클래스이기 때문에
2️⃣ 빅 코인 아이템 BigCoinItem
- BigCoinItem은 50점짜리 코인입니다. ACoinItem을 상속받아서 간단히 점수를 50점으로 설정하고, ItemType에 "BigCoin"이라는 라벨을 달아줍니다.


- virtual void ActivateItem(AActor* Activator) override;는 코인 아이템이 ‘사용’되었을 때(획득)의 동작을 재정의하는 함수입니다.
- 여기서는 단순히 DestroyItem() 함수를 통해 아이템을 제거하고 있으나, 실제로는 플레이어 점수를 증가시키는 로직을 추가할 수 있습니다.
#pragma once
#include "CoreMinimal.h"
#include "CoinItem.h"
#include "BigCoinItem.generated.h"
UCLASS()
class BC_CH3_ASSIGNMENT_5_API ABigCoinItem : public ACoinItem
{
GENERATED_BODY()
public:
ABigCoinItem();
virtual void ActivateItem(AActor* Activator) override;
};
#include "BigCoinItem.h"
ABigCoinItem::ABigCoinItem()
{
PointValue = 50;
ItemType = "BigCoin";
}
void ABigCoinItem::ActivateItem(AActor* Activator)
{
DestroyItem(); // 아이템 소멸(부모의 DestroyItem() 혹은 자체 로직)
}
3️⃣ 스몰 코인 아이템 SmallCoinItem
- SmallCoinItem은 10점짜리 코인입니다. ACoinItem을 상속받아 점수와 아이템 타입만 다르게 지정해 줍니다.
- BaseItem 이나 BigCoinItem 이 아닌 CoinItem을 상속받아야 합니다.


- 큰 코인과 작은 코인의 차이는 점수(PointValue)와 구분용 문자열(ItemType) 뿐이지만, ActivateItem()을 통해 아이템 사용 로직을 재정의함으로써 각자 다른 동작 (또는 같은 동작)을 하도록 설정할 수 있습니다.
#pragma once
#include "CoreMinimal.h"
#include "CoinItem.h"
#include "SmallCoinItem.generated.h"
UCLASS()
class BC_CH3_ASSIGNMENT_5_API ASmallCoinItem : public ACoinItem
{
GENERATED_BODY()
public:
ASmallCoinItem();
virtual void ActivateItem(AActor* Activator) override;
};
#include "SmallCoinItem.h"
ASmallCoinItem::ASmallCoinItem()
{
PointValue = 10;
ItemType = "SmallCoin";
}
void ASmallCoinItem::ActivateItem(AActor* Activator)
{
DestroyItem();
}
힐링 및 지뢰 아이템 클래스 구현하기
1️⃣힐링 아이템 HealingItem
- 플레이어의 체력을 회복시키는 아이템입니다. HealAmount만큼 체력을 회복하고, 아이템을 소멸시킵니다.
- BaseItem 으로부터 상속 받아서 만듭니다.


#pragma once
#include "CoreMinimal.h"
#include "BaseItem.h"
#include "HealingItem.generated.h"
UCLASS()
class BC_CH3_ASSIGNMENT_5_API AHealingItem : public ABaseItem
{
GENERATED_BODY()
public:
AHealingItem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
float HealAmount;
virtual void ActivateItem(AActor* Activator) override;
};
#include "HealingItem.h"
AHealingItem::AHealingItem()
{
HealAmount = 20.0f;
ItemType = "Healing";
}
void AHealingItem::ActivateItem(AActor* Activator)
{
// 플레이어 캐릭터의 체력을 20만큼 회복시키는 로직 등을 구현 가능
DestroyItem();
}
2️⃣지뢰 아이템 MineItem
- 지뢰 아이템 (MineItem)은 설치된 후, 일정 시간이 지나면 폭발하여 주변에 피해를 줍니다. 폭발 범위와 폭발 데미지 등은 변수로 관리합니다.
- 동일하게 BaseItem 으로부터 상속 받아서 만듭니다.


#pragma once
#include "CoreMinimal.h"
#include "BaseItem.h"
#include "MineItem.generated.h"
UCLASS()
class BC_CH3_ASSIGNMENT_5_API AMineItem : public ABaseItem
{
GENERATED_BODY()
public:
AMineItem();
// 폭발까지 걸리는 시간
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Mine")
float ExplosionDelay;
// 폭발 범위
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Mine")
float ExplosionRadius;
// 폭발 데미지
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Mine")
float ExplosionDamage;
virtual void ActivateItem(AActor* Activator) override;
};
#include "MineItem.h"
AMineItem::AMineItem()
{
ExplosionDelay = 5.0f;
ExplosionRadius = 300.0f;
ExplosionDamage = 30.0f;
ItemType = "Mine";
}
void AMineItem::ActivateItem(AActor* Activator)
{
// 지뢰를 "사용"한다고 가정했을 때의 로직
// 여기서는 간단히 소멸시키지만,
// 실제로는 지연 시간 후 폭발 로직을 구현하거나,
// 폭발 이펙트, 데미지 계산 등을 추가할 수 있음
DestroyItem();
}
MineItem, HealingItem처럼 전혀 다른 동작을 하는 아이템들도 같은 함수 이름 (ActivateItem)으로 제어 가능합니다.
이러한 인터페이스 기반 설계는 한 번에 여러 종류의 아이템을 다형성으로 처리할 수 있어, 새로운 아이템을 추가하는 작업이 훨씬 쉬워집니다.
현재까지 만든 Class 구조

추천
[페이지] Unreal Engine | 언리얼 엔진