인터페이스 기반 아이템 클래스 설계하기

주상돈·2025년 2월 5일

TIL

목록 보기
24/53

인터페이스 이해하기


1️⃣ 인터페이스란?

  • 인터페이스 (Interface)란 클래스 (또는 오브젝트)가 반드시 구현해야 할 함수 목록만을 미리 정의해 두고, 실제 함수의 동작(구현 내용)은 해당 클래스를 상속받거나 구현하는 쪽에서 자유롭게 작성할 수 있도록 하는 일종의 계약서이다.

2️⃣ 상속 (Inheritance)과의 차이점

  • 상속
    • 부모 클래스의 모든 속성과 기능을 자식 클래스가 물려받는 구조.
    • 부모 클래스에 구현된 로직을 자식 클래스가 그대로 사용할 수 있으며, 필요하다면 자식 클래스에서 재정의 (오버라이딩)할 수도 있다.
  • 인터페이스
    • 인터페이스는 “이 함수를 반드시 만들어야 한다”라는 함수 원형 (함수 시그니처)만을 정의.
    • 실제 함수가 어떻게 동작할지는 각 자식 (또는 구현 클래스)에서 자유롭게 작성할 수 있다.
  • 상속은 부모의 실제 구현을 가져다 쓰는 반면, 인터페이스는 “함수의 틀”만 빌려 쓰고, 그 안에 담길 코드는 직접 만들어야 합니다.

3️⃣ 인터페이스를 사용하면 좋은 점

  • 결합도 (Coupling) 감소
  • 확장성 (Extensibility) 향상
  • 다형성 (Polymorphism) 극대화

인터페이스 정의 및 아이템 부모 클래스 구현하기


1️⃣ ItemInterface 인터페이스 정의하기

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "IItemInterface.generated.h"

// 인터페이스를 UObject 시스템에서 사용하기 위한 기본 매크로
UINTERFACE(MinimalAPI)
class UItemInterface : public UInterface
{
    GENERATED_BODY()
};

// 실제 C++ 레벨에서 사용할 함수 원형(시그니처)를 정의
class SPARTAPROJECT_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;
};

IItemInterface.h

  • UINTERFACE(MinimalAPI)
    • 언리얼 엔진의 리플렉션 시스템 (Reflection)을 위해 사용하는 매크로.
    • 이렇게 선언해야 블루프린트나 다른 모듈에서도 해당 인터페이스를 인식하고 사용할 수 있다.
  • class UItemInterface : public UInterface
    • 실제 객체(클래스) 관리를 위한 언리얼 측 클래스로, C++의 IItemInterface와 구분해 사용한다.
  • class SPARTAPROJECT_API IItemInterface
    • 우리가 직접 구현해서 사용할 인터페이스 함수들을 정의한다.
    • = 0;으로 끝나는 순수 가상 함수(Pure Virtual Function) 형태이므로, 반드시 이를 구현(Override)해야 한다.

2️⃣ 아이템 부모 클래스 구현하기

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemInterface.h"  // 만들어둔 인터페이스 헤더 포함
#include "BaseItem.generated.h"

UCLASS()
class SPARTARPROJECT_API ABaseItem : public AActor, public IItemInterface
{
    GENERATED_BODY()

public:    
    ABaseItem();
    
protected:
		// 아이템 유형(타입)을 편집 가능하게 지정
		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		FName ItemType;

		// IItemInterface에서 요구하는 함수들을 반드시 구현
		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.h

#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();
}

BaseItem.cpp

  • ABaseItem
    • 모든 아이템에 공통적으로 적용될 기능을 담는 부모 클래스.
    • 인터페이스의 함수들을 빈 함수로 구현해두어, 필요한 아이템 (자식 클래스)에서 오버라이드(Override) 하여 실제 로직을 작성하게끔 한다.
  • DestroyItem()
    • 아이템을 제거하는 함수.
    • 단순히 Destroy()로 끝낼 수도 있고, 사운드나 파티클 이펙트 등을 추가하여 연출을 강화할 수도 있다.
  • ItemType
    • 에디터에서 “Coin”, “Mine”, “Potion” 같은 식으로 입력하여, 각 아이템의 타입을 구분할 수 있다.
    • GetItemType()를 통해 간단히 아이템 종류를 확인할 수 있다.

코인 아이템 클래스 구현하기


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

  • 먼저, “코인” 계열 아이템들의 공통 기능을 담을 CoinItem 클래스를 만들자.
    • 코인 아이템이 가져야 할 공통 로직 (예: 사용 시 소멸)
    • 코인 점수(PointValue) 속성
#pragma once

#include "CoreMinimal.h"
#include "BaseItem.h"
#include "CoinItem.generated.h"

UCLASS()
class SPARTAPROJECT_API ACoinItem : public ABaseItem
{
		GENERATED_BODY()

public:
		ACoinItem();

protected:
		// 코인 획득 시 얻을 점수 (자식 클래스에서 상속받아 값만 바꿔줄 예정)
		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 PointValue;
};

CoinItem.h

#include "CoinItem.h"

ACoinItem::ACoinItem()
{
		// 부모 생성자 로직 (필요 시)
}

CoinItem.cpp

  • PointValueUPROPERTY로 선언했기 때문에, 에디터에서 값을 수정하거나 블루프린트로 접근이 가능.
  • CoinItem 자체는 직접 사용하기보다는, 구체적인 아이템 (BigCoin, SmallCoin)의 부모 클래스로 활용된다.

2️⃣ 빅 코인 아이템 BigCoinItem

위에 만든 CoinItem을 부모로 상속받아서 BigCoinItem클래스를 작성해보자.

#pragma once

#include "CoreMinimal.h"
#include "CoinItem.h"
#include "BigCoinItem.generated.h"

UCLASS()
class SPARTAPROJECT_API ABigCoinItem : public ACoinItem
{
		GENERATED_BODY()

public:
		ABigCoinItem();
	
		virtual void ActivateItem(AActor* Activator) override;
};

BigCoinItem.h

#include "BigCoinItem.h"

ABigCoinItem::ABigCoinItem()
{
		PointValue = 50;
		ItemType = "BigCoin";
}

void ABigCoinItem::ActivateItem(AActor* Activator)
{
		DestroyItem(); // 아이템 소멸(부모의 DestroyItem() 혹은 자체 로직)
}

BigCoinItem.cpp

  • virtual void ActivateItem(AActor* Activator) override;코인 아이템이 ‘사용’되었을 때(획득)의 동작을 재정의하는 함수.
  • 여기서는 단순히 DestroyItem() 함수를 통해 아이템을 제거하고 있으나, 실제로는 플레이어 점수를 증가시키는 로직을 추가할 수 있다.

3️⃣ 스몰 코인 아이템 SmallCoinItem

BigCoinItem은 50점으로 설정했다. SmallCoinItem의 점수는 10점으로 설정하고 작성해보자.

#pragma once

#include "CoreMinimal.h"
#include "CoinItem.h"
#include "SmallCoinItem.generated.h"

UCLASS()
class SPARTAPROJECT_API ASmallCoinItem : public ACoinItem
{
		GENERATED_BODY()

public:
		ASmallCoinItem();

		virtual void ActivateItem(AActor* Activator) override;
};

SmallCoinItem.h

#include "SmallCoinItem.h"

ASmallCoinItem::ASmallCoinItem()
{
		PointValue = 10;
		ItemType = "SmallCoin";
}

void ASmallCoinItem::ActivateItem(AActor* Activator)
{
		DestroyItem();
}

SmallCoinItem.cpp

  • 큰 코인과 작은 코인의 차이는 점수(PointValue)와 구분용 문자열(ItemType) 뿐이지만, ActivateItem()을 통해 아이템 사용 로직을 재정의함으로써 각자 다른 동작 (또는 같은 동작)을 하도록 설정할 수 있다.

지뢰 및 힐링 아이템 클래스 구현하기


1️⃣ 지뢰 아이템 MineItem

#pragma once

#include "CoreMinimal.h"
#include "ItemBase.h"
#include "MineItem.generated.h"

UCLASS()
class SPARTAPROJECT_API AMineItem : public AItemBase
{
    GENERATED_BODY()

public:
    AMineItem();

    // 폭발까지 걸리는 시간 (5초)
    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;
};

MineItem.h

#include "MineItem.h"

AMineItem::AMineItem()
{
		ExplosionDelay = 5.0f;
		ExplosionRadius = 300.0f;
		ExplosionDamage = 30.0f;
		ItemType = "Mine";
}

void AMineItem::ActivateItem(AActor* Activator)
{
		// 지뢰를 "사용"한다고 가정했을 때의 로직
    // 여기서는 간단히 소멸시키지만,
    // 실제로는 지연 시간 후 폭발 로직을 구현하거나,
    // 폭발 이펙트, 데미지 계산 등을 추가할 수 있음
		DestroyItem();
}

MineItem.cpp
MineItem은 설치된 후, 플레이어가 접촉하면 일정 시간이 지나면 폭발범위만큼 주변에 피해를 주는 아이템이다.

2️⃣ 힐링 아이템 HealingItem

#pragma once

#include "CoreMinimal.h"
#include "BaseItem.h"
#include "HealingItem.generated.h"

UCLASS()
class SPARTAPROJECT_API AHealingItem : public ABaseItem
{
	GENERATED_BODY()

public:
	AHealingItem();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	float HealAmount;

	virtual void ActivateItem(AActor* Activator) override;
};

HealingItem.h

#include "HealingItem.h"

AHealingItem::AHealingItem()
{
		HealAmount = 20.0f;
		ItemType = "Healing";
}

void AHealingItem::ActivateItem(AActor* Activator)
{
		// 플레이어 캐릭터의 체력을 20만큼 회복시키는 로직 등을 구현 가능
		DestroyItem();
}

HealingItem.cpp
HealingItem은 포션 아이템이다. 플레이어의 체력을 회복시킨다.

  • MineItem, HealingItem처럼 전혀 다른 동작을 하는 아이템들도 같은 함수 이름 (ActivateItem)으로 제어 가능.
  • 이러한 인터페이스 기반 설계는 한 번에 여러 종류의 아이템을 다형성으로 처리할 수 있어, 새로운 아이템을 추가하는 작업이 훨씬 쉬워진다.

0개의 댓글