C++ 블루프린트 퓨전~~!(UPROPERTY,UFUNCTION,Gamemode,Pawn,Character,Defaultpawnclass,레플렉션)

정윤재·2026년 8월 14일
post-thumbnail

오늘은 C++과 블루프린트의 연동 방법을 중심으로 UPROPERTY()와 UFUNCTION()의 주요 지정자를 알아보았습니다. 기존에 했던 블프랑 연결되는 것이 신기하더라고요.

이후 GameMode와 DefaultPawnClass를 통해 게임 시작 시 어떤 Pawn이 스폰되는지 설정하고, Pawn과 Character의 차이도 정리했습니다.

마지막으로 ACharacter를 상속받은 캐릭터를 C++로 만들고 SpringArmComponent와 CameraComponent를 추가하여 3인칭 카메라를 구성한 뒤, GameMode에서 기본 캐릭터로 설정해 실제 게임에서 스폰되는 것까지 확인했습니다.
그런 과정 중에서 배웠던 개념과 문법 헷갈렸던 것을 정리했습니다.
아무튼 먼저 UPROPERTY()부터 알아보겠습니당~~


1. UPROPERTY()


Create Blueprint Class based on AItem" → C++에 등록된 AItem을 기반으로 블루프린트를 만들어봅시다.

UPROPERTY()를 사용해 Root Scene Component는 루트가 되는 컴포넌트이므로(뿌리가 되니까 웬만하면 안 건드는 게 좋겠죵?) VisibleAnywhere를 사용해 에디터에서 확인만 가능하게 하고, BlueprintReadOnly를 사용해 블루프린트에서도 읽기만 가능하게 했습니다.
반면 회전 속도는 EditDefaultsOnly를 사용해 블루프린트 클래스의 기본값에서만 수정할 수 있게 설정했습니다.(변수나 구조는 건드리지 않고 오로지 속도 값만 변경하기 위해서)

아 참고로 UPROPERTY()는 뒤에 오는 변수의 속성과 접근 방식을 설정하는 것이기 때문에 반드시 뒤에 변수가 와야 합니다~

그럼 Class Defaults(Details 패널도 가능)에서 RotationSpeed을 확인할 수 있습니다.
이 값만 조정해도, 코드를 다시 빌드하지 않고 오브젝트의 회전 속도를 직접 바꿀 수 있습니다.
StaticMeschComp나 Material 속성도 변경이 가능합니다. 이렇게 C++ 코드 수정 없이 바로 변경하고 테스트 가능한게 리플렉션의 장점입니다.


2. UFUNCTION()

그럼 함수들을 블프에 등록하도록 하겠습니다. 애네들도 마찬가지로 UFUNCTION 뒤에 함수 와야합니다. void ResetActorPosition()은 호출만 되도록 하고 float GetRotationSpeed() const는 값만 반환하는(Getter죠)로 void OnItemPickedUP()는 호출은 하지만 블프에서 구현하도록 설정하겠습니다.
헤더에 입력했으니 소스에도 적어줘야겠죵?


OnItemPickedUP()은 BeginPlay()에서 호출하고, ResetActorPosition()은 따로 함수를 정의해주었습니다. 값은 ZeroVector 사용해서 0,0,0 위치로 설정했습니다. 생성자는 반환만 하도록 구현했습니다.


ResetActorPosition는 Callable로 선언했기에 노드로 실행 가능하고 GetRotationSpeed는 Pure로 선언했으므로 단순히 값만 반환하는 Getter 노드로 사용 가능하고 OnItemPickedUP는 Event로 선언했으므로 이벤트 그래프 안에서 구현한 내용을 C++에서
OnItemPickedUp()를 호출함으로써 실행할 수 있습니다(아직 아무 기능을 넣지는 않았지만).


3. GameMode


Tools에서 New C++ Class를 누릅시다.

검색해 줘서 GameMode SpartaGameMode를 생성해줍니다.

그럼 이런 식으로 SpartaGameMode 헤더파일과 소스파일이 생성됩니다. 소스파일을 보면 스파르타 게임 모드가 게임 모드를 상속 받고 있습니다.

그리고 블프로 감싸줍시다. 이러면 여러 파라미터 조정 등 유용하게 잘 쓰이기 때문입니다.

그러면 제가 이전에 블프로 만들었더 프로젝트들에서도 게임 모드 설정을 했었죠? Edit에서 Project Settings로 이동하여 목록에서 Maps & Modes 항목 선택하고 Default GaMEmODE를 BP_SpartaGameMode로 지정해주면 모든 레벨에 기본 GameMode가 스파르타 게임 모드로 설정됩니다. 아무래도 Default 값이니까~~
자 이제 레벨별로 설정을 한 번 바꿔보겠습니다. 이전에 했었지만 복습 차원에서 해보겠습니다. ![]

Window에서 World Settings을 열어줍시다.

여기서 GameMode을 프로젝트 전역 설정 대신 이 특정 레벨은 스파르타 게임 모드로 설정됩니다.

또 Default Pawn Class, HUD Class, Player Controller Class, Game State Class/Player State Class, Spectator Class를 설정할 수 있습니다. 차레대로 간략히 알아보면
Default Pawn Class-플레이어가 조종할 pawn(또는 Character)
HUD Class-게임 화면에 표시되는 HUD(UI) 요소를 관리하는 클래스
Player Controller Class-플레이어 입력 처리, 카메라 제어, UI와 상호작용을 담당한느 클래스
Game State/ Player State Class-게임의 전역 상태나 플레이어별 상태를 추적
Spectator Class-관전자 모드에서 사용할 Pawn 클래스를 지정, 일반 플레이어와 다른 이동 방식이나 카메라 제어가 필요할 때 활용
게임을 실행하면 기본 설정들이 자동으로 스폰되고, 스파르타게임모드가 게임의 규칙을 총괄하는 GameMode로 동작합니다. 밑에처럼 말이죠.

근데 여기서 의문점이 하나 들죠? Pawn이랑 Character 클래스의 차이가 무엇인가? 한 번 알아보도록 하겠습니다.


4.Pawn과 Character

Pawn은 플레이어 혹은 AI가 Possess 할 수 있는 가장 상위 클래스입니다. 엔진에서 무언가를 조종한다? 가장 기본이 되는 형태 pawn입니다. Pawn에는 이동 로직이나 충돌 처리, 중력, 네트워크 이동을 위한 기능들이 기본적으로 포함 안 되어 있습니다. 사실 깡통이죠. 그래서 보행이나 점프 등에 필요한 시스템을 모든 단계에서 구현해야해서 사람 캐릭터(몬스터 등)을 Pawn으로 만들면 힘들겠죠?

Character는 Pawn을 상속받아 만들어진 자식 클래스로 기본적으로 UCharacterMovementComponent를 포함하고 있습니다.(이동, 회전, 점프, 중력 등등) 따라서 미리 정의된 대표적인 함수들(MoveFoward, Jump)이 존재하므로 딸각(?)으로 처리가 가능합니다. 그리고 이런 캐릭터를 구현하는 전형적인 요소들이 표준화되어 있어, 일반적인 인간형 캐릭터를 만드는데 최적화 되어있습니다.

그렇다면 왜 모든 것을 Character로 안 만들고 Pawn으로 만들까요? 자동차나 비행기 처럼 완전히 다른 이동 방식으로 움직여야할 때 Character에 내부에 탑대죈 기능들이 오히려 방해가 되기에 Pawn에서 확장하는 것이 나을 수 있습니다.
그렇다면 이런 Character를 한 번 생성해보겠습니다.

이제는 익숙하죠? 여기서 제 이름을 가진 캐릭터를 생성하겠습니다.(그냥 앞으로 캐릭터라고 할게용~)

그럼 이렇게 기본적인 기능들이 가진 캐릭터가 생성이 됩니다. 자 이제 이걸 블루프린트에서 다루어봅시다.
만든 캐릭터 기반으로 블프를 만들어줍시다.

(이름이 마음에 안 들어서 다시 만들어서 찍은 거라 이후에 나오는 spring arm과 camera는 못 본 척 해주세요~)
그럼 이런 식으로 이미 기초적인 컴포넌트들은 생성이 되어있습니다. 캡슐 컴포넌트와 메쉬는 예전에 다뤘기 때문에 설명하지 않겠습니다.
ArrowComponent는 캐릭터가 어느 방향을 바라보고 있느지를 표시하기 위해 씬에 화살표를 띄워주는 컴포넌트입니다. 게임 플레이 로직에는 직접적인 영향을 주지 않고, 주로 편집기에서 시각적인 디버깅용으로 사용됩니다.
CharacterMovementComponent의 경우 캐릭터의 이동, 점프, 중력, 네트워크 동기화 등 물리적 이동 로직을 담당하는 핵심 컴포넌트입니다. 캐릭터의 꽃이죠. 주요 이동 함수가 이미 연결되어 있어 최소한의 코드만으로 캐릭터 조작을 구현할 수 있습니다.
메쉬 쪽에서 새로 배운 것만 기술하겠습니다.
언리얼에서 일반적으로 캐릭터의 전방 방향은 X축입니다.

하지만 언리얼 엔진에서 기본적으로 제공해주는 SKM_Manny나 SKM_Quinn의 모델은 Y축을 전방으로 사용하기 때문에 옆을 보는 것처럼 보입니다. 따라서 좌우쪽인(공부할래yaw? 절레절레) yaw(z축)을 -90도로 조정해 맞춰줍시다.
그럼 이렇게 전방을 보게 됩니다. 제가 앞길을 찾게 되었다니 참 다행인 일입니당.

기즈모를 사용해 루트 컴포넌트인 캡슐 컴포넌트에 맞춰 메쉬를 움직여줍시다.


5. 카메라 및 GameMode 설정하기

게임에서는 캐릭터가 뒤쪽이나 어깨 너머 시점을 보며 많이 이동하죠? 그럼 캐릭터와 카메라 간의 거리를 유지하고, 충돌시 카메라가 벽 등에 박히지 않도록 위치를 자동 조정하는 SpringArmComp과 실제로 화면에 표시되고 위치와 회전을 제어하며 게임뷰가 변경되는 카메라 컴포넌트를 추가하겠습니다. 물론 저희가 블프에서 카메라를 설정했었지만 이번에는 SpringArm 및 CameraComponent을 C++로 추가하여 한 번 직접해봅시다!

먼저 SpringArm에 대하여 클래스 헤더를 선언합시다. 그 다음 CameraComponent에 대하여 클래스를 전방 선언해줍시다. VisibleAnywhere, BlueprintReadOnly 블루프린트에서 보기만 가능하고, C++ 코드 쪽에서 수정 가능하게 해주는 속성입니다.

먼저 스프링 암 생성해줍시다. 그리고 그걸 루트 컴포넌트에 부착해주십다. 그리고 캐릭터와 카메라 사이의 거리 기본값을 300으로 설정합니다.
SpringArmCom->UsePawnControlRotation=true으로 설정하여 카메라가 플레이어의 컨트롤러에 반응하도록 true로 설정해주십시다.

다음 카메라 컴포넌트를 생성해줍시다. 아까랑 똑같이 루트 컴포넌트에 부착해줍시다. 그리고 아까 저희는 카메라 컴포넌트에서 카메라가 회전하도록 설정했기에 여기서는 회전이 되지 않도록 false로 설정해줍시다. 그럼 이게 언리얼 에디터에서 적용이 되었는지 빌드 후 확인해보겠습니다.

잘 적용이 되었네요.


6.GameMode에서 DefaultPawnClass 설정하기

블프로 할 때도 했었지만 게임 시작시 어떤 캐릭터를 플레이어게 제공할 건지 C++ 게임모드에서 DefaultPawnClass를 추가해서 설정합시다.

헤더파일에서 DefaultPawnClass를 설정할 생성자를 만들어줍시다.

소스파일에서 기본 폰을 저로 설정합니다. 이제 잘 작동하는지 확인해보겠습니다.

스폰이 되었군요. 좋은 일입니다.


7. 개념 및 헷갈려던 문법 복습

엑터를 새롭게 배치할 수 있음

FVector NewLocation(30.of, 200.0f, 100.0f)
FRotator NewRoatation(0.0f, 90.0f, 0.0f)
FVector NewScale(2.0f)

FTransform NewTransform(NewRotation, NewLocation, NewScale)

SetActorTransform(NewTransform)으로 설정하면 새로운 위치로 옮기기 가능

1 리플렉션 시스템 활용

1-1. 리플렉션 시스템 이해

블프는 시각적 스크립팅 도구, 노드를 연결하여 게임 로직 작성

  • 장점
  • Blueprint 그래프를 수정, 에디터에서 play 버튼 누르면 결과를 확인 가능, 빠르게 검증 반복할 때 장점
  • 레고 블록이다보니 초보자도 쉽다
  • 한계
  • 노드 많아지면 가독성 유지보수 어려워짐
  • 블프는 추가 해석 과정이 필요함, 물리 연산이 AI 같은 높은 성능이 필요한 시스템에서는 병목이 되기 쉬움

C++

  • 장점
  • 엔진 코어까지 직접 수정 가능, 복잡하고 성능이 중요한 게임 로직을 빠르고 최적화된 방식으로 구현 가능
  • 표준 라이브러리와 외부 라이브러리를 자유롭게 사용 가능, 대규모 프로젝트에 적합
  • 포인터, 템플릿 같은 C++ 언어적 기능을 통해 메모리와 로직을 정교하게 다루기 가능
  • 한계
  • C++ 코드 수정시 에디터 재시작이나 Live Coding을 다시 컴파일해야함, 반복시 안 좋음

블프와 C++와 상호보완적 관계
실무에서는 하이브리드 워크플로우가 일반적

  • 블프: UI 제작, 간단한 이벤트 처리, 시각적 연출 등 빠른 프로토타이핑과 직관적 로직 작성에 사용
  • C++: 높은 성능이 필요한 게임플레이 로직이나 엔진 레벨의 확장, 복잡한 수학 연산 등에 사용

1-2리플렉션이란

C++ 클래스의 변수 및 함수 정보를 엔진 내부의 메타데이터 형태로 저장, 이를 에디터나 블프에서 활용할 수 있게 끔 만들어줌

C++ 클래스에 있는 여러 멤버를 반사해 에디터와 블프에서 직접 설정, 호출이 가능
매개변수를 코드에서만 변경하는 것이 아니라 에디터에서 바로 조정(슬라이더나 숫자 입력)하여 반복 테스트 가능|

#include "item.generated.h"
  • 언리얼 엔진이 자동 생성하는 헤더 파일로, 클래스의 리플렉션 및 엔진 통합에 필요한 코드 들어 있음
  • UCLASS() 해당 클래스를 언리얼 엔진의 리플렉션 시스템에 등록
  • 이 매크로가 있어야 블프 등 에디터 차원에서 클래스 인식 가능
  • GENERATED_BODY()
  • 언리얼의 코드 생성 도구가 사용하는 코드 삽입 역할을 함
  • 클래스 내부에 필요한 리플렉션 정보를 자동으로 생성

UCLASS() 매크로의 주요 지정자 (옵션)

기본 동작은 블루프린트에서 상속이 가능하고 변수로 참조 가능한 형태로 등록됨

주요 옵션

  • Blueprintable - 블프에서 상속 가능한 클래소 만ㄷ름
  • NotBLueprintable - 블프에서 상속 X
  • blueprintType - 블프에서 변수나 참조 사용할 수 있게 끔 함
  • 이 옵션만 있으면 ㅅ상속 불가 참조만 가능

1-3. 변수에 리플렉션 적용하기

UPROPERTY() 매크로의 주요 지정다

(멤버 변수를 리플렉션 시스템에 등록)

UPROPERTY()에는 여러 지정자를 작성해 에디터에서의 표시 여부나 블프 접근성, 읽기/쓰기 권한 등을 자세하게 설정 가능

1. 편집 가능 범위 지정자

  • VisibleAnywhere: 일기 전용으로 표시, 수정은 불가능
  • EditAnywhere: 클래스 기본값, 인스턴스 모두에서 수정 가능
  • EditDefaultsOnly: 클래스 기본값에서만 수정 가능
  • EditInstanceOnly: 인스턴스에서만 수정 가능

2. 블프 접근성 지정자

  • BlueprintReadWrite: 블프 그래프에서 Getter/Setter로 값을 읽거나 쓸 수 있음
  • BlueprintReadOnly: Blueprint 그래프에서 Getter 핀만 노출되어, 읽기만 가능

3. Catergory 지정자

  • Details 패널에서 이 변수는 "Rotation" 범주(폴더) 아래에 표시
  • 여러 변수를 비슷한 카테고리에 묶으면, 세부 정보 패널에서 깔끔하게 정리되어 보임

4. 메타 옵션 지정자

  • meta=(ClampMIn="0.0"): 에디터에서 변수 입력시 최소값을 제한 할 수 있음
  • meta=(AllowPrivateAccess="true"): 해당 멤버가 private로 선언되어 있어도, 에디터나 블프에서 접근할 수 있도록 허용

If UPROPERTY()만 있고 추가 지정자를 하나도 안 해준다면?

  • 엔진 리플렉션 시스템에는 등록되지만, 에디터나 블프에서는 노출되지 X
  • 엔진이 변수의 존재를 알고 있지만 외부에서는 보이지 않게 클로킹한 상태
  • 리플렉션에 등록만 되어 잇어도 가비지 컬렉션(메모리 관리)과 직렬화(세이브/로드) 같은 엔진 내부 기능이 작동할 수 있음

1-4 함수에 리플렉션 적용

지금까지는 멤버 변수를 리플렉션 시스템에 노출하는 것을 함 이제 함수로 넘어감
UFUNCTION()은 멤버 함수를 등록함

주요 지정자 (Blueprint 관련 지정자)

  • BlueprintCallable: 이벤트 그래프에서 호출 가능한 함수로 만듦
  • BlueprintPure: Getter 역할만 수행(Exec 핀 없이 Return Value만 노출)
  • BlueprintImplementableEvent: 함수 선언만 C++에서 하고 구현은 블프에서(Blueprint Event Graph에서)

If 지정자를 하나도 안 쓰면?

  • UPROPERTY()와 마찬가지로, 함수가 언리얼 리플렉션에 등록되지만 특별히 블프에서는 노출 X
  • 엔진이 조재는 파악했지만 블프에서는 클로킹한 상태

2. Character 클래스를 활용한 캐릭터 구현

GameMode는 전반적인 규칙과 흐름을 총괄 관리하는, 일종의 컨트롤 타워임
싱글플레이에서는 서버와 클라이언트이기 때문에 필요없음 온전히 로컬에서 동작
Pawn(또는 Character)을 플레이어게 스폰할지, 컨트롤러를 사용할지, 승패 조건이나 점수 계산 방법은 어떻게 설정할지 등 게임 플레이의 핵심 로직을 담당

  • 프로젝트 전체 혹은 레벨별로 gamdmode를 별도 구분 설정 가능

2-1 GameMode의 주요 기능과 책임

  • 플레이어 Pawn/Character 스폰
  • 게임이 시작될 때(또는 플레이어가 리스폰 될 때), DefaultPawnClass 또는 지정한 Pawn 클래스를 자동으로 스폰
  • 스폰된 Pawn을 플레이어가 조작할 수 있도록 playercontroller와 연동해주는 역할을 함
  • PlayerController 지정
  • 플레이어의 입력(키보드, 마우스, 게임패드 등)을 전달하고 처리하는 PlayerController가 어떤 클래스로 동작할지 설정
  • 게임 규칙 관리
  • 점수 계산, 타이머, 라운드 제어, 난이도 등 게임 전반의 규칙을 정의하고 유지
  • 특정 점수 달성, 보스 몬스터 처치, 제한 시간 종료 등 승리/패배를 결정하는 조건을 관리
  • 승리 혹은 패배가 확정되면 게임 오버 화면을 띄우거나 다음 레벨로 전환하는 식의 후속 처리를 담당
  • GameState / PlayerState 사용
  • GameState는 전체 게임 흐름(타이머, 전역 변수 등), PlayerState는 플레이별 정보(체력, 점수 등)를 관리하는 용도
  • 멀티플일 때 만큼 복잡하게 쓰지 않더라도, 상태 저장과 관리를 좀 더 체계적으로 하고 싶을 때 유용함

3. GameMode 클래스 생성하기

3-1 GameMode vs GameModeBase

  • GameMode: 언리얼에서 제공하는 멀티플 기능(세션, 플레이어 연결 로직 등)을 일부 포함, 싱글 플레이에서도 문제없이 사용 가능. 필요에 따라 GameState, PlayerState 등 연동이 활성됨
  • GameModeBase: 멀티플 관련 로직이 거의 포함 X. 싱글플겜 또는 직접 멀티플 로직 구현하고 싶을 때 사용함

3-2 C++클래스 GameMode 생성

  • 언리얼 에디터 상단 메뉴에서 Tools -> New C++ Class를 클릭
  • Common Classes 목록 또는 검색창에서 GameMode를 선택
  • 클래스 접근 수준 Public으로 설정

문법

  • :: → 범위 지정 연산자
  • : → 상속이나 초기화 리스트 등에 사용

8.마무리

오늘은 C++과 블루프린트가 어떻게 서로 손을 잡고 일하는지 알아보았습니다. UPROPERTY()와 UFUNCTION()을 통해 C++에서 만든 변수와 함수를 블루프린트와 연결할 수 있다는 게 꽤 신기하더라고요. 기존에 했던 내용들이 이렇게 이어지는게 보이니까 참 놀라운 일입니당.

또한 GameMode와 DefaultPawnClass를 통해 게임이 시작될 때 어떤 캐릭터가 스폰되는지도 직접 설정해보고, Pawn과 Character의 차이까지 정리해봤습니다.

마지막으로 C++로 캐릭터를 만들고 SpringArm과 CameraComponent까지 붙여보니, 이제 진짜 게임 캐릭터 하나를 만들어낸 기분이네요.

아직 배운 게 산더미지만 그래도 하나씩 쌓다 보면 언젠가는 코드로 세상을 움직이는 날이 오겠죠?
그 때까지 이 팔팔(?)하지 못한 치타는 달려보겠습니당........

profile
게임 개발자 꿈나무

0개의 댓글