GameMode이해하기

1️⃣GameMode란?

  • GameMode는 게임 전반적인 규칙과 흐름을 총괄 관리하는 일종의 컨트롤 타워 역할을 하는 클래스
  • 싱글 플레이에선 '서버'와 '클라이언트' 개념이 나눠지지 않으므로, GameMode가 온전히 로컬에서 동작하여 게임 전체를 제어
  • 어떤 Pawn(또는 Character)을 플레이어에게 스폰해줄지, 어떤 PlayerController를 사용할지, 승패 조건이나 점수 계산 방법은 어떻게 설정할지 등 게임 플레이의 핵심 로직을 담당함
    • 프로젝트 전역(기본) 혹은 레벨별로 필요한 GameMode를 구분해 설정 가능

2️⃣GameMode의 주요 기능과 책임

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

GameMode 클래스 생성

1️⃣GameMode VS GameModeBase

  • GameMode
    • 언리얼에서 제공하는 멀티플레이 기능을 일부 포함하고, 싱글 플레이도 문제없이 사용 가능
    • 필요에 따라 GameState, PlayerState등 연동이 활성화 되어있음
  • GameModeBase
    • 좀 더 단순화된 형태로, 멀티 플레이 관련 로직이 거의 포함되어있지 않음
    • 간단한 싱글 플레이 게임 또는 직접 멀티플레이 로직을 구현하고 싶을 때 사용

2️⃣GameMode 생성 및 적용

  • GameMode Class를 생성해주고 클래스를 확인해보자
  • 아직은 아무런 것도 없을 것이다
  • 현재 Character가 없기 때문에 나중에 사용하기로 하고 적용을 시켜보기 위해 블루프린트로 만들어보자
  • 적용하는 방법은 크게 두가지이다
    1. 프로젝트 전역 설정

      •  Default GameMode를 우리가 만든 BP 클래스로 바꾸기
    2. 레벨별 설정
      • 현재 열려있는 레벨의 GameModeOverride를 BP 로 지정
      • 이렇게 하면 이 특정 레벨은 프로젝트 전역 설정 대신 BP를 사용하게 됨
  • 만약 프로젝트 전역 설정과 레벨별 설정이 충돌한다면, 레벨별 설정이 우선 적용됨

3️⃣GameMode 기본 설정 확인

  • WorldSettings에 GameMode를 살펴보자
    • DefaultPawn Class : 레벨 시작 시 플레이어가 조종하게 될 Pawn(또는 Character) 클래스를 지정
    • HUD Class : 게임 화면에 표시되는 HUD(UI) 요소를 관리하는 클래스를 지정
    • Player Controller Class : 플레이어 입력 처리와 카메라 제어, UI와의 상호작용을 담당하는 클래스를 지정
    • GameState Class/Player State Class : 플레이어 입력 처리와 카메라 제어, UI 와의 상호작용을 담당하는 클래스 지정
    • Spectator Class : 관전 모드에서 사용할 Pawn Class를 지정함, 일반 플레이어와 다른 이동 방식이나 카메라 제어가 필요할 때 활용

Pawn과 Chartacter 클래스 이해하기

1️⃣Pawn Class란?

  • Pawn은 플레이어 혹은 AI가 "소유(Possess)" 할 수 있는 가장 상위 클래스
  • 엔진에서 무언가를 조종한다 할 때 기본 형태가 Pawn이 됨
  • Pawn은 이동, 충돌, 중력, 네트워크 이동을 위한 기능이 포함되어 있지 않음
    • 보행(걷기, 달리기, 점프 등)에 필요한 시스템(캡슐 콜리전, 중력, 지형 따라가기)을 모든 단계에서 직접 구현해야 하므로, 사람 캐릭터를 처음부터 Pawn으로 만들기에는 부담이 큼
  • 이러한 특성 때문에 비행기, 드론, 카메라처럼 기존 Character의 이동 방식을 벗어난 특수한 로직을 완전 자유롭게 구현할 때 유용

2️⃣Character Class 란?

  • Character는 Pawn을 상속받아 만들어진 자식 클래스 중 하나로, 기본적으로 UCharacterMovementComponent를 포함하고 있음
    • 이동, 회전, 점프, 중력, 지형 따라가기, 네트워크 동기화 등 보행형 캐릭터에게 필요한 기능이 이미 구현되어 있어, 사람이 달리고 점프하는 형태의 캐릭터를 쉽게 만들 수 있음
    • 여기 미리 정의된 대표적인 함수들이 존재하므로, 몇 줄의 코드만으로 금방 캐릭터의 움직임을 구현 가능
  • 캐릭터를 구성하는 전형적인 요소들이 표준화되어 있어, 일반적인 인간형 캐릭터를 만드는ㄴ데 최적화되어 있음
    • 자동차나 비행기처럼 완전 다른 이동 방식을 구현할 땐 Character 내부에 탑재된 기능이 방해될 수 있어 Pawn을 상속받아 직접 구현하는 것을 고려해야함

Character Class 생성

1️⃣생성

  • Character Class를 상속받아 생성 후 확인해보면 여러 함수가 구현되어 있는 것을 볼 수 있다

2️⃣구조 살펴보기

  • 만들어진 Class를 BP를 만들어 확인해보면 왼쪽 컴포넌트 트리에 이미 여러개의 컴포넌트가 포함된 것을 볼 수 있다

    • CapsuleComponent(Root Component)

      • 캐릭터가 벽이나 지형에 충돌하는 범위를 정의하는 콜리전 컴포넌트
      • 캡슐 형태이며, Radius와 HalfHeight를 조정해 캐릭터의 물리적 크기를 설정 가능
    • ArrowComponent
      • 캐릭터가 어느 방향을 바라보고 있는지 표시하기 위한 씬에 화살표를 띄어주는 컴포넌트
      • 게임 플레이 로직에는 직접 영향을 주지 않고, 주로 편집기에 시각적 디버깅용으로 사용
    • SkeletalMeshComponent
      • 캐릭터의 3D 모델과 애니메이션을 적용하는 컴포넌트
      • Skeletal Mesh, Anim Blueprint등을 여기로 할당해 캐릭터의 외형과 동작을 제어
    • CharacterMovementCOmponent
      • 캐릭터의 이동, 점프, 중력, 네트워크 동기화등 물리적 이동 로직을 담당하는 핵심 컴포넌트
      • 언리얼에서 제공하는 주요 이동 함수가 이미 연결되어있어 최소한의 코드만으로 캐릭터 조작을 구현할 수 있음

Skeletal Mesh 설정

1️⃣Skeletal Mesh 설정

  • Skeletal Mesh란?
    • 내부에 뼈대(Skeleton)를 갖춘 3D 모델
      • 이 뼈대는 부모-자식 관계로 연겨로디어 있어, 본이 움직이면 외형도 움직임
    • 본과 Mesh가 연동되어 있어, 애니메이션에 맞춰 캐릭터가 뛰거나 걷는 동작을 구현할 수 있음
    • 언리얼 엔진은 물리 엔진과도 연결 가능해 Ragdoll 효과 등 물리기반 애니메이션 구현도 쉽게 가능
  • SkeletalMesh 설정
    • Character BP에 컴포넌트 트리에 Mesh 컴포넌트를 선택한 후 우측 Details 패널에 Skeletal Mesh 항목에 Mesh 할당

2️⃣메시 위치 및 방향 조정

  • 메시 방향 조정

    • 언리얼에선 캐릭터의 전방 방향은 X축임
    • 그러나 모델에 따라 Y축을 전방으로 사용하기 때문에 옆을 보는 것 처럼 보일 수 있음
    • 이를 해결하기 위해 Mesh Component 선택 후 우측 Details 패널에 Rotation을 변경하면 됨
  • 캡슐 크기와 메쉬 위치 조정
    • Character Class는 최상위 루트로 CapsuleComponent를 사용함
    • 그 내부 중앙에 SkeletalMesh가 위치하게 하기위해 Mesh의 발이 캡슐의 바닥에 정확히 일치하게 해야함
    • 캡슐의 충돌 범위가 작으면 충돌에 문제가 발생할 수 있으므로 CapsuleComponent의 Half Height, Radius 값을 조절하면 됨
  • 코드로 변경
    • ACharacter Class에 이미 구현된 Mesh Class는 private으로 되어있기 때문에 직접 가져다 쓰진 못하지만 내부에 Getter 함수가 구현되어 있어 그 함수를 이용하면 된다
ACCharacter::ACCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
	
	static ConstructorHelpers::FObjectFinder<USkeletalMesh> asset(L"/Script/Engine.SkeletalMesh'/Game/Resources/Characters/Meshes/SKM_Manny.SKM_Manny'");
	if (asset.Succeeded() == true)
		GetMesh()->SetSkeletalMesh(asset.Object);
	GetMesh()->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
	GetMesh()->SetRelativeLocation(FVector(0.0f, 0.0f, -90.0f));
}

카메라 및 GameMode 설정

  • 3인칭 게임이선 플레이어가 캐릭터의 뒷쪽이나 어깨 너머 시점을 보며 이동하는 경우가 많아 이를 구현하기 위해 SpringArm, CameraComponent를 추가 후 GmaMode 설정을 통해 우리가 만든 캐릭터가 기본으로 Spawn되도록 만들어 볼 것이다

1️⃣SpringArm 및 CameraComponent 추가

class UCameraComponent;
class USpringArmComponent;

UCLASS()
class SPARTAPROJECT_API ACCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ACCharacter();

public:
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	USpringArmComponent* SpringArm;
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	UCameraComponent* Camera;
};

.cpp

ACCharacter::ACCharacter()
{
	//SprintArm Setting
	{
		SpringArm = CreateDefaultSubobject<USpringArmComponent>("SpringArm");
		SpringArm->SetupAttachment(RootComponent);
		SpringArm->TargetArmLength = 300.0f;
		SpringArm->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f));
		SpringArm->bUsePawnControlRotation = true;
	}
	//Camera setting	
	{
		Camera = CreateDefaultSubobject<UCameraComponent>("Camera");
		Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
		Camera->bUsePawnControlRotation = false;
	}

	//Mesh 설정
	{
		static ConstructorHelpers::FObjectFinder<USkeletalMesh> asset(L"/Script/Engine.SkeletalMesh'/Game/Resources/Characters/Meshes/SKM_Manny.SKM_Manny'");
		if (asset.Succeeded() == true)
			GetMesh()->SetSkeletalMesh(asset.Object);
		GetMesh()->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
		GetMesh()->SetRelativeLocation(FVector(0.0f, 0.0f, -90.0f));
	}
}
  • VisibleAnywhere, BlueprintReadOnly: 블루프린트에서 보기만 가능하고, C++ 코드에서 수정 가능하게 만드는 속성
  • ForwardDeclaration(전방선언) : 헤더 파일의 의존성을 줄이는 방법
  • SpringArmComponent
    • SpringArmComponent(카메라 붐)
    • 캐릭터와 카메라 간의 거리를 유지하고 충돌시 카메라가 벽등에 박히지 않도록 위치를 자동 조정
  • CameraComponent
    • 실제 화면에 표시되는 카메라 컴포넌트
    • 위치와 회전을 제어하면 게임 뷰가 변경됨
  • bUsePawnControlRotation
    • 플레이어가 회전하면 Controller의 회전 값이 변경되어 이를 따라 자연스럽게 회전을 할지 말지를 결정하도록 하는 bool형 변수
  • 문제가 없다면 컴포넌트 트리와 Viewport에 적용이 잘 된것을 볼 수 있다

 

2️⃣GameMode에서 DefaultPawn Class 설정

  • DefaultPawnClass는 "게임 시작 시 어떤 캐릭터(Pawn)를 플레이어에게 제공할지 결정하는 것
UCLASS()
class SPARTAPROJECT_API ACGameMode : public AGameMode
{
	GENERATED_BODY()
public:
	ACGameMode();
};

.cpp

ACGameMode::ACGameMode()
{
	DefaultPawnClass = ACCharacter::StaticClass();
}
  • StaticClass : 객체 인스턴스를 사용하지 않고 클래스 자체의 정보를 다룰 수 있게 해주며, 주로 동적 객체 생성이나 타입 체크에 사용됨
  • 이렇게 하면 게임 시작 시 Character가 자동 Spawn되고 PlayerController와 연결되어 플레이어가 조작할 수 있게 됨

4️⃣구현 테스트