-
[UE5] 인터페이스의 활용UE5 2024. 12. 5. 06:02
이득우의 언리얼 프로그래밍 pt2에서는 세 개의 레이어를 기준으로 게임의 다양한 기능을 구현한다.
게임 구성 요소의 분류 하위 레이어에서 상위 레이어를 참조할 때는 인터페이스를 활용하여 구현한다.
인터페이스를 활용할 때의 장점
1. 의존성 문제 해결
- 하위 레이어가 상위 레이어에 강하게 결합하면, 의존성 문제가 발생할 수 있음
class UABCharacterHUD : public UUserWidget { public: void UpdateHPBar(AABCharacterBase* Character) { float CurrentHP = Character->GetCurrentHP(); float MaxHP = Character->GetMaxHP(); HPBar->SetPercent(CurrentHP / MaxHP); } private: UProgressBar* HPBar; // UI의 HP 바 위젯 };
- 만약 캐릭터 클래스가 AABCharacterBase가 아닌 ANewCharacterBase로 변경되면
- UI레이어(UABCharacterHUD) 코드도 수정해야함
- 유지보수성이 떨어지고, 강한 결합이 발생
- 다른 캐릭터 타입이 추가되면 UI 레이어가 이를 모두 인식하고 처리해야함
- 새로운 캐릭터마다 UpdateHPBar를 수정하거나 if/else 체인을 만들어야함
해결방법
class IABCharacterWidgetInterface { public: virtual float GetCurrentHP() const = 0; virtual float GetMaxHP() const = 0; };
- AABCharacterBase와 ANewCharacterBase 같은 캐릭터 클래스에서 IABCharacterWidget 인터페이스를 상속받아 구현
class AABCharacterBase : public ACharacter, public IABCharacterWidgetInterface { public: virtual float GetCurrentHP() const override { return CurrentHP; } virtual float GetMaxHP() const override { return MaxHP; } private: float CurrentHP; float MaxHP; }; class ANewCharacterBase : public ACharacter, public IABCharacterWidgetInterface { public: virtual float GetCurrentHP() const override { return NewCurrentHP; } virtual float GetMaxHP() const override { return NewMaxHP; } private: float NewCurrentHP; float NewMaxHP; };
- UI 레이어에서는 캐릭터의 구체적인 구현을 알 필요없이 인터페이스만 참조하면 됨
class UABCharacterHUD : public UUserWidget { public: void UpdateHPBar(IABCharacterWidgetInterface* Character) { float CurrentHP = Character->GetCurrentHP(); float MaxHP = Character->GetMaxHP(); HPBar->SetPercent(CurrentHP / MaxHP); } private: UProgressBar* HPBar; // UI의 HP 바 위젯 };
- 실제 사용
IABCharacterWidgetInterface* Character1 = new AABCharacterBase(); IABCharacterWidgetInterface* Character2 = new ANewCharacterBase(); HpBarWidget->UpdateHpBar(Character1); HpBarWidget->UpdateHpBar(Character2);
2. 캐릭터의 역할 분리와 책임 모듈화
- 캐릭터가 여러 인터페이스를 상속받음으로써, 캐릭터의 동작을 역할 단위로 분리할 수 있음
class IABAnimationAttackInterface { virtual void PerformAttackAnimation() = 0; }; class IABCharacterWidgetInterface { virtual void UpdateCharacterHPBar(float CurrentHP, float MaxHP) = 0; }; class IABCharacterItemInterface { virtual void EquipItem(UItem* Item) = 0; };
이런 설계를 통해 책임의 분리와 모듈화가 가능해짐
3. 다형성을 활용한 유연성
void TriggerAttackAnimation(IABAnimationAttackInterface* Character) { Character->PerformAttackAnimation(); }
- 상위 레이어인 AABCharacterBase는 구체적으로 어떤 캐릭터인지 알 필요없음
- IABAnimationAttackInterface를 구현한 캐릭터(상속받은 캐릭터)라면 누구나 호출가능
- 이를 통해 새로운 캐릭터 클래스 추가 시 기존 로직 수정 없이 인터페이스만 구현하면 됨
- 예시) ABossCharacter는 방어막과 HP를 합산한 값을 반환하도록 구현
class ABossCharacter : public ACharacter, public IABCharacterWidgetInterface { public: virtual float GetCurrentHP() const override { return Shield > 0.0f ? Shield + CurrentHP : CurrentHP; } virtual float GetMaxHP() const override { return MaxHP + MaxShield; } private: float CurrentHP = 200.0f; // 기본 HP float MaxHP = 200.0f; // 최대 HP float Shield = 50.0f; // 현재 방어막 float MaxShield = 50.0f; // 최대 방어막 };
- 이와 같이 UI 로직 수정없이 새로운 캐릭터 클래스가 동작
4. 다중 상속의 강점
- C++에서 인터페이스는 다중 상속이 가능하며, 캐릭터가 다양한 인터페이스를 상속받아 각각의 책임을 구현
class AABCharacterBase : public ACharacter, public IABAnimationAttackInterface, public IABCharacterWidgetInterface, public IABCharacterItemInterface { public: virtual void PerformAttackAnimation() override { /* 공격 애니메이션 로직 */ } virtual void UpdateCharacterHPBar(float CurrentHP, float MaxHP) override { /* UI 업데이트 로직 */ } virtual void EquipItem(UItem* Item) override { /* 아이템 장착 로직 */ } };
결론
- 의존성 역전 원칙(DIP):
- 하위 레이어(UI, Item 등)는 상위 레이어(Character)를 구체적으로 참조하지 않고, 인터페이스만 참조.
- 모듈화:
- 캐릭터의 복잡한 동작을 인터페이스로 분리하여 역할과 책임을 명확히 구분.
- 유연성:
- 다양한 타입의 캐릭터를 인터페이스를 통해 동작하게 할 수 있음(다형성).
- 확장성:
- 새로운 캐릭터나 시스템을 추가할 때, 기존 코드를 수정하지 않고 새로운 인터페이스를 구현하면 됨.
'UE5' 카테고리의 다른 글
[UE5] 네트워크 멀티플레이어 (0) 2024.12.10 [UE5] 추상클래스와 인터페이스 (0) 2024.12.06 [UE5] 순수 가상 함수(Pure Virtual Function) (0) 2024.12.06 [UE5] 단일 캐스팅 델리게이트를 이용한 다중 동작 이벤트 처리 (2) 2024.12.03 [UE5] CoreMinimal.h란? (1) 2024.12.03