Chapter 10. Leveling, Abilities, and Saving Progress

In the previous chapter, we covered how to create and apply equipment to the player, which when equipped, affects the stats of the player. In this chapter, we will allow the player to level up by setting up an experience system for each party member, allowing party members to gain experience from enemies when winning in combat. When each party member gains enough experience, they will level up and their stats will increase at each level that the party member has gained. We will also fix the combat damage settings so that attacks in combat will utilize character stats rather than hardcoded values. Once we have fixed the combat logic, we will then move on to creating an ability for our character that will be activated by gaining a level.

In this chapter, we will cover the following topics:

  • XP and Leveling source code
  • Data Table starting values
  • Displaying levels and experience in the pause menu
  • Applying the correct damage in combat
  • Setting up the abilities array
  • Abilities logic
  • Saving
  • Loading

XP and leveling source code

In order to allow party members to gain experience points from battle, we need to add experience points (which we will call XP) variables to our code. Moreover, the XP variables need to accumulate to a given XP cap (which we will call MXP for maximum XP), and if this cap is hit, the player will gain a level. The best way to do this is to add these variables to our source code, which we will then apply to every party member and enemy that we have in the game. The first thing we will do is add XP and leveling data to our Data classes. Navigate to UnrealRPG | Source | Data and open FCharacterClassInfo.h. In the FCharacterClassInfo : public FTableRowBase struct, add UPROPERTY to XP that will hold cumulative experience, MXP that will hold the experience cap to the next level, and Lvl that will hold the party member's current level:

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ClassInfo")
  int32 XP;

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ClassInfo")
  int32 MXP;

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ClassInfo")
  int32 Lvl;

Next, open FEnemyInfo.h, which is located in the same folder as FCharacterClassInfo.h. We need to add XP to the enemy's info because each enemy will give a certain amount of XP to party members. In the FEnemyInfo : public FTableRowBase struct, add a UPROPERTY to XP:

UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "EnemyInfo")
  int32 XP;

We will now need to use these variables in the GameCharacter instances of the game. Navigate to UnrealRPG | Source and open GameCharacter.h. In the class RPG_API UGameCharacter : public UObject struct, add UPROPERTY to XP, MXP, and Lvl:

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

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

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

Open GameCharacter.cpp so that we set game character instances equal to the party member and enemy data. First, in UGameCharacter* UGameCharacter::CreateGameCharacter( FCharacterInfo* characterInfo, UObject* outer ), set the character's XP, MXP, and Lvl equal to the party member's data:

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;

    character->XP = character->ClassInfo->XP;

    character->MXP = character->ClassInfo->MXP;
    character->Lvl = character->ClassInfo->Lvl;
    character->isPlayer = true;
  }

  return character;
}

Next, set each instance of the enemy character's XP equal to the XP enemy data:

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

  character->CharacterName = enemyInfo->EnemyName;

  character->ClassInfo = nullptr;

  character->MHP = enemyInfo->MHP;
  character->MMP = 0;
  character->HP = enemyInfo->MHP;
  character->MP = 0;

  character->ATK = enemyInfo->ATK;
  character->DEF = enemyInfo->DEF;
  character->LUCK = enemyInfo->Luck;
  character->Gold = enemyInfo->Gold;
  character->XP = enemyInfo->XP;

  character->decisionMaker = new TestDecisionMaker();
  character->isPlayer = false;

  return character;
}

We can now add an XP framework to our combat engine. Open CombatEngine.h. Add XPTotal as a public variable:

public:
  int32 XPTotal;

The XPTotal will be responsible for holding the total amount of XP gained from battle if all of the enemies have perished.

At this point, let's use the XP variables that we created to calculate the amount of XP gained from battle. Open CombatEngine.cpp. In bool CombatEngine::Tick( float DeltaSeconds ), add XP to our check for victory section. To do this, we will set the local XP variable to 0, and for every enemy in the battle, we will accumulate the total amount of experience in the XP variable:

// check for victory
  deadCount = 0;
  int32 Gold = 0;
  int32 XP = 0;
  for( int i = 0; i < this->enemyParty.Num(); i++ )
  {
    if( this->enemyParty[i]->HP <= 0 ) deadCount++;
    Gold += this->enemyParty[i]->Gold;
    XP += this->enemyParty[i]->XP;
  }

If all of the party members have died, we will store the total XP of the enemies in our public XPTotal variable to be used outside this class:

// all enemies have died, switch to victory phase
  if( deadCount == this->enemyParty.Num() )
  {
    this->SetPhase( CombatPhase::CPHASE_Victory );
    GoldTotal = Gold;
    XPTotal = XP;
    return false;
  }

Lastly, we can add the XP gained to each party member in our game instance. To do this, open RPGGameMode.cpp. In void ARPGGameMode::Tick( float DeltaTime ), where we added a check to the victory phase, we will create a for loop. This for loop will cycle through every party member, and for each party member, we will set their current XP to be a cumulative of the XP gained from the battle:

for (int i = 0; i < gameInstance->PartyMembers.Num(); i++)
{
  gameInstance->PartyMembers[i]->XP += this->currentCombatInstance->XPTotal;
}

In this for loop, we can also check the current XP with the current XP cap for the level the player is currently at. If the current XP of the party member is more than or equal to MXP, the player will level up, gain increased base stats, and the XP cap to gain the next level (MXP) will increase:

if (gameInstance->PartyMembers[i]->XP >= gameInstance->PartyMembers[i]->MXP){
  gameInstance->PartyMembers[i]->Lvl++;
  gameInstance->PartyMembers[i]->MHP++;
  gameInstance->PartyMembers[i]->MMP++;
  gameInstance->PartyMembers[i]->ATK++;
  gameInstance->PartyMembers[i]->DEF++;
  gameInstance->PartyMembers[i]->LUCK++;
  gameInstance->PartyMembers[i]->MXP += gameInstance->PartyMembers[i]->MXP;
}

In this example, we kept our calculations simple by only allowing the stats to increase by one when the party member gains a level, and setting the cap to the next level to just be double of what the previous level was. If you like, you can come up with more complex calculations specific to your game here. Note that all the calculations used for differentiating stat numbers and for each party member can be done here.

When you are done, the victory condition will look like this:

else if( this->currentCombatInstance->phase == CombatPhase::CPHASE_Victory )
{
  UE_LOG( LogTemp, Log, TEXT( "Player wins combat" ) );
  URPGGameInstance* gameInstance = Cast<URPGGameInstance>(GetGameInstance());
  gameInstance->GameGold += this->currentCombatInstance->GoldTotal;

  for (int i = 0; i < gameInstance->PartyMembers.Num(); i++)
  {
    gameInstance->PartyMembers[i]->XP += this->currentCombatInstance->XPTotal;

    if (gameInstance->PartyMembers[i]->XP>= gameInstance->PartyMembers[i]->MXP){
      gameInstance->PartyMembers[i]->Lvl++;
      gameInstance->PartyMembers[i]->MHP++;
      gameInstance->PartyMembers[i]->MMP++;
      gameInstance->PartyMembers[i]->ATK++;
      gameInstance->PartyMembers[i]->DEF++;
      gameInstance->PartyMembers[i]->LUCK++;

      gameInstance->PartyMembers[i]->MXP +=gameInstance->PartyMembers[i]->MXP;
    }

  }

UGameplayStatics::GetPlayerController( GetWorld(), 0 )->SetActorTickEnabled( true );
}

At this point, you can compile your source code and restart/open your project in UE4.

We are now done with creating the framework for our experience system in our source code, and we can now move on to providing specific starting values for each of these in our game.

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

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