Search Unity

Question Converting Enum to another Type

Discussion in 'Scripting' started by Cornysam, Oct 27, 2022.

  1. Cornysam

    Cornysam

    Joined:
    Feb 8, 2018
    Posts:
    1,465
    Greetings,

    I'm all sorts of confused and i feel like this should be a simple fix but me no think good right now.

    I have a class that has a bunch of Enums for my RPG. I also have player stats on my player class that are ints or floats. I followed one tutorial series 90% of the way and needed the other for random item generation. I am struggling to convert the new process of using Enums to my existing that just has int/float variables listed out and used as properties.

    Basically, through my random item generator, it can have from 1-5 stats, kinda PoE/D2. I finally got this working but the stats on the item don't actually increase my existing player stats. Aka, Endurance should increase my player's max health, int mana, etc.
    upload_2022-10-26_19-2-7.png

    I feel like i am missing something quite simple but i hardly work with Enums unless they are for States.

    Code below:

    Enum
    Code (CSharp):
    1. public enum Stat { Strength, Endurance, Intelligence, Agility, Life, Mana, LifeRegen, ManaRegen, CritcalHitChance, PhysicalResistance,
    2.         FireResistance, IceResistance, LightningResistance, MoveSpeed, MagicFind, AttackPower, SpellPower, NoAffix };
    Equipment class
    Code (CSharp):
    1. [SerializeField] private Enums.EquipmentSlot equipmentSlot;
    2.     [SerializeField] private int intellect;
    3.     [SerializeField] private int strength;
    4.     [SerializeField] private int endurance;
    5.     [SerializeField] private int agility;
    6.  
    7.     [SerializeField] private Sprite visual;
    8.  
    9.     public Enums.Tier tier;
    10.     public Enums.Stat prefix = Enums.Stat.NoAffix;
    11.     public float prefixValue;
    12.     public Enums.Stat suffix = Enums.Stat.NoAffix;
    13.     public float suffixValue;
    14.     public Enums.Stat property1 = Enums.Stat.NoAffix;
    15.     public float property1Value;
    16.     public Enums.Stat property2 = Enums.Stat.NoAffix;
    17.     public float property2Value;
    18.     public Enums.Stat property3 = Enums.Stat.NoAffix;
    19.     public float property3Value;
    20.     public int newSuffix;
    21.     public int newSuffixValue;
    22.  
    23.     internal Enums.EquipmentSlot MyEquipmentSlot { get => equipmentSlot; }
    24.     public int MyIntellect { get => intellect; set => intellect = value; }
    25.     public int MyStrength { get => strength; set => strength = value; }
    26.     public int MyEndurance { get => endurance; set => endurance = value; }
    27.     public int MyAgility { get => agility; set => agility = value; }
    28.     public Sprite MyVisual { get => visual; set => visual = value; }
    29.  
    30.     private void Awake()
    31.     {
    32.         int rand = Random.Range(1, 95);
    33.         GenerateEquipment.instance.RandomizeStats(this, rand);
    34.     }
    35.  
    36.     public void Equip()
    37.     {
    38.         CharacterPanel.instance.EquipItem(this);
    39.     }
    Random Item Gen class
    Code (CSharp):
    1. public Equipment RandomizeStats(Equipment e, int iLvl)
    2.     {
    3.         Equipment newEquipment = e;
    4.         //Equipment newEquipment = ScriptableObject.CreateInstance<Equipment>();
    5.         newEquipment.MyILevel = iLvl;
    6.         newEquipment.tier = SelectTier(iLvl);
    7.         newEquipment.MyItemRarity = SelectRarity();
    8.         AddAffixes(newEquipment);
    9.         AddAffixValues(newEquipment);
    10.         return newEquipment;
    11.     }
    Code (CSharp):
    1. private void AddAffixValues(Equipment e)
    2.     {
    3.         if(e.prefix != Enums.Stat.NoAffix) { e.prefixValue = GetAffixValue(e.prefix, e.tier); }
    4.         if (e.suffix != Enums.Stat.NoAffix) { e.suffixValue = GetAffixValue(e.suffix, e.tier); }
    5.         if (e.property1 != Enums.Stat.NoAffix) { e.property1Value = GetAffixValue(e.property1, e.tier); }
    6.         if (e.property2 != Enums.Stat.NoAffix) { e.property2Value = GetAffixValue(e.property2, e.tier); }
    7.         if (e.property3 != Enums.Stat.NoAffix) { e.property3Value = GetAffixValue(e.property3, e.tier); }
    8.     }
    9.  
    10.     private float GetAffixValue(Enums.Stat stat, Enums.Tier tier)
    11.     {
    12.         float statValue;
    13.  
    14.         switch(stat)
    15.         {
    16.             case Enums.Stat.Strength: case Enums.Stat.Endurance: case Enums.Stat.Intelligence: case Enums.Stat.Agility:
    17.             case Enums.Stat.Life: case Enums.Stat.Mana:
    18.                 switch(tier)
    19.                 {
    20.                     case Enums.Tier.Tier1:
    21.                         statValue = Random.Range(StatArrays.attributeMinMax[0], StatArrays.attributeMinMax[1]); break;
    22.                     case Enums.Tier.Tier2:
    23.                         statValue = Random.Range(StatArrays.attributeMinMax[2], StatArrays.attributeMinMax[3]); break;
    24.                     case Enums.Tier.Tier3:
    25.                         statValue = Random.Range(StatArrays.attributeMinMax[4], StatArrays.attributeMinMax[5]); break;
    26.                     case Enums.Tier.Tier4:
    27.                         statValue = Random.Range(StatArrays.attributeMinMax[6], StatArrays.attributeMinMax[7]); break;
    28.                     case Enums.Tier.Tier5:
    29.                         statValue = Random.Range(StatArrays.attributeMinMax[8], StatArrays.attributeMinMax[9]); break;
    30.                     case Enums.Tier.Tier6:
    31.                         statValue = Random.Range(StatArrays.attributeMinMax[10], StatArrays.attributeMinMax[11]); break;
    32.                     default: statValue = 50; break;
    33.  
    34.                 }
    35.                 break;
    This function is much larger, but is just a bunch of switch/case statements.

    And finally the PlayerStats class
    Code (CSharp):
    1. private int intellect;
    2.     private int strength;
    3.     private int agility;
    4.     private int endurance;
    5.  
    6. public int MyEndurance { get => endurance; set => endurance = value; }
    7.     public int MyIntellect { get => intellect; set => intellect = value; }
    8.     public int MyAgility { get => agility; set => agility = value; }
    9.     public int MyStrength { get => strength; set => strength = value; }
    10.  
    11. private void Awake()
    12.     {
    13.         SetStartingValues();
    14.     }
    15.  
    16. public void SetStartingValues()
    17.     {
    18.         jumpForce = 22;
    19.         //attackDamage = 1f;
    20.         moveSpeed = 7;
    21.         jumpCount = 1;
    22.         //playerMaxHealth = 100f;
    23.         //playerMaxMana = 100f;
    24.  
    25.         MyCastRate = 1.0f;
    26.         MyIntellect = 10;
    27.         MyStrength = 10;
    28.         MyAgility = 10;
    29.         MyEndurance = 10;
    30.         MyMagicFind = 0.05f;
    31.         ResetStats();
    32.         UIManager.instance.UpdateCharacterStatText(strength, endurance, intellect, agility);
    33.         //CalculateFinalDamage();
    34.     }
    35.  
    36. public void EquipGear(Equipment equipment)
    37.     {
    38.         endurance += equipment.MyEndurance;
    39.         intellect += equipment.MyIntellect;
    40.         strength += equipment.MyStrength;
    41.         agility += equipment.MyAgility;
    42.         UpdateMaxStats();
    43.         UIManager.instance.UpdateCharacterStatText(strength, endurance, intellect, agility);
    44.     }
    45.  
    46.     public void RemoveGear(Equipment equipment)
    47.     {
    48.         endurance -= equipment.MyEndurance;
    49.         intellect -= equipment.MyIntellect;
    50.         strength -= equipment.MyStrength;
    51.         agility -= equipment.MyAgility;
    52.         UpdateMaxStats();
    53.         UIManager.instance.UpdateCharacterStatText(strength, endurance, intellect, agility);
    54.     }
    I realize there is a ton of code, my apologies but trying to give all context possible. I've tried something like this in a variety of ways:
    Code (CSharp):
    1. private float ConvertStatFromEnum(Enums.Stat statName, int statType)
    2.     {
    3.         statType = (int)prefixValue;
    4.         return statType;
    5.     }
    And put it in awake but that doesn't work. My gut tells me there is something i can do in the property and have it set the proper Enum.Stat value to it, but I've failed at that as well. Any help is appreciated.

    Edit: The newer tutorial series doesnt talk about how he implements the Enum stats to the Player's stats. He shows it, but doesnt show code on that part. As far as the numerous related videos ive combed thru.
     
    Last edited: Oct 27, 2022
  2. Cornysam

    Cornysam

    Joined:
    Feb 8, 2018
    Posts:
    1,465
    okay, of course as i spend all that time typing it out, i sorta figure it out. The key was in the EquipGear function.

    Solution:
    Code (CSharp):
    1. public void EquipGear(Equipment equipment)
    2.     {
    3.         //endurance += equipment.MyEndurance;
    4.         intellect += equipment.MyIntellect;
    5.         strength += equipment.MyStrength;
    6.         agility += equipment.MyAgility;
    7.         if(equipment.prefix == Enums.Stat.Endurance)
    8.         {
    9.             endurance += (int)equipment.prefixValue;
    10.         }
    11.         if (equipment.suffix == Enums.Stat.Endurance)
    12.         {
    13.             endurance += (int)equipment.suffixValue;
    14.         }
    15.        
    16.         UpdateMaxStats();
    17.         UIManager.instance.UpdateCharacterStatText(strength, endurance, intellect, agility);
    18.     }
    However, I still think there may be a better way to do this (there has to be, right?). Otherwise I will have 5 if blocks per stat in the EquipGear (and RemoveGear) functions. I am definitely willing, but it just seems like way too much typing for something that is probably like 2 lines of code to convert the Enum to the stat like i was asking for help with above.
     
  3. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    Use ScriptableObjects instead of Enums, as SO can contain code (fields and and methods). Basically the SO will have all the necessary values (+ you can edit/tweak them in the Editor), then in your code for Equip/RemoveGear just read those values and add them to the character.


    Code (CSharp):
    1. public class Gear : ScriptableObject
    2. {
    3.     public float Strength;
    4.     public float Endurance;
    5.     public float Intellect;
    6.     public float Agility;
    7.     // Bonus: you can also have really interesting fields like:
    8.     public GameObject Prefab;
    9.     public Sprite Image;
    10.     public string Description;
    11.     public AudioClip EquipSound;
    12.     public int InventoryWidth;
    13.     public int InventoryHeight;
    14.     public int PlayerMinimumLevel;
    15.     public float Price;
    16. }
    17.  
    18. // In your Equip method
    19. public void EquipGear(Gear gear)
    20. {
    21.     character.Strength += gear.Strength;
    22.     character.Endurance += gear.Endurance;
    23.     character.Intellect += gear.Intellect;
    24.     character.Agility += gear.Agility;
    25. }
     
    Last edited: Oct 27, 2022
    Cornysam and spiney199 like this.
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,925
    Yeah I'd advise against using enums for this sort of stuff. They're an anti-pattern that makes one's logic a mess and scalability impossible.

    Like above I would personally use scriptable objects to define your gear, to define your stat types, etc etc. Just don't use Enums as they will make systems like these hell.
     
    Cornysam and Nad_B like this.
  5. Cornysam

    Cornysam

    Joined:
    Feb 8, 2018
    Posts:
    1,465
    So i am actually using Scriptable objects for my Equipment class. It inherits from a class called Item that is a scritable object. So yall are just saying i should convert the random selection logic away from Enums. Should i create a List or Array and put the stats in there and then select randomly from there?

    Here is the current AddAffixes function. so it would just become like: e.suffix = MyNewStatList[rand] or something like that? And Rand is 0-6 or w/e?

    Code (CSharp):
    1. private void AddAffixes(Equipment e)
    2.     {
    3.         switch(e.MyItemRarity)
    4.         {
    5.             case Enums.ItemRarity.Common:
    6.                 e.suffix = (Enums.Stat)Random.Range(0, 6);
    7.                 //e.newSuffix = ChooseBaseStat();
    8.                 break;
    9.             case Enums.ItemRarity.Magic:
    10.                 e.suffix = (Enums.Stat)Random.Range(0, 6);
    11.                 e.prefix = (Enums.Stat)Random.Range(7, 16);
    12.                 break;
    13.             case Enums.ItemRarity.Rare:
    14.                 e.suffix = (Enums.Stat)Random.Range(0, 6);
    15.                 e.prefix = (Enums.Stat)Random.Range(7, 16);
    16.                 e.property1 = (Enums.Stat)Random.Range(7, 16);
    17.                 e.property2 = (Enums.Stat)Random.Range(7, 17);
    18.                 break;
    19.             case Enums.ItemRarity.Epic:
    20.                 e.suffix = (Enums.Stat)Random.Range(0, 6);
    21.                 e.prefix = (Enums.Stat)Random.Range(7, 16);
    22.                 e.property1 = (Enums.Stat)Random.Range(7, 16);
    23.                 e.property2 = (Enums.Stat)Random.Range(7, 16);
    24.                 e.property3 = (Enums.Stat)Random.Range(7, 16);
    25.                 break;
    26.             default:
    27.                 break;
    28.         }
    29.     }
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,925
    Well the long and short of it is that you can't really put any 'information' in an enum (aside from inheriting it from some integral types and casting it).

    So rather than using enums for stuff like this it's far better 99% of the time to use the thing that makes C# so flexible, objects (It's an object oriented programming language after all). And in Unity most of the time you use scriptable objects for the native inspector and serialisation support.

    So rather than a switch statement OR a list, just make scriptable objects out of these rarities and code the functionality you need into them, based on values you can assign in them.

    The same can probably said for most of the other enums you seem to be using. Ask yourself, "Can I encapsulate this into an object?", and the answer should almost always be yes.

    That said scriptable objects can get a little unwieldy after a while. It's also worthwhile looking at SerializeReference with some inspector support, so you can define this in plain c# objects and serialise them right into your equipment objects.
     
    Cornysam likes this.
  7. Cornysam

    Cornysam

    Joined:
    Feb 8, 2018
    Posts:
    1,465
    Thank you Spiney. I was on the Unity Dev discord and they mentioned basically the same thing. Having a SO class and add a list of Prefixes, Suffixes, and the values possible. Then call RandomPrefix() or RandomValue() or w/e to add to the SO class Equipment I am using.
     
    Nad_B likes this.
  8. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,330
    To play devil's advocate a little bit here, I'd like to point out that it's not in my opinion really the use of enums here that is causing the code to be complicated, it's the use of switch statements and if statements.

    If the enum is treated more like just a general identifier, an integer with a human readable name, then it can be used as a means of filtering collections, and the resulting code can be simple and scalable.
    Code (CSharp):
    1. public enum Stat
    2. {
    3.     None = 0,
    4.     Strength = 1,
    5.     Endurance = 2,
    6.     Intelligence = 3,
    7.     Agility = 4
    8. }
    9.  
    10. public enum EquipmentSlot
    11. {
    12.     None = 0,
    13.     Head = 1,
    14.     Body = 2,
    15.     Feet = 3
    16. }
    17.  
    18. [Serializable]
    19. public struct StatModifier
    20. {
    21.     [SerializeField] private Stat stat;
    22.     [SerializeField] private int amount;
    23.  
    24.     public Stat Stat => stat;
    25.     public int Amount => amount;
    26.  
    27.     public StatModifier(Stat stat, int amount)
    28.     {
    29.         this.stat = stat;
    30.         this.amount = amount;
    31.     }
    32. }
    33.  
    34. public abstract class Equipment : ScriptableObject
    35. {
    36.     [SerializeField] private EquipmentSlot slot;
    37.     [SerializeField] private StatModifier[] statModifiers;
    38.  
    39.     public EquipmentSlot Slot => slot;
    40.  
    41.     public int GetStat(Stat stat)
    42.     {
    43.         foreach(var statModifier in statModifiers)
    44.         {
    45.             if(statModifier.Stat == stat)
    46.             {
    47.                 return statModifier.Amount;
    48.             }
    49.         }
    50.  
    51.         return 0;
    52.     }
    53.  
    54.     public static TItem CreateRandom<TItem>(int level) where TItem : Equipment
    55.     {
    56.         var modifiers = new List<StatModifier>();
    57.         var stats = new List<Stat>(Enum.GetValues(typeof(Stat)) as Stat[]);
    58.         for(int i = 0; i < level; i++)
    59.         {
    60.             int statIndex = Random.Range(0, stats.Count);
    61.             var stat = stats[statIndex];
    62.             stats.RemoveAt(statIndex);
    63.  
    64.             int amount = Random.Range(1, level);
    65.  
    66.             var modifier = new StatModifier(stat, amount);
    67.             modifiers.Add(modifier);
    68.         }
    69.  
    70.         var result = CreateInstance<TItem>();
    71.         result.statModifiers = modifiers.ToArray();
    72.         return result;
    73.     }
    74. }
    75.  
    76. public sealed class PlayerStats
    77. {
    78.     private readonly Dictionary<Stat, int> stats = new Dictionary<Stat, int>();
    79.  
    80.     public int GetStat(Stat stat) => stats[stat];
    81.     public void IncreaseStat(Stat stat, int amount) => stats[stat] += amount;
    82. }
    83.  
    84. public sealed class PlayerEquipment
    85. {
    86.     private readonly Dictionary<EquipmentSlot, Equipment> equipped = new Dictionary<EquipmentSlot, Equipment>();
    87.  
    88.     public int GetStat(Stat stat)
    89.     {
    90.         int result = 0;
    91.         foreach(var equipment in equipped.Values)
    92.         {
    93.             result += equipment.GetStat(stat);
    94.         }
    95.  
    96.         return result;
    97.     }
    98.  
    99.     public void Equip(Equipment item) => equipped[item.Slot] = item;
    100. }
    101.  
    102. public sealed class Player : MonoBehaviour
    103. {
    104.     private readonly PlayerStats stats = new PlayerStats();
    105.     private readonly PlayerEquipment equipment = new PlayerEquipment();
    106.  
    107.     public int GetStat(Stat stat) => stats.GetStat(stat) + equipment.GetStat(stat);
    108. }
    Not that I'm saying using ScriptableObjects instead is a bad idea or anything, I just wanted to point out the heart of the issue in the original code :)
     
    Cornysam likes this.