Search Unity

Question Creating a character stat system with SOs

Discussion in 'Scripting' started by dannyryba, Aug 5, 2020.

  1. dannyryba

    dannyryba

    Joined:
    Jun 22, 2020
    Posts:
    45
    Hi everyone,

    So I am looking at making an RPG game and I am trying to figure out the best way to do stats for character, coupling it with a job system.
    Each character in the party will have a class and of course stats, and only 4 characters will be active in a party at once, with other members not in the active party receiving less XP.
    However, I feel like I may have gotten confused here and that using a class and ScriptableObject in the way I have will not work properly.

    Here is what I have:
    A script that defines the Base Stat
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class BaseStat
    6. {
    7.     public List<StatBonus> AdditiveModifiers { get; set; }
    8.     public int BaseValue { get; set; }
    9.     public string StatName { get; set; }
    10.     public string StatDescription { get; set; }
    11.     public int FinalValue {get; set;}
    12.  
    13.     public BaseStat(int baseValue, string statName, string statDescription)
    14.     {
    15.         AdditiveModifiers = new List<StatBonus>();
    16.         BaseValue = baseValue;
    17.         StatName = statName;
    18.         StatDescription = statDescription;
    19.     }
    20.  
    21.     public void AddStatBonus(StatBonus statBonus)
    22.     {
    23.         AdditiveModifiers.Add(statBonus);
    24.     }
    25.  
    26.     public void RemoveStateBonus(StatBonus statBonus)
    27.     {
    28.         AdditiveModifiers.Remove(statBonus);
    29.     }
    30.  
    31.     public int CalculateFinalValue()
    32.     {
    33.         FinalValue = BaseValue;
    34.         AdditiveModifiers.ForEach(x => FinalValue += x.BonusValueAdd);
    35.         return FinalValue;
    36.     }
    37. }

    A script that defines a StatBonus (this is additive only ATM, but eventually I will want to be able to do multiplicative bonuses as well):
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class StatBonus
    6. {
    7.     public int BonusValueAdd { get; set; }
    8.  
    9.     public StatBonus(int bonusValue)
    10.     {
    11.         BonusValueAdd = bonusValue;
    12.     }
    13.  
    14. }
    And an SO that controls the character's job, adding the Base Stats as part of it (sorry for the length):

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor.Experimental;
    4. using UnityEngine;
    5.  
    6. [CreateAssetMenu(fileName = "New Job", menuName = "ScriptableObjects/Job")]
    7. public class CharacterJobs_SO : ScriptableObject
    8. {
    9.  
    10.     public List<BaseStat> stats = new List<BaseStat>();
    11.  
    12.     #region Basic Information
    13.  
    14.     public new string name = "Job Name";
    15.  
    16.     public int currentLevel = 1;
    17.  
    18.     public int maxLevel = 99;
    19.  
    20.     private int currentExp = 0;
    21.  
    22.     public int[] expNext;
    23.  
    24.     public float growthRate;
    25.  
    26.     public int baseExp;
    27.     [SerializeField]
    28.     private Sprite jobIcon = null;
    29.     [SerializeField]
    30.     [TextArea(3, 5)]
    31.     private string description = "Description";
    32.     [SerializeField]
    33.     [TextArea(1, 3)]
    34.     private string passive = "Passive ability description";
    35.     [SerializeField]
    36.     private int health;
    37.     [SerializeField]
    38.     private int currentHp;
    39.     [SerializeField]
    40.     private int energy;
    41.     [SerializeField]
    42.     private int currentEn;
    43.     [SerializeField]
    44.     private int strength;
    45.     [SerializeField]
    46.     private int defense;
    47.     [SerializeField]
    48.     private int power;
    49.     [SerializeField]
    50.     private int resistance;
    51.     [SerializeField]
    52.     private int agility;
    53.     [SerializeField]
    54.     private int luck;
    55.     [SerializeField]
    56.     private bool noClassAdvance = false;
    57.     #endregion
    58.  
    59.     void Awake()
    60.     {
    61.         stats.Add(new BaseStat(strength, "Strength", "Determines damage from physical abilities."));
    62.         stats.Add(new BaseStat(defense, "Defense", "Determines damage prevention from physical abilities."));
    63.         stats.Add(new BaseStat(power, "Power", "Determines damage from magical abilities."));
    64.         stats.Add(new BaseStat(resistance, "Resistance", "Determines damage prevention from magical abilities."));
    65.         stats.Add(new BaseStat(agility, "Agility", "Determines damage and damage prevention from gun abilities."));
    66.         stats.Add(new BaseStat(luck, "Luck", "Determines critical chance, dodge chance, and item drop chance."));
    67.  
    68.         currentHp = health;
    69.         currentEn = energy;
    70.  
    71.         expNext = new int[maxLevel];
    72.         expNext[1] = baseExp;
    73.  
    74.         for (int i = 2; i < expNext.Length; i++)
    75.         {
    76.             expNext[i] = Mathf.FloorToInt(expNext[i - 1] * growthRate);
    77.         }
    78.     }
    79. }
    80.  


    My idea is that by separating things like this, other events or objects in the game could affect the stats by passing in StatBonuses or XP. However, I am not sure the easiest way to do this (or if I even can with this system). I'm hoping someone can help me understand how I can use or alter what I have here to possibly increase stats from sources (such as equipment) and pass in XP from sources (such as battles). It made sense in my head but now that I have written the code, I have run some confusion on how to access these variables without needing to create a ton of unnecessary references or Singletons.

    edit: Also, my original idea was to use an array for the stats in the SO instead of a list...but I could not figure out if there was a way to make an array of the BaseStat class type while also defining the stats that need to be in that array. Any suggestions on that end (in case it makes more sense than a list) would be helpful as well :)

    Thanks in advance for any help on this. I am new to scripting and so perhaps it is something simple I am merely overlooking.
     
    Last edited: Aug 5, 2020
  2. dannyryba

    dannyryba

    Joined:
    Jun 22, 2020
    Posts:
    45
    Bumping this to hopefully get some help.

    In reviewing the code a bit more, it seems like I would ideally put an AddExp and LevelUp method in the Scriptable Object as well. However, my stopping point becomes how to call this method for just one job? For example, if I wanted only one character to gain exp? Could I do a call like MainCharacter.Job.AddExp(30)?

    Same for increasing stats...I am unsure if doing something like MainCharacter.Job.stats[3].AddStatBonus(5) is even possible...and if so, if this is the most elegant way to do it.