Unreal 게임 개발 과정 TIL

Today's I Learning

개인 공부

06.10 Unreal 키워드로 개념 정리) UPROPERTY / UObject

Sunlight__ 2026. 6. 10. 21:39

 

1. UPROPERTY

 : 언리얼 엔진에서 UPROPERTY는 C++ 클래스의 멤버 변수를 언리얼 엔진의 시스템(리플렉션 시스템)에 등록하는 매크로이다.

   C++ 변수 위에 UPROPERTY()를 붙이지 않으면, 그 변수는 일반적인 C++ 변수일 뿐이며 언리얼 엔진의 특수 기능들을 전혀 사용할 수 없다.

 

 - UPROPERTY가 제공하는 핵심 기능

 : UPROPERTY를 사용하면 언리얼 엔진이 다음과 같은 기능들을 자동으로 처리해 준다.

- 가비지 컬렉션 (메모리 관리)
 : 언리얼 엔진이 변수의 참조 상태를 추적한다.
   더 이상 사용되지 않는 객체(Object)를 자동으로 메모리에서 해제하여 메모리 누수를 방지한다.
 - 디테일 패널 노출
 : 에디터의 '디테일(Details)' 창에 변수를 표시하여, 개발자가 코드를 수정하지 않고도 에디터에서 직접 값을 변경할 수 있다.
 - 네트워크 복제 (Replication)
 : 멀티플레이어 게임을 만들 때, 서버의 변수 값이 변경되면 클라이언트를 자동으로 동기화되도록 설정할 수 있다.
 - 직렬화 (Serialization)
 : 게임을 저장하거나 불러올 때 변수의 값을 자동으로 파일에 저장하고 복구할 수 있게 한다.

 

 - 가장 자주 쓰이는 지정자 (Specifiers)

 : UPROPERTY() 괄호 안에는 다양한 지정자를 넣어 변수의 성격과 권한을 세부적으로 제어할 수 있다.

1) 에디터 노출 관련 (디테일 패널)

 : 에디터에서 변수를 어떻게 보여주고 수정하게 할지 결정한다.

- EditAnywhere
 : 에디터의 디테일 패널과 블루프린트 에셋 창 양쪽 모두에서 값을 수정할 수 있다.
- EditDefaultOnly
 : 블루프린트 에셋(원형) 창에서만 수정할 수 있고, 레벨에 배치된 개별 액터의 디테일 패널에서는 수정할 수 없다.
- EditInstanceOnly
 : 레벨에 배치된 개별 액터의 디테일 패널에서만 수정할 수 있고, 블루프린트 에셋 창에서는 수정할 수 없다.
- VisibileAnywhere / VisibleDefaultOnly / VisibleInstanceOnly
 : 위와 규칙은 같지만, 값을 수정할 수는 없고 디테일 패널에서 보기만 가능(읽기 전용)하게 만든다.

 

2) 블루프린트 연동 관련

 : 블루프린트 그래프에서 이 변수를 어떻게 다룰지 결정한다.

 - BlueprintReadOnly
 : 블루프린트에서 이 변수의 값을 가져올 수만 있다. (Get 노드만 사용 가능)
- BlueprintReadWrite
 : 블루프린트에서 이 변수의 값을 가져오거나 변경할 수 있다. (Get / Set 노드 모두 사용 가능)

 

3) 카테고리 지정

 - Category = "이름" 
 : 디테일 패널이나 블루프린트에서 변수들을 그룹화하여 보기 좋게 정리해준다.
   계층 구조가 필요하다면 Category = "Combat|Weapon" 처럼 폰트 ( | ) 기호를 사용할 수 있다.

 

 * UPROPERTY 사용시 주의할 점

1. 포인터 변수는 필수
 : 다른 UObject나 AActor를 가리키는 포인터 변수(AActor* MyTarget; 등)를 선언할 때는 반드시 위에 UPROPERTY()를 붙여주어야 한다.
   그렇지 않으면 가비지 컬렉터가 해당 객체를 메모리에서 지워버려, 나중에 댕글링 포인터(Dangling Pointer)로 인한 크래시가 발생할 수 있다.
2. 기본 자료형 초기화
 : C++ 특성상 int32, float, bool 같은 기본 자료형은 선언만 하면 쓰레기 값이 들어갈 수 있으므로, 생성자나 선언부에서 꼭 초기화를 해주는 것이 좋다. (예 : float Health = 100.0f;)

 

2. UObject

 : 언리얼 엔진에서 UObject는 엔진 프레임워크의 최상위 루트(Root) 기본 클래스이다.

   언리얼 엔진이 제공하는 거의 모든 핵심 기능(메모리 관리, 리플렉션, 블루프린트 연동 등)을 사용할 수 있는 '가장 원초적인 객체' 이다. (예 : 액터나 컴포넌트도 결국 UObject를 상속받아 만들어진 자식이다.)

 

 - UObject 계층 구조 이해하기

 : 언리얼 엔진의 오브젝트 구조는 거대한 피라미드 형태를 띠고 있다.

- Object
 : 최상위 부모, 데이터와 논리 구조를 가질 수 있지만, 월드(게임 화면)에 직접 배치할 수는 없다. (트랜스폼 값이 없다.)
- AActor
 : UObject를 상속받아 만들어진 클래스, 레벨(월드)에 배치할 수 있는 최소 단위이다.
   위치 값을 가지며, 게임 안에서 눈에 보이거나 물리적인 상호작용을 할 수 있다.
- UActorComponent
 : 액터에 부착되어 특정 기능(외형 렌더링, 무브먼트, 오디오 재생 등)을 담당하는 부품이다.
   이것 역시 UObject를 상속받았다.

 

 - UObject가 제공하는 핵심 시스템 (존재 이유)

 : 표준 C++ 클래스를 쓰지 않고 굳이 UObject를 상속받아 사용하는 이유는 언리얼 엔진이 자랑하는 오브젝트 처리 시스템을 그대로 이용할 수 있기 때문이다.

1. 가비지 컬렉션 (Garbage  Collection)
2. 리플렉션 시스템 (Reflection System)
3. CDO (Class Default Object)
 : 언리얼 엔진은 게임이 실행 될 때 각 UObject 클래스마다 단 하나의 '기본 객체(CDO)'를 미리 생성해 둔다.
   - 이 클래스로 만들어질 객체들의 '원본 템플릿' 역할을 한다.
   - 새로운 객체가 생성 될 때 처음부터 무겁게 초기화 하는 것이 아니라, 이 CDO의 값을 그대로 복사해서 생성하므로 속도가 매우 빠르다.

 

 - C++ 코드에서 UObject 다루기

 : UObject는 일반적인 C++ 클래스와 생성 및 관리 방식이 완전히 다르다.

1. 클래스 선언 (접두사 'U')
 : 언리얼 엔진 규칙에 따라 UObject를 직접 상속받는 클래스는 이름 앞에 접두사 U를 붙여야 한다. (액터는 A)
2. 객체 생성 (NewObject)
 : UObject 기반 클래스는 절대 C++ 표준 방식인 new MyClass() 로 생성하면 안된다.
   엔진 시스템에 등록하며 생성해야 하므로 NewObject<T>() 함수를 사용해야 한다.
3. 객체 소멸 (메모리 해제)
 : delete NewData; 역시 절대 사용 금지이다.
   - UPROPERTY()로 지정된 변수에서 해당 객체의 참조를 끊거나 nullptr을 대입하면, 다음 가비지 컬렉션 주기에 자동으로 메모리가 해제된다.
   - 명시적으로 즉시 지우고 싶다면 NewData->MarkAsGarbage(); 를 호출하여 가비지 컬렉터에게 지워달라고 마킹할 수 있다.

 

 * 요약

- 언제 UObject를 직접 상속받아 사용 하는가?
 : 게임에 배치될 필요는 없지만(즉, 위치나 외형이 필요 없음), 언리얼의 메모리 관리와 블루프린트 연동이 필요한 순수한 데이터나 논리 시스템을 만들 때 사용한다.
 예) 게임의 세이브 데이터 구조체, 아이템 인벤토리의 데이터 시스템, 퀘스트 정보 저장소, 복잡한 스킬의 효과(Buff/Debuff) 게산 로직 등.