[UE] 언리얼 C++ 설계 1 - 인터페이스
언리얼 C++ 인터페이스 클래스를 사용해 보다 안정적으로 클래스를 설계하는 기법을 학습하자.
목차
언리얼 C++ - 인터페이스
언리얼 C++ 인터페이스
인터페이스란?
- 객체가 반드시 구현해야 할 행동을 지정하는데 활용되는 타입
- 다형성(Polymorphism)의 구현, 의존성이 분리(Decouple)된 설계에 유용하게 활용
언리얼 엔진에서 게임 콘텐츠를 구성하는 오브젝트의 설계 예시
- 언리얼 엔진 월드에 배치되는 모든 오브젝트.
- 월드에 배치되는 모든 오브젝트를 액터라고 한다. 액터(Actor)는 움직이는 물체와 안 움직않는 물체를 모두 통틀은 상위 개념이다.
- 움직이지 오브젝트 ( Pawn )
- 길찾기 시스템을 반드시 사용하면서 움직이는 오브젝트 ( INavAgentInterface 인터페이스를 구현한 Pawn)
- Pawn 인스턴스가 길찾기 시스템을 반드시 사용하면서 움직이고 싶다면, INavAgentInterface 를 구현하도록 설계되어 있다.
예제를 위한 클래스 다이어그램
- 수업에 참여하는 사람과 참여하지않는 사람의 구분
- 수업에 반드시 참여해야 하는 학교 구성원: 학생, 선생
- 수업에 참여하지 않는 학교 구성원: 교직원
- 수업 행도에 관련된 인터페이스: ILessonInterface
언리얼 C++ 인터페이스 특징
- 언리얼 엔진에서 하나의 인터페이스를 생성하면 두 개의 클래스가 생성
- U 로 시작하는 타입 클래스
- I 로 시작하는 인터페이스 클래스
- 객체를 설계할 때 I 인터페이스 클래스를 사용한다.
- U 타입 클래스 정보는 런타임에서 인터페이스 구현 여부를 파악하는 용도로 사용됨. (이는 언리얼 엔진의 객체 시스템과 호환되기 위함이 목적이다.)
- 실제로 U 타입 클래스에서 작업할 일은 없음.
- 실질적으로 인터페이스에 관련된 구성 및 구현은 I 인터페이스 클래스에서 진행.
- C++ 인터페이스의 특징
- 추상 타입으로만 선언할 수 있는 Java, C#과 달리 언리얼 엔진에서는 인터페이스에도 구현이 가능함. (언리얼 내부 구현이 C++ 언어를 기반으로 하다 보니 그렇다. 하지만 C++와 마찬가지로 인터페이스에서의 구현은 지양해야 한다)
실습
예제 코드 상속 구조
예제 코드
MyGameInstance.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"
UCLASS()
class UNREALINTERFACE_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UMyGameInstance();
virtual void Init() override;
private:
UPROPERTY()
FString SchoolName;
};
MyGameInstance.cpp
#include "MyGameInstance.h"
#include "Student.h"
#include "Teacher.h"
#include "Staff.h"
UMyGameInstance::UMyGameInstance(){
SchoolName = TEXT("학교");
}
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("=========================================="));
TArray<UPerson*> Persons = { NewObject<UStudent>(), NewObject<UTeacher>(), NewObject<UStaff>() }; // Student, Teacher, Staff 하나씩 추가
for (const auto Person : Persons)
{
UE_LOG(LogTemp, Log, TEXT("구성원 이름: %s"), *Person->GetName());
}
UE_LOG(LogTemp, Log, TEXT("=========================================="));
}
LessonInterfac.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "LessonInterface.generated.h"
// 타입 정보를 저장하기 위함.
UINTERFACE(MinimalAPI)
class ULessonInterface : public UInterface
{
GENERATED_BODY()
};
class UEPARTONE_API ILessonInterface
{
GENERATED_BODY()
public:
virtual void DoLesson() = 0; // 순수 가상함수로 선언
};
Student.h
UCLASS()
class UEPARTONE_API UStudent : public UPerson, public ILessonInterface
{
GENERATED_BODY()
public:
UStudent();
virtual void DoLesson() override;
};
Student.cpp
#include "Student.h"
UStudent::UStudent(){
Name = TEXT("학생");
}
void UStudent::DoLesson(){
UE_LOG(LogTemp, Log, TEXT("%s 님은 공부합니다."), *Name);
}
Teacher도 Stduent과 같이 Interface 가상함수를 재정의한다.
Teacher.h
#pragma once
#include "CoreMinimal.h"
#include "Person.h"
#include "LessonInterface.h"
#include "Teacher.generated.h"
UCLASS()
class UNREALINTERFACE_API UTeacher : public UPerson, public ILessonInterface
{
GENERATED_BODY()
public:
UTeacher();
virtual void DoLesson() override;
};
Teacher.cpp
#include "Teacher.h"
UTeacher::UTeacher(){
Name = TEXT("이선생");
}
void UTeacher::DoLesson()
{
//ILessonInterface::DoLesson(); // 만약 인터페이스의 함수가 순수 가상함수가 아니고 구현부가 있으면 Super키워드가 아닌 Interface::함수로 위의 함수를 가져온다. 하.지.만. 인터페이스에서의 구현은 지.양.해야한다. 웬만하면 하지말자.
UE_LOG(LogTemp, Log, TEXT("%s님은 가르칩니다."), *Name);
}
정리
언리얼 C++ 인터페이스
- 클래스가 반드시 구현해야 하는 기능을 지정하는데 사용함.
- C++은 기본적으로 다중상속을 지원하나, 언리얼 C++의 인터페이스를 사용해 가급적 축소된 다중상속의 형태로 구현하는 것이 향후 유지보수에 도움된다.
- 언리얼 C++ 인터페이스는 두 개의 클래스를 생성한다. (U, I 형태 2개)
- 언리얼 C++ 인터페이스는 추상 타입으로 강제되지 않고, 내부에 기본 함수를 구현할 수 있다.
언리얼 C++ 인터페이스를 사용하면,
클래스가 수행해야 할 의무를 명시적으로 지정할 수 있어,
좋은 객체 설계를 만드는데 도움을 줄 수 있다.
더 알아보기
C++ 언어가 제공하는 다중 상속 기능에서 문제가 될 수 있는 부분
다이아몬드 상속 문제
이름 충돌(=모호한 이름):
- 문제: 다이아몬드 구조 - BaseClass, Derived Class x 2, DerivedDerived Class가 있는 상황에서 DerivedDerived Class에서 가상함수를 상속받아 재정의할 때 같은 이름의 함수 때문에 문제 발생.
- 해결방법 2가지:
- 1. dynamic_cast()로 객체를 명시적으로 업캐스팅해서 원하지 않는 버젼을 컴파일러가 볼 수 없게 가린다.
- 2. 스코프 지정 연산자(::)로 원하는 버젼을 구체적으로 지정한다.
모호한 베이스 클래스
- 가장 흔한 사례는 부모가 겹칠 때 발생한다.
전문가를 위한 C++ 책 p460 ~ 464 참고
Java와 C#과 같은 후발언어들은 왜 다중 상속을 사용하지 않고 인터페이스를 사용한 제한적 상속을 구현했을까?
객체지향 설계(OOP)에서 다중 상속은 지양해야된다. 중복 상속으로 인한 문제를 야기할 수 있기 때문이다.
Java와 C#의 인터페이스는 순수 가상함수로만 이루워져 구현부가 없어 온전히 추상 클래스의 역할을 수행한다. 그렇기에 인터페이스에서 선언된 함수들의 구현부에 대한 걱정이 없다.
C++는 이러한 개념이 대두되기 전에 탄생한 언어여서 Java, C#과 달리 인터페이스 전용 클래스가 없다. 그래서 인터페이스 클래스 생성 시 구현부를 정의하지 않는 방법으로 다중상속 관련 문제를 방지하는게 좋다.
'⭐ Unreal Engine > UE 개념정리 - 언리얼의 이해' 카테고리의 다른 글
[UE] 언리얼 C++ 설계 3 - 델리게이트 Delegate (0) | 2024.04.05 |
---|---|
[UE] 언리얼 C++ 설계 2 - 컴포지션 (0) | 2024.04.05 |
[UE] 언리얼 리플렉션 Unreal Reflection II (0) | 2024.03.21 |
[UE] 언리얼 리플렉션 Unreal Reflection I (0) | 2024.03.18 |
[UE] 언리얼 오브젝트 시스템 (0) | 2024.03.18 |
댓글
이 글 공유하기
다른 글
-
[UE] 언리얼 C++ 설계 3 - 델리게이트 Delegate
[UE] 언리얼 C++ 설계 3 - 델리게이트 Delegate
2024.04.05 -
[UE] 언리얼 C++ 설계 2 - 컴포지션
[UE] 언리얼 C++ 설계 2 - 컴포지션
2024.04.05 -
[UE] 언리얼 리플렉션 Unreal Reflection II
[UE] 언리얼 리플렉션 Unreal Reflection II
2024.03.21 -
[UE] 언리얼 리플렉션 Unreal Reflection I
[UE] 언리얼 리플렉션 Unreal Reflection I
2024.03.18