컴포지션은 객체 지향 설계에서 Has-A 관계를 구현하는 설계 방법
컴포지션 구현 방법
- 한 언리얼 오브젝트에는 항상 클래스 기본 오브젝트 CDO가 있다.
- 언리얼 오브젝트간의 컴포지션은 어떻게 구현할 것인가?
-
- CDO에 미리 언리얼 오브젝트를 생성해 조합한다. (필수적 조합)
-
- CDO에 빈 포인터만 넣고 런타임에서 언리얼 오브젝트를 생성해 조합한다. (선택적 조합)
- 언리얼 오브젝트를 생성할 때 컴포지션 정보를 구축할 수 있다.
- 내가 소유한 언리얼 오브젝트를 Subobject라고 한다.
- 나를 소유한 언리얼 오브젝트를 Outer라고 한다.
코드
Card.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| UENUM()
enum class ECardType : uint8
{
Student = 1 UMETA(DisplayName = "For Student"),
Teacher UMETA(DisplayName = "For Teacher"),
Staff UMETA(DisplayName = "For Staff"),
Invalid
};
UCLASS()
class UNREALCOMPOSITION_API UCard : public UObject
{
GENERATED_BODY()
public:
UCard();
ECardType GetCardType() const { return CardType; }
void SetCardType(ECardType InCardType) { CardType = InCardType; }
private:
UPROPERTY()
ECardType CardType;
UPROPERTY()
uint32 Id;
};
|
Card.cpp
1
2
3
4
5
6
7
| #include "Card.h"
UCard::UCard()
{
CardType = ECardType::Invalid;
Id = 0;
}
|
LessonInterface.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // This class does not need to be modified.
UINTERFACE(MinimalAPI)
class ULessonInterface : public UInterface
{
GENERATED_BODY()
};
class UNREALCOMPOSITION_API ILessonInterface
{
GENERATED_BODY()
public:
virtual void DoLesson()
{
// 언리얼 인터페이스는 구현도 가능함! 비워두지 않아도 됨
UE_LOG(LogTemp, Log, TEXT("수업에 입장합니다."));
}
};
|
LessonInterface.cpp 는 수정 안 했음.
인터페이스 U 붙은건 안건드린다! 링크
Person.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| UCLASS()
class UNREALCOMPOSITION_API UPerson : public UObject
{
GENERATED_BODY()
public:
UPerson();
// FORCEINLINE : 인라인 지정
FORCEINLINE const FString& GetName() const { return Name; }
FORCEINLINE void SetName(const FString& InName) { Name = InName; }
FORCEINLINE class UCard* GetCard() const { return Card; }
FORCEINLINE void SetCard(class UCard* InCard) { Card = InCard; }
protected:
UPROPERTY()
FString Name;
// 전방선언. 헤더를 포함하지 않고도 포인터 선언 가능.
// 헤더를 포함하지 않아 구체적인 구현부는 몰라도
// 포인터 크기는 알 수 있다.
// 보통 오브젝트는 포인터로 관리하기 때문에...
// 이렇게 전방선언 하여 의존성을 낮출 수 있다.
/*UPROPERTY()
class UCard* Card;*/
// 그런데 언리얼5 버전부턴 원시 포인터를 다르게 쓰라고 에픽에서 제안함.
UPROPERTY()
TObjectPtr<class UCard> Card;
};
|
전방선언 및 TObjectPtr
TObjectPtr<class UCard> Card;
Person.cpp
1
2
3
4
5
6
7
8
| #include "Person.h"
#include "Card.h"
UPerson::UPerson()
{
Name = TEXT("홍길동");
Card = CreateDefaultSubobject<UCard>(TEXT("NAME_Card")); // FName
}
|
Teacher.h
1
2
3
4
5
6
7
8
9
10
11
12
13
| #include "Person.h"
#include "LessonInterface.h"
UCLASS()
class UNREALCOMPOSITION_API UTeacher : public UPerson, public ILessonInterface
{
GENERATED_BODY()
public:
UTeacher();
virtual void DoLesson() override;
};
|
Teacher.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #include "Teacher.h"
#include "Card.h"
UTeacher::UTeacher()
{
Name = TEXT("이선생");
Card->SetCardType(ECardType::Teacher);
}
void UTeacher::DoLesson()
{
ILessonInterface::DoLesson();
UE_LOG(LogTemp, Log, TEXT("%s님은 가르칩니다."), *Name);
}
|
Student.h
1
2
3
4
5
6
7
8
9
10
11
12
13
| #include "Person.h"
#include "LessonInterface.h"
UCLASS()
class UNREALCOMPOSITION_API UStudent : public UPerson, public ILessonInterface
{
GENERATED_BODY()
public:
UStudent();
virtual void DoLesson() override;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include "Student.h"
#include "Card.h"
UStudent::UStudent()
{
Name = TEXT("이학생");
Card->SetCardType(ECardType::Student);
}
void UStudent::DoLesson()
{
// 인터페이스에서 구현한 걸 출력하고 싶다면
// Super::DoLesson(); 떠올렸겠지만 아님...
// 학생의 부모는 Person임. LessonInterface가 아니라.
// 이렇게 작성
ILessonInterface::DoLesson();
UE_LOG(LogTemp, Log, TEXT("%s님은 공부합니다."), *Name);
}
|
인터페이스에서 작성된 함수를 호출하는 법
ILessonInterface::DoLesson();
Staff.h
1
2
3
4
5
6
7
8
9
| #include "Person.h"
UCLASS()
class UNREALCOMPOSITION_API UStaff : public UPerson
{
GENERATED_BODY()
public:
UStaff();
};
|
Staff.cpp
1
2
3
4
5
6
7
8
| #include "Staff.h"
#include "Card.h"
UStaff::UStaff()
{
Name = TEXT("이직원");
Card->SetCardType(ECardType::Staff);
}
|
언리얼5에서 오브젝트 포인터 가이드
언리얼5 마이그레이션 문서
TObjectPtr 로 포인터를 감싸라고 한다.
댓글남기기