캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

Unity

모바일 게임 구조 설계와 시스템 데이터 테이블 설계

Stage_Table.DB

Index : 스테이지를 구분하기 위한 구분자

ActGroupId : 스테이지 그룹을 지정하기 위한 ID

StageName : 스테이지 이름을 지정

IsMulti : 다수의 유저거 접속하여 플레이 가능한 스테이지 여부 확인

LimitMinLevel : 스테이지에 진입하기 위한 최소 레벨 제한을 지정

 -> 거의 대부분 0으로 설정 , 행돌역을 소모하기 때문에 별도 레벨 제한 지정하지 않는다.

LimitMaxLevel : 스테이지에 진입하기 위한 최대 레벨 제한을 지정

 -> 대부분 99로 설정, 행동력을 소모하기 때문에

BasePlayerCount : 스테이지에서 플레이가 가능한 유저 수를 지정

 -> 보스는 여러명 참여 가능하게 하도록 하기위해

LimitPlayCount : 스테이지를 제한 횟수만큼만 플레이가 가능하도록 지정

 -> 999로 설정, 무한 반복 플레이, 행동력 = 매출 이기 때문에

 -> 1일 제한 플레이를 설정

NeedStagedId : 해당 스테이지를 플레이하기 위해서 이전 n 스테이지를 클리어 하였는지 체크하는 기능

 -> 데이터 형식 : StageData_Table.DB/[Index]를 매칭하여 사용

IsBoss : Act 구룹의 최종 보스 스테이지 여부를 설정할 수 있는 기능.

ClearExp : 스테이지를 클리어 하였을 때, 고정적으로 획득할 수 있는 경험치를 설정할 수 있는 기능

 -> Min ~ Max 사이 설정

ClearGold : 스테이지를 클리어 하였을 때, 고정적으로 획득할 수 있는 ㄱ게임 머니를 설정할 수 있는 기능.

BaseResultId : 스테이지 클리어 하였을 때 고정적으로 획득할 수 있는 아이템설정

 -> Item_Table.DB/[Index] 를 매칭하여 사용

SelectResultId : 스테이지를 클리어 하였을 때, 여러개의 보물 상자가 등장하고 그 중에 한개를 선택하는 방식

NextStateId : 스테이지를 클리어 하면 게임결과에서 다음 스테이지를 연결해주는 기능

======================

NPC 가 스폰되어지는 2가지 방법

  1.  스테이지 NPC Data를 전체적 로딩하는 방식

  2. 스테이지 내 트리거를 이용하여 User가 진입시 스폰 시키는 방식.

=========================

Character_Table.DB

Index : Character을 구분하기 위한 유니크한 구분자

DisPlayName : 캐릭터 명을 설정할 수 있는 기능

CharKind : Character의 종류를 구분하기 위한 Type (Int)

 ->     1. Player

2. NPC

3. Monster

CharType : 캐릭터의 등급과 PC여부 그리고 트리거로 동작하는 캐릭터 인지를 구분하기 위한 Type

 ->     1. Player

2. Normal

3. Elite

4. EventBoss

5. HiddenBoss

6. FinalBoss

7. Trigger

AtkType : 캐릭터의 공격 타입이 무엇인지를 설정하는 컬럼

 ->      1. Melee

2. Range

3. Magic

CharLv : 캐릭터의 레벨을 설정할 수 있는 컬럼

DamMin : 캐릭터의 최소 공격력을 설정할 수 있는 기능

DamMax : 캐릭터의 최대 공격력

AtkDistance : 캐릭터가 공격할 수 있는 최소/최대 사거리

AtkSpeed : 캐릭터 공격 속도를 설정할 수 있는 컬럼 기능

MoveSpeed : 캐릭터의 이동 속도를 설정할 수 있는 기능

여러 저항들  %의 값들로 정해짐

ResistFire , ResistWater, ResistLightning, ResistPoison

 ** Array 배열 형태로 테이블을 구성할 경우 패킷 통신 속도가 빠르게 전달 할 수 있다.

=======================================

ItemData_Table.DB

Index : 아이템 구분

ItemName : 아이템 이름

ItemType : 아이템의 속성을 구분하기 위한 타입을 설정

 ->     0. ETC Item (일반적으로 아무런 기능이 없는 아이템)

1. 소모성 아이템

2. 장착형 아이템

3. 상자형 아이템

4. 캐쉬 아이템

GradeType : 아이템의 등급을 구분하기 위한

->      0. 일반

1. 매직

2. 레어

3. 에픽

4. 전설

5. 불멸

AtkType 아이템의 공격 타입을 구분하기 위한

->      0. 없음

1. 근접 공격

2. 원거리 공격

3. 마법 공격

EquipType : 장착형 아이템의 구분하기 위한 타입

->      0. 장착형 아이템이 아님

1. 무기

2. 보조 무기

3. 투구

4. 값옷

5. 장갑

6. 신발

7. 망토

8. 목걸이

9. 반지

ItemLv : 아이템을 장착 또는 사용하기 위한 레벨을 설정

EnchantLv : 아이템의 강화 레벨을 설정할 수 있는

NeedID : 아이템 강화시 필요한 재료 아이템을 설정 할 수 있는 기능

SuccessRate : 강화 성공확률을 설정할 수 있는 기능

  -> 실제로는 맥스 10000으로 5000이면 50%를 나타낸다 3655 면 36.55% 의미

DamMin 과 DamMax

CriticalRate : 크리티컬 확률

DamFir, DamWater, DamLightning, DamPoison

Atk_T_Point : 전투력을 측정하는 포인트

Desc : 아이템의 설명을 지정하는 컬럼

참고  http://blog.daum.net/_blog/BlogTypeView.do?blogid=0sGbU&articleno=24&_bloghome_menu=recenttext

캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

참고 자료

'이득우의 언리얼 C++ 게임 개발의 정석' Chapter 11

&

wergia.tistory.com/154

데이터 테이블을 사용하는 이유

게임 데이터를 파일로 따로 관리하면 체계적으로 관리할 수 있고, 더 편하게 사용할 수 있다.

나는 엑셀이 없어서 구글 스프레드시트를 사용했다.

CSV 파일 만들기

엑셀 파일 형식은 사용할 수 없고, CSV 파일 형식으로 변환해야 사용할 수 있다.

CSV 파일로 변환해주자.

캐릭터 데이터 테이블 - kaeligteo deiteo teibeul
캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

엑셀 파일의 1행을 비워놓아서, CSV 파일의 첫번째 줄에 의미 없는 (,,,,,)가 생겼다.

언리얼에서 오류가 날 수 있으니 지워주자.

캐릭터 데이터 테이블 - kaeligteo deiteo teibeul
캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

데이터 테이블 구조체 만들기

각 열의 이름과 유형이 동일한 구조체를 선언해야 한다.

구조체를 생성할 때, USTRUCT, GENERATED_BODY() 매크로를 사용해줘야 에디터 인터페이스에서 볼 수 있다.

CSV 파일에서 1열에 있는 데이터는 언리얼 엔진에서 자동으로 키 값으로 사용된다.

위의 예제에서는 Name이 키 값으로 사용된다.

데이터를 담을 구조체를 만든다.

구조체의 선언 위치는 관리하기 쉽게, CustomDataTables라는 더미 액터를 만들어서 선언해주었다.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Engine/DataTable.h" // 헤더 추가.
#include "CustomDataTables.generated.h"

USTRUCT(BlueprintType)
struct FABCharacterData : public FTableRowBase
{
	GENERATED_BODY()

	public:
	FABCharacterData() : Level(1), MaxHP(100.0f), Attack(10.0f), DropExp(10), NextExp(30) {}

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
	int32 Level;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
	float MaxHP;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
	float Attack;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
	int32 DropExp;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
	int32 NextExp;
};

UCLASS()
class ARENABATTLE_API ACustomDataTables : public AActor
{
	GENERATED_BODY()
};
CSV 파일 임포트하기

CSV 파일을 임포트한다.

캐릭터 데이터 테이블 - kaeligteo deiteo teibeul
캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

이전에 만들었던 구조체로 연결을 해주고, 적용을 누른다.

캐릭터 데이터 테이블 - kaeligteo deiteo teibeul
캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

데이터 테이블 로드하기

캐릭터 스텟 데이터는 변하지 않는 데이터이므로 보통 게임 앱이 초기화할 때 불러들인다.

게임 앱을 관리하기 위한 용도로 만들어진 게임 인스턴스를 사용해볼 것이다.

GameInstance를 부모클래스로 상속받아서 클래스를 만들고, 프로젝트 세팅에서 설정해준다.

캐릭터 데이터 테이블 - kaeligteo deiteo teibeul
캐릭터 데이터 테이블 - kaeligteo deiteo teibeul

헤더 파일

1. 데이터테이블 변수를 하나 만든다.

2. Getter 함수를 만들었다.

#pragma once

#include "ArenaBattle.h"
#include "Engine/GameInstance.h"
#include "ABGameInstance.generated.h"

struct FABCharacterData;
class UDataTable;

UCLASS()
class ARENABATTLE_API UABGameInstance : public UGameInstance
{
	GENERATED_BODY()
	
public:
	UABGameInstance();
	FABCharacterData* GetABCharacterData(int32 Level);

private:
	UPROPERTY()
	UDataTable* ABCharacterTable;
};

CPP 파일

1. 생성자에서 데이터 테이블 리소스를 로드한다.

2. 해당 Level의 데이터를 반환한다.

#include "ABGameInstance.h"
#include "CustomDataTables.h"

UABGameInstance::UABGameInstance()
{
	FString CharacterDataPath = TEXT("DataTable'/Game/Book/GameData/ABCharacterData.ABCharacterData'");
	static ConstructorHelpers::FObjectFinder<UDataTable> DT_ABCHARACTER(*CharacterDataPath);
	if (DT_ABCHARACTER.Succeeded())
	{
		ABCharacterTable = DT_ABCHARACTER.Object;
	}
}

FABCharacterData* UABGameInstance::GetABCharacterData(int32 Level)
{
	return ABCharacterTable->FindRow<FABCharacterData>(*FString::FromInt(Level), TEXT(""));
}
데이터 테이블 사용하기

GameInstance에 구현을 해놨으니까 가져다 쓰면 된다.

void AABCharacter::SetNewLevel(int32 NewLevel)
{
	UABGameInstance* ABGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	if (nullptr == ABGameInstance) return;

	CurrentStatData = ABGameInstance->GetABCharacterData(NewLevel);
	if (CurrentStatData)
	{
		Level = NewLevel;
		CurrentHP = CurrentStatData->MaxHP;
	}
	else
	{
		// 데이터 테이블에 없는 레벨일 때
		UE_LOG(LogClass, Warning, TEXT("Level %d data doesn't exist."), NewLevel);
	}
}