본문 바로가기

게임 개발/이득우의 언리얼 C++

[이득우의 언리얼 C++ 게임개발의 정석] #14. 게임플레이의 제작

반응형

이득우의-언리얼-C++-게임개발의-정석-표지

여태까지 프로토타입을 만들었다면,
앞으로 실제 게임으로 확장할 때 필요한 기능을 채워 넣어야 한다.

플레이어 스테이트, 게임 스테이트에 대해 학습하고
이들을 바탕으로 데이터를 체계적으로 관리하도록 게임 기능을 구현한다.

<1>. 캐릭터의 스테이트 설정

플레이어 캐릭터, AI 캐릭터의 기능을 체계적으로 관리하기 위해 캐릭터에도 스테이트 머신 모델을 구현한다.

  • PREINIT 스테이트 : 캐릭터 생성 전의 스테이트. 기본 캐릭터 에셋이 설정돼 있지만 캐릭터, UI를 숨긴다. 데미지를 입지 않는다.
  • LOADING 스테이트 : 선택한 캐릭터 에셋을 로딩하는 스테이트. 게임이 시작된 시점이므로, 현재 조종하는 컨트롤러가 AI인지 플레이어인지 구분하고 플레이어인 경우 에셋 로딩이 완료될 때까지 조종하지 못하게 입력을 비활성화함.
  • READY 스테이트 : 캐릭터 에셋 로딩이 완료된 스테이트. 숨겨져 있던 캐릭터와 UI를 보여주고 공격을 받으면 데미지를 입는다. 플레이어 컨트롤러로 캐릭터를 조종할 수 있으며 AI 컨트롤러는 비헤이비어 트리 로직을 구동해 캐릭터를 동작시킨다.
  • DEAD 스테이트 : 캐릭터가 사망할 때 발생하는 스테이트. DEAD Animation 재생 후 UI 끄기. 충돌 기능 없애고 데미지를 입지 않도록 하기. 플레이어 컨트롤러 입력 비활성화, AI 컨트롤러 비헤이비어 트리 로직 중지. 일정 시간 후 레벨 재시작.

하나의 캐릭터를 플레이어, NPC가 공유하기 때문에 이러한 환경에서 캐릭터는 자신이 플레이어인지 NPC인지 판별할 수 있어야 한다. 이를 확실히 파악할 수 있는 시점은 BeginPlay이다. 

 

※ 캐릭터의 스테이트 구성

  1. PREINIT 스테이트 시작. 게임 시작 후 BeginPlay 호출 시, 플레이어인지 NPC인지 파악해 IsPlayer 변수에 저장. 그에 따라 캐릭터 에셋 로딩 시작하고 스테이트를 LOADING으로 변경.
  2. 로딩이 완료될 때까지 LOADING, 로딩 완료 후 READY 스테이트로 변경. 활동 중 HP가 0 이하가 되면 DEAD 스테이트로 변경.

※ 스테이트 구성이 끝났으니, 스테이트에 맞게 비헤이비어 트리 로직을 수동으로 구동, 중지할 수 있게 AI 컨트롤러의 구조를 변경하자.

<2>. 플레이어 데이터와 UI 연동

전에는 스탯 정보를 액터 컴포넌트로 분리해 구현했다. 이번엔 플레이어의 게임 데이터(ex. 점수)를 별도의 액터에서 보관하도록 구현해 본다. ('PlayerState 클래스' : 플레이어의 정보를 관리하기 위한 용도)

 

※ 'ABPlayerState' 클래스를 게임 모드의 PlayerStatClass 속성에 지정한다.

→ 플레이어 컨트롤러의 구성을 완료하는 시점은 게임 모드의 'PostLogin' 함수이므로 이때 함께 ABPlayerState도 초기화해 준다.

 

//ABGameMode.cpp

#include "ABPlayerState.h"

AABGameMode::AABGameMode()
{
	...
    PlayerStateClass = AABPlayerState::StaticClass();
}

void AABGameMode::PostLogin(APlayerController* NewPlayer)
{
	...
   	auto ABPlayerState = Cast<AABPlayerState>(NewPlayer->PlayerState);
    ABPlayerState->InitPlayerData();
}

 

플레이어 데이터를 설정했으니, 이를 시각적으로 표현해 주는 HUD를 화면에 부착해 보자.

(UserWidget을 기본 클래스로 하는 'ABHUDWidget' 클래스 생성 후 HUD의 부모 클래스로 설정)

→ 플레이어 컨트롤러에서 위젯 생성 후 이를 화면에 띄워준다. (CreateWidget 함수 사용)

위젯-생성-화면

 

 

※ 플레이어 스테이트, 캐릭터 스탯 컴포넌트 정보를 HUD에 연동해야 한다.

→ 플레이어 컨트롤러에서 HUD와 스테이트를 연결하고, 캐릭터에서 HUD와 스탯 컴포넌트를 연결한다.

 

NPC가 사망하면 NPC 레벨에 지정된 경험치를 플레이어에게 전달하고 이를 축적해 레벨 업하는 기능을 구현해 보자.

  • 캐릭터 스탯에 NPC를 위한 경험치 값 설정
  • 플레이어 스테이트에 플레이어 경험치 데이터 보관하도록 설계 확장.
  • NPC 사망 시 플레이어에게 죽는지 검사하고, 플레이어 컨트롤러를 통해 플레이어 스테이트 업데이트. (데미지 프레임워크에서 플레이어 컨트롤러 = 가해자(Instigator))

<3>. 게임 데이터의 관리

UI 오른쪽 상단에 위치한 게임 스코어 부분을 구현해 보자.

멀티 플레이를 고려한다면, '플레이어에 설정된 데이터' 외에도 '게임의 데이터'를 관리하는 기능을 추가로 고려해야 한다.

→ 이를 관리하도록 '게임 스테이트 클래스'가 제공된다. 

 

※섹션 액터의 로직

  • NPC가 제거되면 이를 감지해 섹션 액터의 스테이트를 COMPLETE로 변경하는 기능 추가.
  • NPC가 제거될 때 막 타를 친 컨트롤러의 기록은 LastHitBy 속성에 저장. (위에서 구현한 Instigator를 검사하는 방식보다 효율적이다.)

※ 섹션을 클리어하면 '게임 모드'에게 스코어를 올리라는 명령을 내린다. 여기서 LastHitBy에 저장된 플레이어 컨트롤러 정보를 함께 넘겨서 해당 '플레이어 스테이트'의 스코어를 높이고, '게임 스테이트'의 스코어도 같이 올린다.

 

 

[이득우의 언리얼 C++ 게임개발의 정석] #15. 게임의 완성

완전한 게임 제품으로 발전하려면게임의 시작, 게임의 종료, 미션 달성, 게임 데이터의 저장 및 로딩 등의 기능이 필요하다.이를 구현하기 위해 알아두면 좋은 언리얼 엔진의 기능을 학습한다..

lbto.tistory.com

 

반응형