What is an efficient way to store and reference hundreds of values?

In my game I have many different types of units that are created / destroyed with regularity. They have certain values, such as MaxHP or Level or Defense, which must be referenced when creating the instance and may vary depending on certain factors. For example, MaxHP may be higher if the level increases, but is not based on a strict formula that depends on one. So for example:

  • Soldier Level 1 Max. HP: 5
  • Soldier Level 2 Max. HP: 6
  • Soldier Level 3 Max. HP: 8

  • Mage Level 1 Max. HP: 3

  • Mage Level 2 Max. HP: 4
  • Mage Level 3 Max. HP: 5

The class name will be easily referenced, and the unit level will be stored elsewhere, and I will need to find out what the hell I'm looking for in order to find it first. Therefore, it makes sense for me to store them as key / value pairs. I can programmatically check something like (className + lvlString + traitName) as a key, so the key will end up in Mage2MaxHP and the value will be 4.

, . , , . , , . , ( MaxHP, Defense ..), , , . ?

+4
6

, , , ..

public abstract class Unit
{
    // This approach would use one Dictionary per Trait
    protected abstract Dictionary<int, int> MaxHpByLevel { get; }

    public int Level { get; set; } = 1;

    public int MaxHp => this.MaxHpByLevel[this.Level];
}

public class Soldier : Unit
{
    protected override Dictionary<int, int> MaxHpByLevel => new Dictionary<int, int>
    {
        [1] = 5,
        [2] = 6,
        [3] = 8
    };
}

public class Mage : Unit
{
    protected override Dictionary<int, int> MaxHpByLevel => new Dictionary<int, int>
    {
        [1] = 3,
        [2] = 4,
        [3] = 5
    };
}

:

var soldier = new Soldier { Level = 2 };
Console.WriteLine(soldier.MaxHp); // 6

, , -.

, , (, ..). - - .

, , . , /, . , , .

+3

"" , . : List<T> , . LINQ , hp, . , , , , , , Dictionary .

, , N.

+3

, :

   public class Unit
    {
        public static Dictionary<string, Unit> units { get; set; } // string is unit name

        public string name { get; set; }
        public int level { get; set; }
        public Dictionary<string, int> attributes { get; set; }
    }
+2

, , , , . - :

public enum Class
{
    Soldier,
    Mage,
    /*...*/
}

public enum Attribute
{
    Health,
    Magic,
    /*...*/
}

, , "", . - :

public struct AttributeKey : IEquatable<AttributeKey>
{
    public AttributeKey(Class @class, Attribute attribute, int level)
    {
        Class = @class;
        Attribute = attribute;
        Level = level;
    }

    public readonly Class Class;
    public readonly Attribute Attribute;
    public readonly int Level;

    public bool Equals(AttributeKey other)
    {
        return Class == other.Class && Attribute == other.Attribute && Level == other.Level;
    }
    public override bool Equals(object obj)
    {
        return obj is AttributeKey && Equals((AttributeKey)obj);
    }
    public override int GetHashCode()
    {
        unchecked
        {
            return (((Class.GetHashCode() * 397) ^ Attribute.GetHashCode()) * 397) ^ Level;
        }
    }
}

- Equals GetHashCode.

. GetHashCode, Equals ? MSDN .

+2

, , . Indexer, Dictionary Tuple

. db , . , , .


private static readonly CharLookup _lookup = new CharLookup();

private static void Main()
{
   _lookup[CharacterClass.Mage, CharacterTrait.SingingAbility, 2] = 123;
   _lookup[CharacterClass.Mage, CharacterTrait.SingingAbility, 3] = 234;
   _lookup[CharacterClass.Soilder, CharacterTrait.MaxBeers, 3] = 23423;

   Console.WriteLine("Mage,SingingAbility,2 = " + _lookup[CharacterClass.Mage, CharacterTrait.SingingAbility, 2]);
   Console.WriteLine("Soilder,MaxBeers,3 = " + _lookup[CharacterClass.Soilder, CharacterTrait.MaxBeers, 3]);
}

public enum CharacterClass
{
   Soilder,

   Mage,

   SmellyCoder
}

public enum CharacterTrait
{
   MaxHp,

   MaxBeers,

   SingingAbility
}

CharLookup

public class CharLookup
{
   private Dictionary<Tuple<CharacterClass, CharacterTrait, int>, int> myDict = new Dictionary<Tuple<CharacterClass, CharacterTrait, int>, int>();

   public int this[CharacterClass characterClass, CharacterTrait characterTrait, int level]
   {
      get => Check(characterClass, characterTrait, level);
      set => Add(characterClass, characterTrait, level, value);
   }

   public void Add(CharacterClass characterClass, CharacterTrait characterTrait, int level, int value)
   {
      var key = new Tuple<CharacterClass, CharacterTrait, int>(characterClass, characterTrait, level);

      if (myDict.ContainsKey(key))
         myDict[key] = value;
      else
         myDict.Add(key, value);
   }

   public int Check(CharacterClass characterClass, CharacterTrait characterTrait, int level)
   {
      var key = new Tuple<CharacterClass, CharacterTrait, int>(characterClass, characterTrait, level);

      if (myDict.TryGetValue(key, out var result))
         return result;

      throw new ArgumentOutOfRangeException("blah");
   }
}
+2

, .

public enum UnitType
{
    Soldier,
    Mage
}

public enum StatType
{
    MaxHP,
    MaxMP,
    Attack,
    Defense
}

// Where the unit initialisation data is stored
public static class UnitData
{
    private static Dictionary<string, Dictionary<StatType, int>> Data = new Dictionary<UnitType, Dictionary<StatType, int>>();

    private static string GetKey(UnitType unitType, int level)
    {
        return $"{unitType}:{level}";
    }

    public static AddUnit(UnitType unitType, int level, int maxHP, int maxMP, int attack, int defense)
    {
        Data.Add(GetKey(unitType, level), 
            new Dictionary<StatType, int> 
            {
                { StatType.MaxHP, maxHP },
                { StatType.MaxMP, maxMP },
                { StatType.Attack, attack },
                { StatType.Defense, defense }
            });
    }

    public static int GetStat(UnitType unitType, int level, StatType statType)
    {
        return Data[GetKet(unitType, level][statType];
    }
}

// The data is not stored against the unit but referenced from UnitData
public class Unit
{
    public UnitType UnitType { get; private set; }
    public int Level { get; private set; }
    public Unit(UnitType unitType, int level)
    {
        UnitType = unitTypel
        Level = level;
    }
    public int GetStat(StatType statType)
    {
        return UnitData.GetStat(UnitType, Level, statType);
    }
}

// To initialise the data
public class StartClass
{
    public void InitialiseData()
    {
        UnitData.Add(UnitType.Soldier, 1, 5, 0, 1, 1);
        UnitData.Add(UnitType.Soldier, 2, 6, 0, 2, 2);
        UnitData.Add(UnitType.Soldier, 3, 8, 0, 3, 3);

        UnitData.Add(UnitType.Mage, 1, 3, 10, 1, 1);
        UnitData.Add(UnitType.Mage, 2, 4, 15, 2, 2);
        UnitData.Add(UnitType.Mage, 3, 5, 20, 3, 3);
    }
}

// Use of units
public class Level1
{
    public List<Unit> Units = new List<Unit>();
    public void InitialiseUnits()
    {
        Units.Add(new Unit(UnitType.Soldier, 1));
        Units.Add(new Unit(UnitType.Soldier, 1));
        Units.Add(new Unit(UnitType.Mage, 1));
        Units.Add(new Unit(UnitType.Mage, 1));
    }
    public void Something()
    {
        int maxHP = Units.First().GetStat(StatType.MaxHP);
        // etc
    }
}
+2
source

Source: https://habr.com/ru/post/1695796/


All Articles