I already did this as part of the challenge a couple of videos ago.
You can use indexers to get an array element from a class, using this method does still use the nested for each loops but it is hidden.
[System.Serializable]
public SomeClassWithAnArray
{
[SerializeField] private float[] myArray;
public this[index] => levels[index];
}
For More details see:
Using indexers (C# Programming Guide)
To implement this in the Code from the course will look something like this:
Progression.cs
using UnityEngine;
namespace RPG.Stats
{
[CreateAssetMenu(fileName = "Progression", menuName = "Stats/New Progression", order = 0)]
public class Progression : ScriptableObject
{
[SerializeField] private ProgressionCharacterClass[] characterClasses;
public float this[CharacterClass cc, Stat stat, int level] => FindProgressionCharacterClassWithStat(cc, stat, level);
private float FindProgressionCharacterClassWithStat(CharacterClass cc, Stat stat, int level)
{
var ccFound = false;
var error = "";
foreach (ProgressionCharacterClass pcc in characterClasses)
{
if (pcc.CharacterClass != cc) continue;
try
{
return pcc[stat, level];
}
catch (System.ArgumentOutOfRangeException e)
{
error =
$"{(ccFound ? $"Multiple progressions for {cc} found:\n{error}\n" : "")}No {stat} found in {cc}\n{e}";
}
ccFound = true;
}
if (!ccFound)
throw new System.ArgumentOutOfRangeException(
nameof(characterClasses), $"No Progression {cc} found");
throw new System.ArgumentOutOfRangeException(nameof(characterClasses), error);
}
[System.Serializable]
class ProgressionCharacterClass
{
[SerializeField] private CharacterClass characterClass;
[SerializeField] private ProgressionStat[] stats;
public CharacterClass CharacterClass => characterClass;
public float this[Stat stat, int level] => FindStat(stat, level);
private float FindStat(Stat stat, int level)
{
foreach (ProgressionStat s in stats)
{
if (s.Stat == stat) return s[level];
}
throw new System.ArgumentOutOfRangeException(
nameof(stat), $"Progression Character Class does not contain a Stat of {stat}");
}
}
[System.Serializable]
class ProgressionStat
{
[SerializeField] private Stat stat;
[SerializeField] private float[] levels;
public Stat => Stat;
public int Length => temps.Length;
public this[index] => levels[index];
}
}
}
BaseStats.cs
using UnityEngine;
namespace RPG.Stats
{
public class BaseStats : MonoBehaviour
{
[SerializeField, Range(1, 99)] private int startingLevel = 1;
[SerializeField] private CharacterClass characterClass;
[SerializeField] private Progression progression;
public float GetStatValue(Stat stat)
{
try
{
return progression[characterClass, stat, int startingLevel - 1];
}
catch (System.Exception e)
{
Debug.LogWarning(e);
//TODO: add checks for different stats here to return a different values
return 0;
}
}
}
}
Any place that a stat is need use
GetComponent<BaseStats>().GetStatValue(Stat.Health)
GetComponent<BaseStats>().GetStatValue(Stat.ExperienceReward)
I am not using Levels for my progression in my code, I am using a Hybrid of the Progression Linear Formula and Progression Curve Formula concept that was introduced My take on the Progression SO