2025-03-25 모의면접

별빛에소원을·2025년 3월 25일

TeamSparta-Unreal1기-TIL

목록 보기
63/90
post-thumbnail

면접자료 준비

예상질문

제일 재밌게 한 게임 : 리그오브레전드.

영감을 준 점 :
트리플 A와 같은 몰입형 게임과 플레이어가 컨텐츠가 되어 유통이 되는 게임이 인기를 게임을 플레이하는데 있어서
꼭 좋은 경험이 좋은 게임의 판단요소가 아니라는 점을 알게 해 주었고
밸런스와 다양한 시도를 가능하게 하는 플레이어의 경험들이 게임의 완성도를 높혀준다는 것을 느끼게 해주었습니다.

  • Actor와 Component의 차이
    Actor는 레벨에 배치될 수 있는 Object를 이야기하고, Component는 Actor에
    종속되어 기능을 수행하는 모듈을 이야기합니다.

Actor는 RootComponent를 개념을 가져서 가지고 있는 Component를 계층구조로 보유합니다.

언리얼 컴포넌트 계층구조
UObject
├── AActor
│ ├── AMyChampion
│ └── ATibbers
└── UActorComponent
├── UHealthComponent
└── USceneComponent
└── UPrimitiveComponent
└── USkeletalMeshComponent

컴포넌트는 크게 3가지로 분류가 되는데 로직, 트랜스폼, 렌더링에 대한 기능별로 분류가 가능합니다.
로직에 대한 부분은 ActorComponet
크기, 이동, 회전같은 값을 가지는 SceneComponent
렌더링에 관련된 PrimitiveComponent

언리얼의 객체는 모두 UObject를 상속받아 직렬화를통해 가비지컬렉션의 관리받을 수 있습니다.


직렬화 !
런타임에 생성한 클래스를 활용하기 위한 기술이며 언리얼에서는 UHT가 리플렉션 데이터(타입, 값)를 생성하고
Engine에서 생성한 CDO를 기반으로 디스크 혹은 메모리에 캐싱이 되었다가 에디터에서 로드될때 캐싱된 데이터를 기반으로 복원합니다.


추가적인 깊이
네트워크 직렬화: UPROPERTY(Replicated)를 추가하면 변수가 서버-클라이언트 간 동기화됩니다. 예를 들어, MyFloat가 바뀌면 클라이언트에도 반영되죠.
블루프린트와의 연계: UPROPERTY(BlueprintReadWrite)로 설정하면 블루프린트 스크립트에서도 이 값을 조작할 수 있어요. 직렬화된 데이터가 블루프린트로도 전달됩니다.
가비지 컬렉션과의 관계: UPROPERTY가 없으면 참조 추적이 안 돼서 메모리 누수가 생길 수 있어요. 직렬화와 GC는 긴밀히 연결되어 있죠.


GC의 기능
직렬화된 데이터가 더이상 참조되지 않을때 메모리에서 해지시켜주는 기능입니다.


std::자료구조
Vector : 연속된 메모리구조
List : 각 노드에 대한 포인터를 공유하는 형태 메모리가 연속되지 않음
Map : 키를 기준으로 자동정렬 기능이 포함된 이진탐색트리
std::unordered_map : 키를 해쉬함수로 변환해 버킷에 저장하는 구조 충돌은 list로 해결

해쉬테이블은 공간복잡도가 크다는 거 빼고는 단점이 없다.

list와 vector의 장점을 이용해 만듬.
구현 : 해쉬 함수를 이용해 중복되지 않는 키값을 리스트형태로 충돌을 피하고, 연속된 메모리(버킷)에 저장을 하여 탐색을 쉽게 만든 구조


  1. 블루프린트와 C++의 기본 차이점
    블루프린트 : 빠르게 피드백이 가능하여 프로토타입에 대한 결과를 만들기 좋으나 실행속도가 느리며 엔진에서 제공하지 않는 기능을 확장해서 만들 수 없다.
    C++ : 진입장벽이 높지만 복잡한 로직을 최적화가 가능하고 빌드가 느리다.

UnrealEngine의 Level과 Map의 차이점
Level은 게임에서 플레이하는 공간.
Map은 그 공간을 표현하는 매체


UObject의 생명 주기
생성:
NewObject()나 CreateDefaultSubobject()로 생성.
사용:
Actor에 붙거나, 변수로 참조.
파괴:
참조가 없어지면 GC가 정리.
MarkPendingKill()로 강제 제거 가능.


(1) 리플렉션의 힘
런타임에 변수 값을 동적으로 변경 가능.
예: UProperty* Prop = MyObject->GetClass()->FindPropertyByName("MyValue");
(2) GC의 함정
UPROPERTY 없는 포인터는 GC가 인식 못해 누수 발생.
너무 많은 객체가 참조되면 GC 부하 증가.
(3) 성능 고려
UObject는 강력하지만, 메모리와 초기화 비용이 큼.
단순 데이터는 struct나 Plain Old Data(POD)로 대체 가능.
(4) 블루프린트와의 연결
UObject 기반 클래스는 블루프린트로 확장 가능.
예: AMyChampion을 BP로 상속해 스킬 추가.


GC 정보
(1) 기본 원리
참조 추적: GC는 "루트 객체"에서 시작해 도달 가능한 모든 객체를 추적.
Mark-and-Sweep:
Mark: 도달 가능한 객체에 표시.
Sweep: 표시되지 않은 객체를 메모리에서 해제.
주기: 설정에 따라 주기적으로 실행 (기본: 매 프레임마다 체크 가능, GarbageCollectionTime 조정).
(2) 동작 과정
루트 집합 정의:
UWorld, ULevel, AGameMode 등 게임의 핵심 객체가 루트.
UPROPERTY()로 선언된 포인터도 루트에서 간접 참조.
예: AMyChampion의 HealthComponent가 UPROPERTY라면 GC가 추적.
참조 그래프 탐색:
루트에서 시작해 모든 UObject 포인터를 따라감.
TArray, TMap 같은 컨테이너도 내부적으로 UPROPERTY 요소를 추적.
Mark 단계:
도달 가능한 객체에 플래그 설정.
UObject::IsRooted()로 강제로 루트로 지정 가능.
Sweep 단계:
표시 안 된 객체를 찾아 메모리 해제.
ConditionalBeginDestroy() 호출 → 소멸자 실행.
(3) 세부 메커니즘
클러스터링:
서로 참조하는 객체를 그룹화해 성능 최적화.
예: AMyChampion과 그 컴포넌트들이 하나의 클러스터.
Pending Kill:
MarkPendingKill()로 객체를 즉시 GC 대상으로 표시.
다음 GC 사이클에서 정리.
시간 분할:
GC가 한 번에 너무 오래 걸리지 않게 작업을 프레임 단위로 나눔 (Incremental GC).


UPROPERTY()
AMyClass* ReferencedObject;

AMyClass* UnreferencedObject = NewObject<AMyClass>();

에디터에서 "Play" 요청 → 엔진 초기화.
게임 인스턴스와 월드 생성.
레벨 로드 및 객체 초기화.
게임 모드와 플레이어 설정.
Actor 초기화 후 BeginPlay() 호출.


게임 모드 초기화: 지연 초기화, 비동기 스폰, 레플리케이션 최소화로 시작 속도 개선.
컴포넌트 등록: 등록 지연, 렌더링/물리 비활성화, 풀링으로 런타임 효율성 극대화.


동기화 주의할점
서버가 변경해야할 값을 클라이언트가 변경하면 안된다.


면접결과

많이 말려서 준비한 답변을 제대로 하지 못했다.
긴장을 많이 해서 질문을 제대로 이해하지도 못했다 ㅜㅜ
너무 긴장하지 말고 여유를 가져보라는 조언을 받았다.
앞으로가 걱정이 되지만 경험해보기 잘한거 같다.

profile
취미로 게임하는사람

0개의 댓글