
오늘은 C++과 블루프린트의 연동 방법을 중심으로 UPROPERTY()와 UFUNCTION()의 주요 지정자를 알아보았습니다. 기존에 했던 블프랑 연결되는 것이 신기하더라고요.
이후 GameMode와 DefaultPawnClass를 통해 게임 시작 시 어떤 Pawn이 스폰되는지 설정하고, Pawn과 Character의 차이도 정리했습니다.
마지막으로 ACharacter를 상속받은 캐릭터를 C++로 만들고 SpringArmComponent와 CameraComponent를 추가하여 3인칭 카메라를 구성한 뒤, GameMode에서 기본 캐릭터로 설정해 실제 게임에서 스폰되는 것까지 확인했습니다.
그런 과정 중에서 배웠던 개념과 문법 헷갈렸던 것을 정리했습니다.
아무튼 먼저 UPROPERTY()부터 알아보겠습니당~~

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

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

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

그럼 함수들을 블프에 등록하도록 하겠습니다. 애네들도 마찬가지로 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()를 호출함으로써 실행할 수 있습니다(아직 아무 기능을 넣지는 않았지만).

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 클래스의 차이가 무엇인가? 한 번 알아보도록 하겠습니다.
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도로 조정해 맞춰줍시다.
그럼 이렇게 전방을 보게 됩니다. 제가 앞길을 찾게 되었다니 참 다행인 일입니당.

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

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

먼저 스프링 암 생성해줍시다. 그리고 그걸 루트 컴포넌트에 부착해주십다. 그리고 캐릭터와 카메라 사이의 거리 기본값을 300으로 설정합니다.
SpringArmCom->UsePawnControlRotation=true으로 설정하여 카메라가 플레이어의 컨트롤러에 반응하도록 true로 설정해주십시다.
다음 카메라 컴포넌트를 생성해줍시다. 아까랑 똑같이 루트 컴포넌트에 부착해줍시다. 그리고 아까 저희는 카메라 컴포넌트에서 카메라가 회전하도록 설정했기에 여기서는 회전이 되지 않도록 false로 설정해줍시다. 그럼 이게 언리얼 에디터에서 적용이 되었는지 빌드 후 확인해보겠습니다.

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

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

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

스폰이 되었군요. 좋은 일입니다.
엑터를 새롭게 배치할 수 있음
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)으로 설정하면 새로운 위치로 옮기기 가능
블프는 시각적 스크립팅 도구, 노드를 연결하여 게임 로직 작성
C++
블프와 C++와 상호보완적 관계
실무에서는 하이브리드 워크플로우가 일반적
C++ 클래스의 변수 및 함수 정보를 엔진 내부의 메타데이터 형태로 저장, 이를 에디터나 블프에서 활용할 수 있게 끔 만들어줌
C++ 클래스에 있는 여러 멤버를 반사해 에디터와 블프에서 직접 설정, 호출이 가능
매개변수를 코드에서만 변경하는 것이 아니라 에디터에서 바로 조정(슬라이더나 숫자 입력)하여 반복 테스트 가능|
#include "item.generated.h"
기본 동작은 블루프린트에서 상속이 가능하고 변수로 참조 가능한 형태로 등록됨
주요 옵션
Blueprintable - 블프에서 상속 가능한 클래소 만ㄷ름NotBLueprintable - 블프에서 상속 XblueprintType - 블프에서 변수나 참조 사용할 수 있게 끔 함(멤버 변수를 리플렉션 시스템에 등록)
UPROPERTY()에는 여러 지정자를 작성해 에디터에서의 표시 여부나 블프 접근성, 읽기/쓰기 권한 등을 자세하게 설정 가능
1. 편집 가능 범위 지정자
VisibleAnywhere: 일기 전용으로 표시, 수정은 불가능EditAnywhere: 클래스 기본값, 인스턴스 모두에서 수정 가능EditDefaultsOnly: 클래스 기본값에서만 수정 가능EditInstanceOnly: 인스턴스에서만 수정 가능2. 블프 접근성 지정자
BlueprintReadWrite: 블프 그래프에서 Getter/Setter로 값을 읽거나 쓸 수 있음BlueprintReadOnly: Blueprint 그래프에서 Getter 핀만 노출되어, 읽기만 가능3. Catergory 지정자
4. 메타 옵션 지정자
meta=(ClampMIn="0.0"): 에디터에서 변수 입력시 최소값을 제한 할 수 있음meta=(AllowPrivateAccess="true"): 해당 멤버가 private로 선언되어 있어도, 에디터나 블프에서 접근할 수 있도록 허용If UPROPERTY()만 있고 추가 지정자를 하나도 안 해준다면?
지금까지는 멤버 변수를 리플렉션 시스템에 노출하는 것을 함 이제 함수로 넘어감
UFUNCTION()은 멤버 함수를 등록함
주요 지정자 (Blueprint 관련 지정자)
BlueprintCallable: 이벤트 그래프에서 호출 가능한 함수로 만듦BlueprintPure: Getter 역할만 수행(Exec 핀 없이 Return Value만 노출)BlueprintImplementableEvent: 함수 선언만 C++에서 하고 구현은 블프에서(Blueprint Event Graph에서)If 지정자를 하나도 안 쓰면?
UPROPERTY()와 마찬가지로, 함수가 언리얼 리플렉션에 등록되지만 특별히 블프에서는 노출 XGameMode는 전반적인 규칙과 흐름을 총괄 관리하는, 일종의 컨트롤 타워임
싱글플레이에서는 서버와 클라이언트이기 때문에 필요없음 온전히 로컬에서 동작
Pawn(또는 Character)을 플레이어게 스폰할지, 컨트롤러를 사용할지, 승패 조건이나 점수 계산 방법은 어떻게 설정할지 등 게임 플레이의 핵심 로직을 담당
Tools -> New C++ Class를 클릭GameMode를 선택Public으로 설정:: → 범위 지정 연산자: → 상속이나 초기화 리스트 등에 사용오늘은 C++과 블루프린트가 어떻게 서로 손을 잡고 일하는지 알아보았습니다. UPROPERTY()와 UFUNCTION()을 통해 C++에서 만든 변수와 함수를 블루프린트와 연결할 수 있다는 게 꽤 신기하더라고요. 기존에 했던 내용들이 이렇게 이어지는게 보이니까 참 놀라운 일입니당.
또한 GameMode와 DefaultPawnClass를 통해 게임이 시작될 때 어떤 캐릭터가 스폰되는지도 직접 설정해보고, Pawn과 Character의 차이까지 정리해봤습니다.
마지막으로 C++로 캐릭터를 만들고 SpringArm과 CameraComponent까지 붙여보니, 이제 진짜 게임 캐릭터 하나를 만들어낸 기분이네요.
아직 배운 게 산더미지만 그래도 하나씩 쌓다 보면 언젠가는 코드로 세상을 움직이는 날이 오겠죠?
그 때까지 이 팔팔(?)하지 못한 치타는 달려보겠습니당........