2026-04-07 TIL (31일차)
Unreal
챕터1
• World vs Local
World 좌표계
- 개념: 게임 레벨의 정중앙
(0, 0, 0)을 기준으로 한 절대적인 위치입니다.
World가 움직였다는 것의 의미
부모 액터를 잡고 월드에서 이동시키는 상황입니다.
| 구분 | 변화 여부 | 설명 |
|---|---|---|
| 부모의 월드 좌표 | 변함 | 지도상 위치가 바뀜 |
| 자식의 월드 좌표 | 변함 | 부모한테 매달려 있으니 지도상 위치가 같이 바뀜 |
| 자식의 로컬 좌표 | 그대로 | 부모라는 안에서 자식의 고유한 위치(거리)는 그대로임 |
Local 좌표계
- 개념: 나를 감싸고 있는 부모(또는 나 자신)를 기준으로 한 상대적인 위치입니다.
Local이 움직였다는 것의 의미
부모는 가만히 두고 자식 컴포넌트만 이동시키는 상황입니다. 자식을 움직이는 것은 부모에게 영향을 주지 않습니다.
| 구분 | 변화 여부 | 설명 |
|---|---|---|
| 부모의 월드 좌표 | 그대로 | 자식이 움직인다고 부모가 따라가지 않음 |
| 자식의 월드 좌표 | 변함 | 지도상에서의 최종 위치는 변함 |
| 자식의 로컬 좌표 | 변함 | 부모와의 거리가 멀어지거나 가까워짐 |
개념 정리
- World 좌표계는 GPS다: 게임 세상 전체를 관리합니다. 부모가 움직이면 자식의 GPS 값도 자동으로 바뀝니다.
- Local 좌표계는 줄자다: 부모와 나 사이의 거리입니다. 부모와 내가 같이 이동하면 줄자의 길이는 변하지 않습니다.
- 상속의 원리: 부모는 자식을 ‘끌고 다닐 권한’이 있지만, 자식은 부모를 ‘움직일 권한’이 없습니다.
언리얼 C++ 사용법
- 캐릭터 전체를 다른 곳으로 순간이동 시키고 싶다? -> 부모의 World Location 수정 (
SetActorLocation)- 캐릭터는 가만히 있고 손에 든 총만 살짝 위로 올리고 싶다? -> 자식의 Local Location 수정 (
SetRelativeLocation)
• FRotator와 FQuat (짐벌락 해결법)
FRotator(Pitch, Yaw, Roll)
| 명칭 | 축 (Axis) | 회전 방향 | 비유 |
|---|---|---|---|
| Pitch | Y축 | 위 / 아래 | 비행기가 이륙하거나 착륙할 때 코를 드는 동작 |
| Yaw | Z축 | 왼쪽 / 오른쪽 | 자동차가 핸들을 꺾어 좌회전, 우회전하는 동작 |
| Roll | X축 | 좌우 기울기 | 비행기가 비행 중 날개를 좌우로 까딱이는 동작 |
짐벌락(Gimbal Lock) 문제가 생기는 이유
짐벌락은 “세 개의 축 중 두 개의 축이 겹쳐지면서 한 방향의 회전 자유도(Degree of Freedom)를 잃어버리는 현상”
- 발생 원리:
FRotator는 3차원 회전을 한 번에 계산하지 않고,Yaw ➡️ Pitch ➡️ Roll순서대로 하나씩 차례대로 적용합니다. - 치명적인 상황: 먼저 Yaw로 좌우 방향을 잡은 뒤, Pitch를 이용해 정확히 위로 90도(수직) 꺾어버린다고 가정해 봅시다.
- 자유도 상실: 고개를 완전히 위로 든 상태에서는, 좌우로 도는 것(Yaw)과 양옆으로 비트는 것(Roll)이 완전히 동일한 회전축을 공유하게 됩니다. 즉, 3차원 공간에서 3개의 축이 필요한데 2개의 축이 하나로 합쳐져 버려서, 특정 방향으로는 아무리 수치를 바꿔도 물체가 회전하지 못하는 잠금 상태에 빠집니다.
- 결과: 이 상태에서 애니메이션이나 회전 연산을 주면 오브젝트가 기괴하게 비틀거리거나 순간적으로 화면에서 툭 끊기는 에러가 발생합니다.
해결법: 쿼터니언 (FQuat)
짐벌락을 해결하기 위해 3D 그래픽스에서 도입한 4차원 복소수 체계가 쿼터니언입니다.
원리
오일러 각도(FRotator)가 X축으로 몇 도, 다음엔 Y축으로 몇 도 순차적으로 돌리는 방식이라면, 쿼터니언은 임의의 막대기(Vector 축) 하나를 꽂고, 그 막대기를 기준으로 한꺼번에 회전시키는 방식입니다.
순서대로 축을 계산하지 않고 한 방에 회전해 버리기 때문에, 축이 겹치는 현상이 발생하지 않습니다.
- 차원: 3개의 값 대신 4개의 값($x, y, z, w$)을 사용합니다.
- 수식 형태: \(q = w + xi + yj + zk\) (여기서 $w$는 회전량(스칼라), $x, y, z$는 회전축의 벡터를 나타냅니다.)
쿼터니언 장점
- 짐벌락 해결: 축이 겹칠 일이 아예 없습니다.
- 부드러운 보간 (Slerp):
FRotator로 시작점과 끝점의 회전을 보간(Lerp)하면 중간에 숫자가 튀어 빙글 도는 현상이 생길 수 있지만, 쿼터니언은 구면 선형 보간(SLERP)을 통해 두 회전값 사이의 짧고 부드러운 경로를 찾아 매끄럽게 회전합니다. - 연산 성능 최적화: 복잡한 행렬 곱셈 연산보다 계산 속도가 빠르고 메모리도 적게 차지합니다.
• FTransform
FTransform 변수명(Rotation, NewLocation, NewScale); -> 순서로 들어간다.
• FMath::IsNearlyZero
== 0 (부동소수점 오차)
컴퓨터는 소수점을 계산하고 저장할 때 구조적인 한계로 인해 미세한 오차를 발생시킵니다.
컴퓨터 메모리 내부에서는 속도가 딱 0.0f가 아니라 0.0000014f나 -0.0000001f 같은 미세한 찌꺼기 값으로 남아있을 확률이 매우 높습니다.
이때 if (Speed == 0.0f)라고 검사하면 컴퓨터는 0이 아니라고 판단해 버립니다.
FMath::IsNearlyZero의 역할, 매개변수**
이 함수는 0에 가깝다면 0으로 인정해주는 함수입니다.
- 함수 원형:
FMath::IsNearlyZero(float Value, float ErrorTolerance = UE_KINDA_SMALL_NUMBER)Value: 0인지 검사하고 싶은 대상 숫자입니다.ErrorTolerance(오차 허용 범위): “어디까지를 0으로 인정할 것인가?”에 대한 한계치입니다.- 특징: 허용 범위의 기본값이
UE_KINDA_SMALL_NUMBER($10^{-4}$, 즉0.0001)로 세팅되어 있기 때문에, 대부분의 경우 두 번째 매개변수는 생략하고 사용해도 완벽하게 작동합니다.
• UCLASS
블루프린트 및 에디터 노출 기본형
가장 기본적으로 많이 사용하는 형태입니다.
UCLASS(Blueprintable, BlueprintType, ClassGroup=(GroupName))
Blueprintable- 의미: 이 C++ 클래스를 부모로 삼아 에디터에서 새로운 블루프린트를 생성할 수 있게 허용합니다.
BlueprintType- 의미: 이 클래스를 다른 블루프린트에서 ‘변수 타입’으로 지정해서 사용할 수 있게 허용합니다. (예: 배열의 타입으로 지정 가능)
ClassGroup=(GroupName)- 의미: 에디터의 액터 배치 창이나 클래스 뷰어에서 클래스를 찾을 때, 괄호 안에 적은 그룹명(예: Custom, Player 등)으로 묶어서 검색할 수 있게 해줍니다.
ClassGroup=(GroupName) -> 에디터 디테일 패널 제어형
기획자나 다른 작업자가 건드리면 안 되는 속성을 숨기거나 다시 켤 때 사용합니다.
UCLASS(HideCategories=(Category1), ShowCategories=(Category2))
HideCategories=(Category1)- 의미: 언리얼 에디터의 디테일 패널에서 괄호 안에 적은 특정 카테고리(예: Rendering, Collision)를 아예 숨겨버립니다. *
ShowCategories=(Category2) - 의미: 부모 클래스에서 숨김 처리해 둔 카테고리가 있다면, 내 클래스에서는 다시 에디터 화면에 보이도록 살려냅니다.
- 의미: 언리얼 에디터의 디테일 패널에서 괄호 안에 적은 특정 카테고리(예: Rendering, Collision)를 아예 숨겨버립니다. *
• UPROPERTY()
UPROPERTY() 매크로는 C++에서 선언한 멤버 변수를 언리얼 엔진의 리플렉션 시스템(메모리 관리, 에디터 노출, 블루프린트 연동 등)에 등록하는 역할을 합니다.
빈 UPROPERTY()의 의미
“가비지 컬렉터이 선언된 변수 메모리 관리해줌”
괄호 안에 아무것도 적지 않으면 에디터나 블루프린트 화면에는 나타나지 않습니다. 하지만 언리얼 엔진의 가비지 컬렉션(Garbage Collection) 시스템이 이 변수를 추적하게 되어, 메모리 누수나 크래시(Crash)를 방지하는 중요한 안전장치 역할을 합니다.
에디터 노출 지정자 (디테일 패널 제어)
언리얼 에디터의 오른쪽 ‘디테일 패널’에서 이 변수를 보여줄지(Visible), 아니면 수정하게 해 줄지(Edit) 결정합니다.
| 지정자 | 권한 | 디테일 패널 표시 및 수정 범위 |
|---|---|---|
EditAnywhere | 수정 가능 | 어디서나 수정 가능 (블루프린트 원본 + 월드에 배치된 개별 액터) |
EditDefaultsOnly | 수정 가능 | 블루프린트 원본(디폴트)에서만 수정 가능 (월드에 배치된 액터는 수정 불가) |
EditInstanceOnly | 수정 가능 | 월드에 배치된 개별 액터(인스턴스)에서만 수정 가능 |
VisibleAnywhere | 보기만 가능 | 어디서든 값 확인만 가능 (수정은 C++ 코드에서만 가능) |
VisibleDefaultsOnly | 보기만 가능 | 블루프린트 원본에서 값 확인만 가능 |
블루프린트 접근 지정자
기획자나 다른 작업자가 블루프린트 이벤트 그래프에서 이 변수를 가져다 쓸 수 있도록 Node 형태로 노출합니다.
| 지정자 | 권한 | 생성되는 노드 형태 |
|---|---|---|
BlueprintReadWrite | 읽기 / 쓰기 | 변수 값을 가져오는 Get 노드와, 값을 변경하는 Set 노드 모두 사용 가능 |
BlueprintReadOnly | 읽기 전용 | 변수 값을 가져오는 Get 노드만 사용 가능 (값 변경 불가) |
분류 지정자
디테일 패널이나 블루프린트 노드 검색 창에서 변수를 찾기 쉽게 폴더(그룹)로 묶어줍니다.
Category = "카테고리명"* 예:Category = "Player|Stats"처럼 파이프(|) 기호를 쓰면 하위 폴더(Player 폴더 안의 Stats 폴더)까지 깔끔하게 정리할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 에디터 수정 + 블루프린트 읽기/쓰기 + 카테고리 지정
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Player|Stats")
int32 PlayerLevel;
// 2. 에디터에서 보기만 가능 + 블루프린트에서 읽기만 가능 (데이터 보호 형태) + 블루프린트에서 기본 속성은 변경이 가능하다
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player|Stats")
float CurrentHealth;
// 3. 디폴트(원본)에서만 수정 가능 + 인게임 노출 안 함 (기획자 초기 세팅용)
UPROPERTY(EditDefaultsOnly, Category = "Player|Setup")
TSubclassOf<AActor> StartingWeaponClass;
// 4. 에디터/블루프린트 노출 없이 메모리(GC) 관리만 맡기는 형태
UPROPERTY()
AActor* InternalTarget;
• UFUNCTION()
UFUNCTION()은 멤버 함수를 등록하는 매크로입니다.
주로 C++에서 작성한 강력한 함수를 기획자나 디자이너가 블루프린트(Blueprint) 노드로 쉽게 꺼내 쓸 수 있도록 노출할 때 사용합니다.
빈 UFUNCTION()의 의미
“엔진은 알지만, 블루프린트에는 숨겨진 상태”
UPROPERTY()와 마찬가지로, 함수가 언리얼 엔진의 리플렉션 시스템에는 정상적으로 등록됩니다. 하지만 지정자가 없기 때문에 블루프린트에는 노출되지 않습니다. 즉, “엔진이 함수의 존재는 파악하고 관리하지만, 블루프린트 에디터 창에서는 직접 검색하거나 호출할 수 없게 숨겨둔 상태”가 됩니다.
블루프린트(Blueprint) 지정자
블루프린트에서 C++ 함수를 어떤 형태의 노드로 사용할지 결정하는 핵심 지정자들입니다.
| 지정자 (Specifier) | 노드 형태 | 역할 및 특징 |
|---|---|---|
BlueprintCallable | 실행 핀 (O) | 블루프린트에서 직접 호출 가능한 함수 이벤트 그래프에서 실행(Execute) 핀을 연결해 특정 액션을 수행하는 일반적인 함수 노드로 만듭니다. |
BlueprintPure | 실행 핀 (X) | 값을 읽어오기만 하는 순수 함수 (Getter) 데이터의 상태를 변경하지 않고 오직 값만 반환(Return Value)하는 함수로 만듭니다. |
BlueprintImplementableEvent | 이벤트 노드 | C++에서 선언하고 블루프린트에서 구현 C++ 코드에는 함수의 껍데기(선언)만 존재하고, 실제 내부 동작 로직은 블루프린트 이벤트 그래프에서 구현하도록 위임합니다. |
• 언리얼 클래스 디폴트 vs 인스턴스
클래스 디폴트 (Class Default)
- C++에서의 디폴트값: C++로 클래스를 구현할 때, 주로 생성자에서 초기화해 두는 값이 바로 이 디폴트값이 됩니다.
- 에디터 노출의 중요성: 순수 C++ 코드로만 선언된 변수는 오직 코드 내부에서만 수정할 수 있습니다. 이 값을 언리얼 에디터(블루프린트 디테일 패널)에서 편하게 수정하려면, 반드시
UPROPERTY(EditDefaultsOnly)또는UPROPERTY(EditAnywhere)매크로를 달아서 C++ 변수를 엔진/블루프린트 시스템에 호환되도록 노출시켜 주어야만 합니다.
인스턴스 (Instance)
월드/레벨에 실제로 배치된 개별 객체 하나하나를 말합니다.
- 수정하는 곳: 레벨(월드) 에디터에서 맵에 배치된 특정 액터를 클릭했을 때 우측에 나오는 디테일 패널
- 적용 범위: 오직 내가 지금 클릭한 액터만 값이 적용됩니다. 다른 형제 액터들이나 원본 설계도에는 아무런 영향을 주지 않습니다.
차이
| 구분 | 클래스 디폴트 (Defaults) | 인스턴스 (Instance) |
|---|---|---|
| 작업 위치 | 블루프린트 에디터 내부 | 메인 레벨(맵) 화면 |
| 영향력 | 해당 블루프린트 전체 (광역) | 맵에 배치된 해당 액터 단 하나 (개별) |
| UPROPERTY 지정자 | EditDefaultsOnly | EditInstanceOnly |
[TIP] 매크로를 활용한 C++ & 블루프린트 협업을 위한거다!
언리얼 엔진 개발에서 프로그래머가 C++로 클래스를 만들면, 기획자나 디자이너는 이를 상속받아 블루프린트(BP) 클래스를 만들어 작업하게 됩니다.
1. 매크로를 통한 완벽한 역할 분담
- 프로그래머 (C++): 무겁고 복잡한 핵심 로직을 구현한 뒤, 기획자가 만져야 할 부분만 선별하여 매크로로 권한을 열어줍니다.
UCLASS(Blueprintable): “내가 만든 이 C++ 뼈대로 블루프린트 만드셔도 됩니다!”UPROPERTY(EditDefaultsOnly): “캐릭터 초기 체력이나 이동 속도 같은 수치들은 에디터에서 직접 밸런싱 하세요.”UFUNCTION(BlueprintCallable): “스킬 공격 로직은 제가 다 짜뒀으니, 블루프린트에서는 이 함수 노드만 가져다 호출(연결)하세요.”- 기획자/디자이너 (Blueprint): 프로그래머가
UPROPERTY를 이용하며 수치를 맞추고,UFUNCTION들을 사용합니다.안전한 일방통행 상속 (C++ 원본 보호) 이렇게 상속받은 블루프린트에서 기획자가 디폴트 값을 아무리 수정해도, 부모인 순수 C++ 원본 코드에는 영향도 주지 않습니다. C++ 코드는 이미 뼈대이기 때문입니다. 블루프린트를 만지다가 밸런스 설정이 완전히 꼬여버리면 그 블루프린트만 삭제하고 C++ 원본에서 다시 새 블루프린트를 상속받으면 초기화됩니다
챕터2
• GameMode vs GameModeBase
GameModeBase
- 주요 역할:
DefaultPawnClass,HUDClass,PlayerControllerClass등 게임의 ‘기본 구성 요소’를 결정합니다. - 용도: 퍼즐 게임, 간단한 싱글 플레이, 혹은 엔진이 짜놓은 규칙 없이 처음부터 끝까지 직접 로직을 짜고 싶을 때 사용합니다.
GameMode
GameModeBase를 상속받아 만들어진 클래스로, Match라는 개념이 추가되었습니다.
- 매치 상태 관리: 게임이 현재 ‘대기 중(WaitingToStart)’, ‘진행 중(InProgress)’, ‘일시 정지’, ‘종료(PostMatch)’ 상태인지를 추적하는 기능을 제공합니다.
- 멀티플레이 특화: 플레이어가 서버에 접속을 시도할 때 승인하거나 거절하는 로직, 재시작 로직 등이 구현되어 있습니다.
- 용도: FPS, 배틀로얄, 대전 액션 등 시작과 끝이 명확한 경기 형태의 게임에 적합합니다.
| 구분 | GameModeBase | GameMode |
|---|---|---|
| 상속 관계 | 최상위 부모 | GameModeBase를 상속받음 |
| 매치 상태 | 없음 (항상 실행 중) | 있음 (대기 -> 시작 -> 종료) |
| 멀티플레이 | 최소한의 기능만 포함 | 로그인, 세션 등 풍부한 기능 포함 |
| 복잡도 | 가볍고 단순함 | 다소 무겁지만 강력함 |
| 기본 템플릿 | 빈 프로젝트 시 기본값 | 3인칭/1인칭 템플릿 기본값 |
멀티플레이 프로젝트를 진행할 때 크래시나 동기화 버그로 뒤통수를 맞지 않기 위해 포인트입니다
Server-Only
GameMode는 오직 서버(호스트)의 메모리에만 생성됩니다. 일반 클라이언트(접속자)는 이 클래스가 내 컴퓨터에 있는지조차 모릅니다. 이유: 게임의 핵심 규칙(점수, 승리 조건 등)을 플레이어가 조작(해킹/치트)할 수 없도록, 서버가 숨겨서 관리해야 하기 때문입니다.데이터 공유는 GameState 클라이언트 플레이어도 현재 점수나 남은 시간 등을 화면에 띄워야하는데 클라이언트는
GameMode에 접근할 수 없습니다. 그래서 서버는GameMode의 정보 중 유저들이 알아야 하는 정보들만 골라, 모든 플레이어가 볼 수 있는GameState라는 복사본에 담아 클라이언트들에게 제공합니다.
• Selected GameMode 항목 정의
| 항목명 (Class) | 한 줄 정의 | 비유 |
|---|---|---|
| Default Pawn Class | 플레이어가 조종할 ‘몸체’ | 캐릭터 |
| HUD Class | 플레이어 화면에 띄울 ‘정보창’ | 자막/UI (체력바, 미니맵 등) |
| Player Controller Class | 플레이어의 입력을 처리할 ‘두뇌’ | 마우스/키보드 명령 |
| Game State Class | 게임의 전체적인 ‘현황판’ | 스코어보드 (남은 시간, 팀 점수) |
| Player State Class | 개별 플레이어의 ‘개인 성적표’ | 프로필 (개별 킬 수, 현재 레벨) |
| Spectator Class | 게임에 참여하지 않는 ‘관찰자’ | 자유 시점 카메라 (관전자 시점) |
레벨에서 WorldSetting으로 배정한 게임모드가 1순위로 받아들여지고 세팅으로 설정한 게임모드가 2순위로 받아들여진다.
• Spring Arm과 카메라의 회전 설정
SpringArmComp->bUsePawnControlRotation = true; // 컨트롤러 회전 사용 (O)
이유: 마우스(컨트롤러)를 움직였을 때, 그 회전값을 받아들여 캐릭터 주위를 빙글빙글 도는 주체가 바로 스프링 암이기 때문입니다.
CameraComp->bUsePawnControlRotation = false;// 컨트롤러 회전 사용 (X)
이유: 카메라는 이미 컨트롤러에 맞춰 회전하고 있는 스프링 암 끝에 매달려 있습니다. 부모(스프링 암)가 이미 잘 돌고 있으니, 자식(카메라)은 그저 부모의 움직임만 가만히 따라가면 됩니다.
만약 true로 켜버리면?: 스프링 암이 도는 동시에, 끝에 달린 카메라마저 컨트롤러 회전값을 받아 또 회전해버리는 이중 회전 문제가 생겨서 시점이 이상해집니다.
요약
마우스 회전에 반응해서 회전하는 건 부모인 스프링 암 하나면 충분하다. 자식인 카메라는 가만히 매달려서 앞만 보면 된다.
• StaticClass()
우리가 C++로 코드를 짤 때 AAssignmentsCharacter와 같이 구현한 클래스 이름은 ‘타입’일 뿐이지만, 엔진이 실행될 때는 이 클래스가 어떤 변수를 가졌고 어떤 부모를 가졌는지에 대한 진짜 데이터가 필요합니다.
- 정체: 언리얼 헤더 툴(UHT)이 자동으로 만들어주는 정적(Static) 함수입니다.
- 반환값: 해당 클래스의 정보를 담고 있는
UClass*(포인터)를 반환합니다.
StaticClass() vs GetClass()
| 구분 | StaticClass() | GetClass() |
|---|---|---|
| 성격 | 정적 (Static) | 동적 (Dynamic) |
| 특징 | “설계도 자체”를 가져옵니다. | “이미 태어난 객체”에게 묻습니다. |
| 사용 조건 | 객체가 없어도 언제든 부를 수 있음 (예: MyClass::StaticClass()) | 월드에 존재하는 인스턴스를 통해 호출 |
| 비유 | “이 설계도의 신분증 좀 보여주세요.” | “너는 무슨 설계도로 만들어졌니?” |
요약
- C++ 클래스끼리 정보를 주고받을 때
- ->
MyClass::StaticClass()를 사용합니다.
- ->
- 정보를 받는 쪽의 변수 타입 선언
- -> 단순한
UClass*보다는, 타입 안정성을 위해TSubclassOf<BaseClass>를 사용하는 것을 좋습니다.
- -> 단순한
- 블루프린트(Blueprint) 클래스를 넘겨줄 때
- -> 블루프린트는 C++ 타입이 아니므로
StaticClass()를 쓸 수 없습니다. 대신 생성자에서ConstructorHelpers::FClassFinder등을 사용해 에디터 내 에셋 경로로 찾아서 넘겨주어야 합니다.
- -> 블루프린트는 C++ 타입이 아니므로
언리얼 엔진 기준 해결책
Enhanced Input
- 사용 중인
Input Mapping Context (IMC)에셋을 엽니다. - 마우스 상하 이동이 매핑된 Action(예: Look) 항목을 펼칩니다.
Modifiers(모디파이어)에 Negate를 추가해 줍니다. (값이 -1 곱해져서 반전됨)
• UEnhancedInputLocalPlayerSubsystem: “입력 매뉴얼 총괄 매니저”
모니터 앞에서 조종하고 있는 ‘Local Player’한테 ‘조작 매뉴얼(IMC)’을 할당해주는 매니저입니다.
이름으로 분석
EnhancedInput(향상된 입력): UE5의 최신 입력 시스템입니다.LocalPlayer(로컬 플레이어): AI도 아니고, 네트워크 너머에 있는 다른 유저도 아닌, ‘지금 자신의 컴퓨터에 마우스/키보드를 꽂고 게임하는 바로 자신(1P)’을 뜻합니다.Subsystem(서브시스템): 우리가 직접new나Spawn으로 만들 필요 없이, 언리얼 엔진이 켜질 때 알아서 뒤에서 띄워두고 관리해 주는 ‘자동화된 부서/매니저’입니다.
핵심 역할
IA_Move(WASD)와 IA_Look(마우스)을 만들고, 그걸 IMC(Input Mapping Context)에 할당 했습니다. 그런데 언리얼 엔진은 이 조작법(IMC)을 누구한테 적용해 줘야하는지 모르고 대기하고 있습니다.
이때 UEnhancedInputLocalPlayerSubsystem 매니저를 불러서 이렇게 명령하는 것입니다.
“매니저님! LocalPlayer한테, 이 ‘기본 걷기 조작법(IMC)’ 매뉴얼 적용해 주세요!”
매니저의 우선순위(Priority)
이 매니저는 단순히 매뉴얼을 건네주는 것뿐만 아니라, ‘상황에 따라 어떤 매뉴얼을 우선으로 할지(Priority)’도 결정합니다.
- 평상시: 걷기 매뉴얼 적용 (우선순위 0)
- 차량 탑승 시: 운전 매뉴얼 추가 (우선순위 1) -> 매니저가 “지금은 운전이 먼저야!” 하며 걷기 조작을 무시하고 운전 조작을 먹힙니다.
- 인벤토리 열 때: UI 조작 매뉴얼 추가 (우선순위 2) -> 게임 캐릭터는 멈추고 마우스 커서만 움직이게 통제합니다.
