유지보수와 유연함, 확장성 향상을 위한 객체지향 프로그래밍 원칙(SOLID)

S(Single Responsibility) : 하나의 클래스는 하나의 책임만 가져야 함

O(Open/Closed) : 클래스 설계를 변경하지 않고 동작을 확장할 수 있어야 함

L(Liskov Subtitution) : 자식 클래스는 부모 클래스를 대체 사용할 수 있어야 함

I(Interface Segregation) : 작고 명확한 인터페이스들로 분리해 관리해야 함

D(Dependency Inversion) : 구체적인 클래스가 아닌 인터페이스에 의존해야 함

 

C#, JAVA부터 보완한 새로운 기능

Interface : 객체 설계의 틀을 제공하는 추상 클래스

Reflection : 런타임에서 객체의 구조를 파악하고 객체에 메타 데이터를 부여

Delegate : 프로그램에서 발생한 이벤트를 다수의 객체에 효과적으로 전달하는데 활용

 

성능과 유지보수를 모두 갖기 위해 언리얼은 C++에 매크로 기능을 활용하여 모던 객체 지향 언어들이 가지고 있는 기능을 사용

C++ 오브젝트 : 저수준의 빠른 처리를 위한 기능 구현에 사용

언리얼 오브젝트 : 콘텐츠 제작에 관련된 복잡한 설계 구현에 사용

 

언리얼 오브젝트

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/objects-in-unreal-engine?application_version=5.1

 

클래스가 언리얼 오브젝트인지 지정하기 위해 UCLASS 매크로 사용
언리얼 오브젝트 생성할 때 NewObject<class>() 사용

 

특징

클래스 기본 객체(CDO) : 클래스의 기본 값과 타입 정보 제공

리플렉션 : 런타임에서 클래스 정보 참조 기능

가비지 컬렉션 : 자동 메모리 관리

직렬화 : 객체 정보를 바이트 스트림으로 저장, 전송, 로드하는 기능

네트워크 리플리케이션

 

언리얼 헤더 툴

ProjectName_API : 다른 DLL(모듈)에서 언리얼 오브젝트 클래스를 사용할 수 있도록 하는 키워드

GENERATED_BODY : 객체지향 설계를 위해서 제공되는 여러가지 기능들을 언리얼 엔진이 자동으로 생성

 

언리얼 헤더 툴에 의해서 언리얼 오브젝트 기능을 제공하기 위해 컴파일 과정에서 매크로들을 참조해 genreated.h를 자동으로 생성하고 최종 빌드 됨

 

언리얼 오브젝트 리플렉션(프로퍼티) 시스템

https://www.unrealengine.com/ko/blog/unreal-property-system-reflection

 

런타임에 자기 자신을 조사하는 기능을 지원

C++은 리플렉션을 지원하지 않아 언리얼에서 Unreal Build Tool(UBT)와 Unreal Header Tool(UHT)을 사용하여 자체적으로 구축

언리얼 오브젝트에만 적용되며 옵션이므로 사용할지 안할지 선택할 수 있음

 

"FileName.genterated.h"를 include하고 UENUM(), UCLASS(), USTRUCT(), UFUNCTION(), UPROPERTY() 매크로를 사용하여 리플렉션으로 등록

언리얼 오브젝트를 선언할 때 항상 generated.h 파일이 가장 마지막에 include 해야 함

cpp 파일에서 언리얼 오브젝트가 선언된 헤더 include 순서가 가장 위에 있어야 함

 

함수 본문에 GENERATED_BODY() 매크로가 필수적

매크로 안에 메타 데이터를 지정하여 게임 컨텐츠를 제작할 때 에디터와 연동하여 여러가지 기능을 만들 수 있음

 

멤버를 UPROPERTY로 선언하면 언리얼에서 자동으로 초기화 및 메모리 관리(가비지 컬렉션), 선언하지 않았다면 직접 메모리를 관리해야 함

 

언리얼 오브젝트 계층 구조

위로 갈수록 상위 클래스

UStruct는 컴포지션으로 UField를 소유

 

언리얼 오브젝트 클래스는 UTypeName::StaticClass()나 Instance->GetClass()를 사용하여 UHT가 자동으로 생성한 리플렉션 정보를 보관한 객체에 접근할 수 있음

for( TFieldIterator<UProperty> PropIt(GetClass()); PropIt; ++PropIt)
{
	...
}

 

각 유형에 플래그나 메타 데이터 등이 있어서 필요한 것만 필터링해서 얻을 수 있음

런타임에 어떤 UClass인지 알 수 있으며 형변환이 가능해짐

 

UPROPERTY를 사용해야 하는 경우

메모리를 GC를 통해 관리해야 하는 변수

블루프린트나 리플렉션을 통해서 참조하는 변수

// Outer를 설정하여 컴포지션 관계 설정 및 객체의 생명 주기를 Outer와 동일하게
CourseInfo = NewObject<UCourseInfo>(this);

// 객체의 생명 주기를 유지할 필요가 없기 때문에 Outer 설정 X
UStudent* Student1 = NewObject<UStudent>();
// AddUObject를 사용하여 오브젝트의 멤버 함수를 바인딩
CourseInfo->OnChanged.AddUObject(Student1, &UStudent::GetNotification);

 

예제를 통해 실습한 결과 Delegate 호출할 때 바인딩이 늦게 된 순서대로 호출되는 것으로 보임

 

언리얼 오브젝트 처리

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/unreal-object-handling-in-unreal-engine?application_version=5.1

 

UPROPERTY로 선언한 멤버는 자동으로 0으로 초기화됨

 

언리얼 오브젝트 객체의 UPROPERTY로 지정한 것만 사용자가 지정한 포맷에 맞게 I/O해줌

    - Serialization, 네트워크 통신에 사용

 

CDO(Class Default Object)

언리얼 객체가 가진 기본 값을 보관하는 템플릿 객체

한 클래스로부터 다수의 물체를 생성할 때 같은 기본 값을 조정하는 데 사용됨

GetDefaultObject()를 통해 얻을 수 있음

엔진 초기화 이후 사용할 수 있으므로 CDO의 기본값을 변경하는 경우에 에디터를 끄고 다시 컴파일해야 함

 

형변환할 때 Cast를 사용하여 실패하면 nullptr을 보장해주기 때문에 안전하게 형변환 가능

 

리플렉션을 활용하면 접근 지시자와 무관하게 값을 설정할 수 있으므로 상황에 따라서는 필요할 수도 있음

UStudent* Student = NewObject<UStudent>();
UTeacher* Teacher = NewObject<UTeacher>();

UTeacher::StaticClass()->FindPropertyByName(TEXT("Name")); // 프로퍼티 정보를 가져올 수 있음
if (NameProp) 
{
    NameProp->GetValue_InContainer(Teacher, &CurrentTeacherName); // 프로퍼티의 값을 가져옴
    NameProp->SetValue_InContainer(Teacher, &NewTeacherName); // 프로퍼티의 값을 변경함
}

UFunction* DoLessonFunc = Teacher->GetClass()->FindFunctionByName(TEXT("DoLesson")); // 함수에 대한 객체 얻음
if(DoLessonFunc)
{
    Teacher->ProcessEvent(DoLessonFunc, nullptr); // 리플렉션을 활용한 함수 실행
}

+ Recent posts

목차