이번 포스트에서는 언리얼 C++에서 사용하는 설계 기법 중 하나인 컴포지션에 대해 알려드리겠습니다.
거두절미하고 바로 시작하겠습니다.
1) Is - A VS Has - A
우리가 클래스를 사용하는 이유는 보다 확실한 객체 지향 설계를 이뤄내기 위해 사용합니다.
그런 점에서 Is-A를 통한 상속 관계의 파악은 객체 지향 설계에서 매우 중요합니다.
하지만, Is-A관계만으로는 클래스의 설계와 유지보수가 매우 힘듭니다.
아주 간단한 개념으로 생각해보겠습니다.
우리가 RPG 게임을 만든다고 가정할 때, 플레이어는 직업을 선택해야 합니다.
직업간의 관계는 상속을 통한 클래스들로 만들어 나갈 수 있습니다.
그렇지만, 직업별로 사용하는 무기나 스킬은 어떻게 구성되야 할까요?
분명히 각 직업에 알맞은 무기나 스킬이 존재하고 플레이어는 그것들을 사용해야만 합니다.
다시 말해서 Has-A를 배제한 상태로는 효율적이고 완전한 객체 지향 설계를 이뤄낼 수 없습니다.
이런 상황에서 Has-A 관계를 구현하는 컴포지션은 큰 도움이 됩니다.
컴포지션을 통해 우리는 복합적인 기능을 가진 클래스를 효과적으로 설계할 수 있습니다.
2) 컴포지션의 구현
컴포지션을 구현하는 것에는 두 가지 방법이 존재합니다.
2 - 1) 필수적 포함
필수적 포함은 CDO(Class Default Object)를 통해 미리 언리얼 오브젝트를 생성해 조합하는 것입니다.
2 - 2) 선택적 조합
선택적 조합은 필수적 조합과는 다르게 CDO에는 빈 포인터만 넣고 런타임에서 언리얼 오브젝트를 생성하여 조합합니다.
언리얼 오브젝트의 생성 시점에 따라 2가지 방법으로 나눌 수 있고, 생성할 때 컴포지션의 정보를 구축하시면 됩니다.
그리고 이러한 컴포지션과 컴포지션을 소유한 소유자의 관계에서
내가 소유한 언리얼 오브젝트를 SubObject라고 하고,
나를 소유한 언리얼 오브젝트를 Outer라고 지칭합니다.
3) 컴포지션의 예제
간단한 예제를 통해 컴포지션의 구현에 대해 알아보겠습니다.
구현되는 Weapon 컴포지션은 필수적 포함으로 사용될 것입니다.
설계는 기본적으로 헤더 파일과 cpp 파일을 분리하는 것이 좋지만,
설계의 편의상 헤더파일에 몰아넣어 작업했다는 것을 유의해주시길 바랍니다.
제일 먼저 Weapon 클래스입니다.
구성은 위와 같습니다.
다음은 UnDefinedPlayer 클래스입니다.
생성자에서 CreateDefaultSubObject()를 이용하여 CDO에서 컴포지션을 필수포함하도록 설계하였습니다.
다음으로 각각 Archer, Warlock, Warrior입니다.
MyGameInstance라는 GameInstance 클래스를 하나 만들어,
게임 내 요소가 작동시키도록 하였습니다.
Players는 Init함수에서 초기화할것입니다.
Init 함수는 위와 같이 설계하여 최종적으로
플레이어의 이름과 플레이어가 소유한 무기의 메타 데이터를 출력하도록 설계하였습니다.
실행결과는 다음과 같습니다.
4) 마치며
지금까지 언리얼 엔진에서 컴포지션을 사용하는 방법에 대해서 알아보았습니다.
컴포지션을 활용함으로써 복잡한 클래스를 효율적으로 설계할 수 있을 것입니다.
'언리얼 > 언리얼 C++' 카테고리의 다른 글
언리얼 C++ - 자료 구조 ~ TArray (0) | 2024.07.20 |
---|---|
언리얼 C++ - 델리게이트 (0) | 2024.07.13 |
언리얼 C++ - 인터페이스 (0) | 2024.06.30 |
언리얼 C++ - 언리얼 리플렉션 시스템의 활용 (0) | 2024.06.30 |
언리얼 C++ - 언리얼 오브젝트 리플렉션 시스템 ~ CDO (0) | 2024.06.24 |