Party members

Before we can keep track of party members, we'll need a way to track a character's current state, such as how much HP the character has or what it has equipped.

To do this, we'll create a new class named GameCharacter. As usual, create a new class and pick Object as the parent class.

The header for this class looks like the following code snippet:

#pragma once

#include "Data/FCharacterInfo.h"
#include "Data/FCharacterClassInfo.h"

#include "GameCharacter.generated.h"

UCLASS( BlueprintType )
class RPG_API UGameCharacter : public UObject
{
  GENERATED_BODY()

public:
  FCharacterClassInfo* ClassInfo;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  FString CharacterName;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 MHP;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 MMP;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 HP;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 MP;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 ATK;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 DEF;

  UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = CharacterInfo )
  int32 LUCK;

public:
  static UGameCharacter* CreateGameCharacter( FCharacterInfo* characterInfo, UObject* outer );

public:
  void BeginDestroy() override;
};

For now, we're keeping track of the character's name, character's source class information, and character's current stats. Later, we will use the UCLASS and UPROPERTY macros to expose information to the Blueprint. We'll add other information later as we work on the combat system.

As for the .cpp file, it will look like this:

#include "RPG.h"
#include "GameCharacter.h"

UGameCharacter* UGameCharacter::CreateGameCharacter( FCharacterInfo* characterInfo, UObject* outer )
{
  UGameCharacter* character = NewObject<UGameCharacter>( outer );

  // locate character classes asset
  UDataTable* characterClasses = Cast<UDataTable>( StaticLoadObject( UDataTable::StaticClass(), NULL, TEXT( "DataTable'/Game/Data/CharacterClasses.CharacterClasses'" ) ) );

  if( characterClasses == NULL )
  {
    UE_LOG( LogTemp, Error, TEXT( "Character classes datatable not found!" ) );
  }
  else
  {
    character->CharacterName = characterInfo->Character_Name;
    FCharacterClassInfo* row = characterClasses->FindRow<FCharacterClassInfo>( *( characterInfo->Class_ID ), TEXT( "LookupCharacterClass" ) );
    character->ClassInfo = row;

    character->MHP = character->ClassInfo->StartMHP;
    character->MMP = character->ClassInfo->StartMMP;
    character->HP = character->MHP;
    character->MP = character->MMP;

    character->ATK = character->ClassInfo->StartATK;
    character->DEF = character->ClassInfo->StartDEF;
    character->LUCK = character->ClassInfo->StartLuck;
  }

  return character;
}

void UGameCharacter::BeginDestroy()
{
  Super::BeginDestroy();
}

The CreateGameCharacter factory for our UGameCharacter class takes a pointer to an FCharacterInfo struct, which is returned from a Data Table, and also an Outer object, which is passed to the NewObject function. It then attempts to find the character class Data Table from a path, and if the result is not null, it locates the proper row in the Data Table, stores the result, and also initializes the stats and the CharacterName field. In the preceding code, you can see the path where the character class Data Table is located. You can get this path by right-clicking on your Data Table from the Content Browser, selecting Copy Reference, and then pasting the result into your code.

While this is currently a very basic bare-bones representation of a character, it will work for now. Next, we're going to store a list of these characters as the current party.

The GameInstance class

We have already created a GameMode class, and this might seem like the perfect place to keep track of information such as party members and inventory, right?

However, GameMode does not persist between level loads! This means that unless you save some information to disk, you lose all of that data whenever you load a new area.

The GameInstance class was introduced to deal with just this sort of problem. A GameInstance class persists through the whole game, regardless of level loads, unlike GameMode. We're going to create a new GameInstance class to keep track of our persistent data, such as party members and inventory.

Create a new class, and this time, select GameInstance as the parent class (you'll have to search for it). Name it RPGGameInstance.

In the header file, we're going to add a TArray of the UGameCharacter pointers, a flag to know whether the game has been initialized, and an Init function. Your RPGGameInstance.h file should look like this:

#pragma once

#include "Engine/GameInstance.h"
#include "GameCharacter.h"
#include "RPGGameInstance.generated.h"
UCLASS()
class RPG_API URPGGameInstance : public UGameInstance
{
  GENERATED_BODY()

  URPGGameInstance( const class FObjectInitializer& ObjectInitializer );

public:
  TArray<UGameCharacter*> PartyMembers;

protected:
  bool isInitialized;

public:
  void Init();
};

In the Init function for the game instance, we'll add a single default party member and then set the isInitialized flag to true. Your RPGGameInstance.cpp should look like this:

#include "RPG.h"
#include "RPGGameInstance.h"
URPGGameInstance::URPGGameInstance(const class FObjectInitializer& 
ObjectInitializer)
: Super(ObjectInitializer)
{
  isInitialized = false;
}

void URPGGameInstance::Init()
{
  if( this->isInitialized ) return;

  this->isInitialized = true;

  // locate characters asset
  UDataTable* characters = Cast<UDataTable>( StaticLoadObject( UDataTable::StaticClass(), NULL, 
TEXT( "DataTable'/Game/Data/Characters.Characters'" ) ) );
        if( characters == NULL )
  {
    UE_LOG( LogTemp, Error, TEXT( "Characters data table not found!" ) );
      
    return;
  }

  // locate character
  FCharacterInfo* row = characters->FindRow<FCharacterInfo>( TEXT( "S1" ), TEXT( "LookupCharacterClass" ) );

  if( row == NULL )
  {
    UE_LOG( LogTemp, Error, TEXT( "Character ID 'S1' not found!" ) );
    return;
  }

  // add character to party
  this->PartyMembers.Add( UGameCharacter::CreateGameCharacter( row, this ) );
}

You may run into a linker error at this point if you try to compile. It is recommended that before you move on, save and close everything. Then restart your project. After you do that, compile the project.

To set this class as your GameInstance class, in Unreal, open Edit | Project Settings, go to Maps & Modes, scroll down to the Game Instance box, and pick RPGGameInstance from the drop-down list. Finally, from the game mode, we override BeginPlay to call this Init function.

Open RPGGameMode.h and add virtual void BeginPlay() override; at the end of your class so that your header will now look like this:

#pragma once

#include "GameFramework/GameMode.h"
#include "RPGGameMode.generated.h"

UCLASS()
class RPG_API ARPGGameMode : public AGameMode
{
  GENERATED_BODY()

  ARPGGameMode(const class FObjectInitializer& ObjectInitializer);
  virtual void BeginPlay() override;
};

And in RPGGameMode.cpp, cast RPGGameInstance at BeginPlay so that RPGGameMode.cpp now looks like this:

#include "RPG.h"
#include "RPGGameMode.h"
#include "RPGCharacter.h"
#include "RPGGameInstance.h"

ARPGGameMode::ARPGGameMode(const class FObjectInitializer& 
ObjectInitializer)
: Super(ObjectInitializer)
{
  DefaultPawnClass = ARPGCharacter::StaticClass();
   }

void ARPGGameMode::BeginPlay()
{
  Cast<URPGGameInstance>(GetGameInstance())->Init();
}

Once you compile the code, you now have a list of active party members. It's time to start prototyping the combat engine.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.119.81