📍 2주차 1강 ~ 2강
🛠️ 게임을 만들자 🛠️
🧍♂️ 캐릭터 기능
1. 캐릭터 클래스 구현
2. 입력 매핑 설정 (WASD, 점프, 스프린트, 카메라 회전)
3. 기본 동작 구현 (이동, 점프, 스프린트, 회전 등)
4. 애니메이션 적용 (Idle, Walk, Jump, Sprint)
🎁 아이템 시스템
5. 아이템 종류 설계 (치료, 코인, 체력 회복 아이템 등)
6. 아이템 상호작용 처리 (범위 내 있을 경우 효과 발생 - 체력 감소 / 점수 증가 / 체력 회복 등)
7. 아이템 랜덤 스폰 & 레벨에 따른 수량 및 배치 조정
💥 전투 & 점수
8. 캐릭터 데미지 처리 & 아이템 점수 관리 시스템
🌊 게임 흐름
9. 웨이브 시스템 구현 (적 생성 및 흐름 제어)
10. 실시간 HUD 정보 반영
11. 메뉴 및 UI 흐름 구현
🎨 시각 & 사운드 효과
12. UI 애니메이션 및 3D 위젯 UI 적용
13. 파티클 적용 (지뢰 폭발, 아이템 습득 등)
14. 사운드 효과 적용 (지뢰 폭발음, 아이템 획득음, 발걸음 등)
🚀 마무리
15. 최종 프로젝트 배포
: 총괄 관리자 역할의 클래스
| 번호 | 역할 | 예시 (클래스명) |
|---|---|---|
| 1️⃣ | 플레이어 캐릭터 | Pawn 클래스 or Character 클래스 |
| 2️⃣ | 캐릭터에 빙의 | PlayerController 클래스 |
| 3️⃣ | 게임 규칙 관리 | 로직(함수), 점수의 규칙 등 |
| 4️⃣ | 게임 전역 데이터 | GameState 클래스 (점수 등 전체 공유) |
| - | 개별 캐릭터 데이터 | PlayerState 클래스 |
| 항목 | AGameModeBase | AGameMode |
|---|---|---|
| 기본 기능 | 아주 최소한의 게임 규칙만 제공 | 점수 처리, 팀, 플레이어 관리 등 더 많은 기능 포함 |
| 상속 용도 | 커스텀 룰이 거의 없을 때 적합 | 룰이 많은 게임 (멀티플레이, 점수 등)에 적합 |
| 예시 메서드 | StartPlay() 등 | HandleMatchHasStarted(), RestartPlayer() 등 |
| 유연성 | 더 가볍고 유연함 | 기능 많지만 무겁고 복잡함 |
1️⃣ C++ 클래스로 GameMode 만든다 → SpartaGameMode

2️⃣ 블루프린트 상속 → BP_SpartaGameMode
C++로 만든 GameMode 그대로 적용하는 것보다 블루프린트로 감싸서 쓰면 에디터 내에서 여러 파라미터 수정 가능

SpartaGameMode (C++)BP_SpartaGameMode (블루프린트)➡ "설계도(C++)"를 먼저 만들었고, 그걸 기반으로 "커스터마이징된 사본(BP)"을 만듦
프로젝트에 전역 GameMode 적용하기
Project Settings > Maps & Modes > Default GameMode
이 프로젝트에 생성된 모든 레벨에 전부 BP_GameMode를 GameMode로 사용
GameMode가 관리하는 클래스도 설정 가능
| 항목 | 설명 |
|---|---|
| Default Pawn Class | 플레이어가 조종할 기본 캐릭터 클래스 |
| PlayerController Class | 입력 처리 담당 클래스 |
| HUD Class | UI 표시를 담당하는 클래스 |
| GameState Class | 게임 전반의 상태 (점수, 시간 등) 공유 |
| PlayerState Class | 개별 플레이어 정보 (점수, 팀, 이름 등) 관리 |
| Spectator Class | 관전자용 Pawn 클래스 플레이어가 죽었을 때 조종하는 Pawn 시점 전환, 카메라 이동, 다른 플레이어 구경 등에 사용 |
특정 레벨에만 GameMode 적용하기

Window > World Settings
레벨마다 따로 GameMode를 지정할 수 있음 → Level 단위 설정
→ 레벨에서 따로 지정하면 프로젝트 기본값은 무시되고 그 레벨만의 GameMode가 적용됨
| 구분 | 설정 위치 | 우선순위 |
|---|---|---|
| 프로젝트 기본값 | Project Settings > Maps & Modes | 낮음 |
| 레벨 전용 설정 | 레벨에서 World Settings > Selected GameMode | 높음 |

Actor 모든 게임 오브젝트의 기본 클래스
└── Pawn 플레이어나 AI가 조종 가능한 오브젝트
└── Character 보행, 점프 등 기본 이동 기능이 내장된 Pawn 클래스
| 구분 | AActor | APawn | ACharacter |
|---|---|---|---|
| 조종 가능 | ❌ | ✅ | ✅ |
| 이동 컴포넌트 | ❌ 수동 구현 | ❌ (수동) | ✅ CharacterMovementComponent 내장 |
| 점프 / 중력 | ❌ | ❌ | ✅ 자동 포함 |
| 메시 컴포넌트 | ❌ | ❌ | ✅ SkeletalMeshComponent 내장 |
| 예시 | 문, 상자, 아이템 | 자동차, 비행기, 드론 | 사람형 캐릭터, 몬스터 |
❓ Pawn 클래스 = "조종 가능한 오브젝트"
마리오 카트는 플레이어가 자동차를 조종함
1️⃣ C++ 클래스로 캐릭터 만든다 → SpartaCharacter
2️⃣ 블루프린트 상속 → BP_SpartaCharacter

| 컴포넌트 이름 | 설명 |
|---|---|
| CapsuleComponent | 충돌 영역. 캐릭터의 물리적인 범위를 나타냄 (기본 충돌 바운딩) |
| ArrowComponent | 캐릭터의 앞 방향을 시각적으로 보여줌 (방향 확인용) |
| Mesh (CharacterMesh0) | 캐릭터의 스켈레탈 메시(3D 모델) 표시 컴포넌트, 뼈가 있음 |
| CharacterMovement | 걷기, 점프, 중력 등 이동 처리 전담 컴포넌트 |
3️⃣ Skeletal Mesh Asset 설정하기
Character 시선 방향은 x축
4️⃣ Capsule Component에 맞추기

5️⃣ 3인칭 게임이라면 캐릭터 뒤통수에 카메라 붙여줘야 함
✅ C++로 만들어보기
| 상황 | 설명 |
|---|---|
| 미리 선언만 할 때 | .h 파일에서 class AMyClass;처럼 이름만 알려줌 |
| 전체 정의가 필요할 때 | .h 또는 .cpp에서 #include "MyClass.h" 해야 함 |
RootComponent
└── SpringArmComp
└── CameraComp
| 요소 | 설명 |
|---|---|
CreateDefaultSubobject<T>() | 컴포넌트를 엔진에 등록하면서 생성하는 함수 |
TEXT("SpringArmComp") | 에디터/엔진에서 이 컴포넌트를 식별할 이름 |
SpringArmComp | 해당 컴포넌트를 변수에 저장해서 나중에 설정하거나 사용 |
//SpartaCharacter.h
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpartaCharacter.generated.h"
// 헤더에서 include를 사용하지 않고 미리 선언을 사용
class USpringArmComnponent;
class UCameraComponent;
UCLASS()
class SPARTAPROJECT_API ASpartaCharacter : public ACharacter
{
GENERATED_BODY()
public:
ASpartaCharacter();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera") // 객체 변경은 불가능, 내부 속성은 에디터에서 조정 가능
USpringArmComponent* SpringArmComp; // 컴포넌트 선언
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
UCameraComponent* CameraComp;
protected:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};
// SpartaCharacter.cpp
#include "SpartaCharacter.h"
// 헤더 파일에서 include를 사용하지 않고 미리 선언을 사용
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
ASpartaCharacter::ASpartaCharacter()
{
PrimaryActorTick.bCanEverTick = false; // 이 캐릭터는 Tick을 사용하지 않음
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp")); // 컴포넌트 생성
SpringArmComp->SetupAttachment(RootComponent); // 루트 컴포넌트에 연결
SpringArmComp->TargetArmLength = 300.0f; // 스프링 암(삼각대)의 길이 설정
SpringArmComp->bUsePawnControlRotation = true; // 캐릭터의 회전을 스프링 암이 따르도록 설정
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComp"));
CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName); // 스프링 암 끝 부분에 연결
CameraComp->bUsePawnControlRotation = false; // 카메라가 캐릭터의 회전을 따르지 않도록 설정
}
void ASpartaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}

코드에서 에러는 없어서 솔루션 정리 후 다시 빌드했더니 찾았다!!

VisibleAnywhere, BlueprintReadOnly → 디테일 창에서 조정 가능
→ SprintArm 높이 변경
6️⃣ GameMode에서 Character 연결
✅ Editor
이 GameMode가 적용된 레벨들은 방금 만든 캐릭터를 사용함

✅ C++
// GameMode.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameMode.h"
#include "SpartaGameMode.generated.h"
UCLASS()
class SPARTAPROJECT_API ASpartaGameMode : public AGameMode
{
GENERATED_BODY()
public:
ASpartaGameMode(); // 생성자 선언
};
// GameMode.cpp
#include "SpartaGameMode.h"
#include "SpartaCharacter.h" // Character 헤더파일 포함
ASpartaGameMode::ASpartaGameMode() // 생성자 정의
{
DefaultPawnClass = ASpartaCharacter::StaticClass(); // 기본 폰 클래스를 SpartaCharacter로 설정
// StaticClass()는 클래스의 정적 메서드로,
// 객체를 생성하지 않았지만 해당 클래스의 UClass 객체를 반환함
// C++ 클래스에서 그 클래스의 UClass 정보를 꺼내주는 역할
}
DefaultPawnClass = ASpartaCharacter::StaticClass();
기본 플레이어 캐릭터는 SpartaCharacter라는 클래스를 기반으로 만들어~
StaticClass()
비교
| 코드 | 의미 |
|---|---|
new ASpartaCharacter() | 객체를 "지금" 메모리에 직접 생성 |
ASpartaCharacter::StaticClass() | "클래스 정보"만 가져옴 (객체는 아직 X) |
아래 PlayerStart 위치에 Character spawn
💡 PlayerStart
게임 시작 시 PlayerStart 위치에 Character spawn (GameMode 역할)

여기까지 하면 캐릭터 등장 짠 ~ 🌟

[🧑💻 플레이어]
↓ 입력
[🎮 PlayerController]
├─→ [🧍♂️ 캐릭터 (Pawn)] ← 이동 / 점프 등
├─→ [📷 카메라] ← 시야 회전
└─→ [🖱️ UI] ← 클릭 / 버튼 / 메뉴
PlayerController 뇌 (명령 전달)
Character 몸뚱이 (처리 담당)
Possess() : Pawn에 '빙의'해서 조작 가능하게 만듦UnPossess() : 조작에서 분리됨 (예: 캐릭터 사망 시 관전 전환)📌 하지만 직접 처리는 거의 안 하고, 대부분 Pawn/캐릭터가 처리함!
1️⃣ C++ 클래스로 Player Controller 만든다 → SpartaPlayerController
GameMode에서 PlayerController 관리함
// GameMode.cpp
#include "SpartaGameMode.h"
#include "SpartaCharacter.h"
#include "SpartaPlayerController.h" // GameMode에서 PlayerController 관리함
ASpartaGameMode::ASpartaGameMode() // 생성자 정의
{
DefaultPawnClass = ASpartaCharacter::StaticClass();
PlayerControllerClass = ASpartaPlayerController::StaticClass(); // PlayerController 설정
}
2️⃣ 블루프린트 상속 → BP_SpartaPlayerController
BP로 만들고 GameMode에서 Player Controller Class 설정해줌

| ValueType | 설명 | 예시 |
|---|---|---|
Boolean | 참/거짓 입력 (토글/버튼 누름 등) | 점프, 공격, 인터랙션, 스프린트 |
Axis1D | 1차원 축 입력, 한 방향만 입력 가능 | 전진/후진, 가속 페달 → 자동차에서 많이 사용 |
Axis2D | 2차원 축 입력 (X, Y) | 이동 (WASD), 마우스 시점 회전, 조이스틱 |
Axis3D | 3차원 축 입력 (X, Y, Z) | 카메라 회전(3축), VR 컨트롤러, 비행 시뮬레이션 |
입력이 언제 활성화되게 할 것인가
근데 보통은 따로 지정 안해도 됨
| Trigger | 설명 |
|---|---|
None | 트리거 없음. 기본값. |
Chorded Action | 특정 키 조합(Chorded)이 눌릴 때만 작동. (ex. Ctrl + C) |
Combo (Beta) | 키 조합 연속으로 눌렀을 때 작동하는 실험적 트리거. |
Down | 키가 막 눌렸을 때 1회 작동. |
Hold | 키를 누르고 있는 동안 계속 작동. |
Hold And Release | 키를 누르고 있다가 뗄 때 작동. |
Pressed | 키가 한 번 눌렸을 때 작동. (Down과 비슷) |
Pulse | 누르고 있는 동안 주기적으로 반복 작동. |
Released | 키를 뗐을 때 1회 작동. |
Tap | 짧게 누르고 뗐을 때 작동. 길게 누르면 무시됨. |
| 🛠 Modifier 이름 | 📝 설명 |
|---|---|
| Dead Zone | 조이스틱처럼 민감한 입력 장치에서 작은 입력값을 무시하고 0으로 처리함 |
| FOV Scaling | 시야(Field of View)에 따라 입력을 조정함 (줌인/아웃 시 민감도 자동 조절) |
| Negate | 입력값의 부호를 반전시킴 (예: 1 → -1) |
| Response Curve – User Defined | 커스텀 곡선에 따라 입력값을 보정 (입력 곡선 편집기로 직접 설정 가능) |
| Scalar | 입력값에 특정 수치를 곱함 (예: X축 입력만 2배 빠르게 만들기) |
| Scale By Delta Time | 프레임율에 관계없이 일정한 속도로 입력을 적용하도록 보정 |
| Smooth | 입력값을 부드럽게 만들어 움직임을 자연스럽게 만듦 |
| Smooth Delta | 변화량(Δ값)을 기준으로 부드럽게 보정 |
| Swizzle Input Axis Values | 입력 축을 서로 바꾸거나 조합함 (예: X ↔ Y) |
| To World Space | 입력 벡터를 월드 좌표계 기준으로 변환 |


Y = 좌우, X = 앞뒤Y → X 축 바꿔줌 → 캐릭터가 앞으로 움직임❓ 언리얼 InputAction 값이 왜 반대로 나올까
키보드 입력 값 (WASD) 기준
W → InputValue: (X=0, Y=1)
D → InputValue: (X=1, Y=0)
| 방향 | 축 |
|---|---|
| 앞뒤 | X |
| 좌우 | Y |
| 위아래 | Z |
입력값의 Y가 "앞"인데, 언리얼은 X가 앞이니까 좌표축이 안 맞음!
➡ 축 스왑해서 매핑해줘야 함 ➡ Swizzle Input Axis Values
W ←→ S
A ←→ D
▶ Move의 IMC
▶ Jump의 IMC
▶ Sprint의 IMC
▶ Look의 IMC
❓ 왜 Y축만 부정(Negate)하는가
➡ 그래서 카메라 회전 방향이 직관적으로 맞게 하려면 Y축 입력을 부정(Negate) 해줘야 함
1️⃣ 활성화 해야 Player Controller가 IMC의 영향 아래 있게 됨
// SpartaPlayerController.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"
// 미리 선언
class UInputMappingContext;
class UInputAction;
UCLASS()
class SPARTAPROJECT_API ASpartaPlayerController : public APlayerController
{
GENERATED_BODY()
public:
ASpartaPlayerController(); // 생성자
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") //에디터에서 수정 가능하도록 설정
UInputMappingContext* InputMappingContext; // IMC를 할당해줄 ptr 변수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* SprintAction;
protected: // IMC 활성화 및 입력 액션 바인딩을 위한 함수들
virtual void BeginPlay() override; // 플레이 시작 시 호출되는 함수
// PlayerController가 생성된 직후에 IMC를 활성화
};
// SpartaPlayerController.cpp
#include "SpartaPlayerController.h"
#include "EnhancedInputSubsystems.h" // Enhanced Input Subsystem을 사용하기 위한 헤더
ASpartaPlayerController::ASpartaPlayerController() // 생성자 정의
: InputMappingContext(nullptr),
MoveAction(nullptr),
JumpAction(nullptr),
LookAction(nullptr),
SprintAction(nullptr) // BP에서 할당 할 거니까 여기선 nullptr로 초기화
{
}
// IMC 활성화
void ASpartaPlayerController::BeginPlay() // 플레이 시작 시 호출되는 함수
{
Super::BeginPlay(); // 부모 클래스의 BeginPlay 호출
if (ULocalPlayer* LocalPlayer = GetLocalPlayer()) // 로컬 플레이어 정보 가져오기
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>()) // EnhancedInputSubsystem을 관리하는 Subsystem 획득하기
{
if (InputMappingContext) // IMC가 할당되어 있는지 확인
{
Subsystem->AddMappingContext(InputMappingContext, 0); // 획득한 Subsystem에 IMC를 추가, 0은 최우선순위로 두고 활성화 시킴
}
}
}
}
2️⃣ BP_SpartaPlayerController에서 할당하기

3️⃣ 실행되는지 확인하기


실행하니까 플레이어 컨트롤러를 스폰하지 못했다..

World Settings에서 Player Controller Class BP로 만든 거 넣어주기!

그러면 IA랑 IMC로 만들어 놓은게 작동한다!
분명 예전에 배웠던 내용인데, 구현 순서를 정리하면서 다시 공부하니까 이해가 더 연결돼서 머리에 잘 들어오는 느낌이다.
그래도 아직은 헷갈리고 어려운 부분이 많아서 계속 해봐야겠다...