简体   繁体   English

什么是存储和引用数百个值的有效方法?

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

In my game, I have many different types of units that are instantiated/destroyed with regularity. 在我的游戏中,我有许多不同类型的单元,这些单元经常被实例化/销毁。 They have certain values, like MaxHP or Level or Defense, which need to be referenced at instantiation, and may vary based on certain factors. 它们具有某些值,如MaxHP或Level或Defense,需要在实例化时引用,并且可能会根据某些因素而有所不同。 For example, MaxHP may be higher if Level increases, but not based on a strict formula, which varies by unit. 例如,如果等级增加,则MaxHP可能更高,但不是基于严格的公式,该公式随单位而变化。 So, for example: 所以,例如:

  • Soldier Level 1 Max HP: 5 士兵等级1最高HP:5
  • Soldier Level 2 Max HP: 6 士兵等级2最高HP:6
  • Soldier Level 3 Max HP: 8 士兵等级3最高HP:8

  • Mage Level 1 Max HP: 3 法师级别1最大HP:3

  • Mage Level 2 Max HP: 4 法师2级最大HP:4
  • Mage Level 3 Max HP: 5 法师等级3最大HP:5

The class name would be easy to reference, and the level of the units would be stored elsewhere, and I'd need to know which trait I'm looking up to look it up in the first place. 类名很容易引用,单位的级别将存储在别处,我需要知道我正在查找哪个特性才能查找它。 Therefore, what makes sense to me is to store these as key/value pairs. 因此,对我来说有意义的是将它们存储为键/值对。 I can programmatically check something like (className + lvlString + traitName) as the key, so the key would end up being Mage2MaxHP and the value would be 4. 我可以以编程方式检查类似(className + lvlString + traitName)的内容作为键,因此键最终将成为Mage2MaxHP,值将为4。

Usually my instincts in this regard are pretty bad since I never learned much about data structures. 通常我在这方面的直觉非常糟糕,因为我从未学过很多关于数据结构的知识。 So, I'm wondering if there's a better way to achieve this and organize this amount of data. 所以,我想知道是否有更好的方法来实现这一点并组织这些数据。 For example, it seems like this would be incredibly large for a dictionary. 例如,对于字典而言,这似乎非常大。 Perhaps it would be more manageable split into several dictionaries (one for MaxHP, one for Defense, etc.), but I still feel like I'm overlooking a data structure more suitable for this purpose. 也许它会更容易管理分成几个词典(一个用于MaxHP,一个用于防御等),但我仍然觉得我忽略了一个更适合这个目的的数据结构。 Any recommendations? 有什么建议?

"Hundreds" really isn't much in terms of a modern CPU, a Dictionary adds a lot of complexity and ties you to one access pattern. “数百”在现代CPU方面确实不多,“词典”增加了很多复杂性,并将您与一种访问模式联系起来。 Often it's best to start simple: with a List<T> and figure out what your access patterns will be. 通常最好从简单开始:使用List<T>并找出您的访问模式。 You can use LINQ against it to query by class name, hp, level or any combination thereof. 您可以使用LINQ来查询类名,hp,级别或其任何组合。 Once you have it working, if you find that you need to boost performance for some queries, then, at that time go ahead and refactor it maybe using a Dictionary as an index. 一旦你有了它的工作,如果你发现你需要提高某些查询的性能,那么,那时候继续并重构它可能使用Dictionary作为索引。

But never underestimate the power of a modern compiler and a modern CPU to iterate over a contiguous block of memory blindingly fast beating many other data structures than might offer a better theoretical performance for very large values of N. 但永远不要低估现代编译器和现代CPU迭代连续的内存块的能力,这些内容快速地击败了许多其他数据结构,而不是为非常大的N值提供更好的理论性能。

The following would approach your issue using both inheritance and Dictionaries in order to avoid having to class-name-strings etc. 以下将使用继承和字典来解决您的问题,以避免必须使用类名字符串等。

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
    };
}

You would use it like that: 你可以这样使用它:

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

Just like you, I also think one could solve this issue in different ways. 就像你一样,我也认为可以用不同的方式解决这个问题。

What I like about this particular approach is that it is based on OOP-Principles and that it reduces redundant structural elements (eg avoids enums for trait-types etc.). 我喜欢这种特殊的方法是它基于OOP-Principles并且它减少了冗余的结构元素(例如避免了特征类型的枚举等)。 You can access the properties of a unit over - well - Properties of a unit. 您可以访问单元的属性 - 井 - 单元的属性。

The Dictionaries used here would be rather small. 这里使用的字典相当小。 However, I agree with the folks from the comments/answers when it comes to the size-restrictions. 但是,在尺寸限制方面,我同意评论/答案中的人。 No matter what approach you choose, you will likely not bring your dictionaries any close to its limits. 无论您选择何种方法,您都可能不会使您的词典接近其极限。

I usually use code like below : 我通常使用如下代码:

   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; }
    }

You might want to have the classes and attributes as enums, which I'd expect to come in handy in a lot of other places, too. 您可能希望将类和属性作为枚举,我希望在许多其他地方也可以派上用场。 Something like: 就像是:

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

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

And then combine them to have a key for a dictionary that should be a bit more efficient and "useful" than just a concatenated string. 然后将它们组合起来以获得一个字典的密钥,该字典应该比连接字符串更有效和“有用”。 Something like: 就像是:

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;
        }
    }
}

Most important for the use as a key in a dictionary is to be very deliberate with the Equals and especially the GetHashCode methods. 对于在字典中作为键使用最重要的是使用Equals ,特别是GetHashCode方法非常慎重。

See for example Why is it important to override GetHashCode when Equals method is overridden? 请参阅示例为什么重写Equals方法时重写GetHashCode很重要? and MSDN for further information about that. MSDN有关的更多信息。

There is no particular reason why this is better than any other approach. 没有特别的理由说明为什么这比任何其他方法都要好。 However you could use a class with an Indexer that has an internal Dictionary with a Tuple key 但是,您可以使用具有带有Tuple键的内部DictionaryIndexer的类

Note : Ideally you would want to store and load this stuff from db, or a file, or maybe just generate it dynamically if there is not too much. 注意 :理想情况下,您可能希望从db或文件中存储和加载这些内容,或者如果没有太多则可能只是动态生成它。 However, depending on the nature of the game, this maybe a novel approach. 但是,根据游戏的性质,这可能是一种新颖的方法。

Demo here 在这里演示


Main 主要

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]);
}

Enums 枚举

public enum CharacterClass
{
   Soilder,

   Mage,

   SmellyCoder
}

public enum CharacterTrait
{
   MaxHp,

   MaxBeers,

   SingingAbility
}

CharLookup 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");
   }
}

This is still storing in a dictionary but includes simple enums and methods to store and retreive the data. 这仍然存储在字典中,但包括简单的枚举和存储和检索数据的方法。

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
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Sql Server会话数据库中存储多个值的有效方法是什么? - What is the efficient way to store multiple values in Sql Server Session database? 发送数百或数千个 http 请求(c#)的首选/有效方式是什么? - What is a preferred/efficient way to send hundreds or thousands of http requests (c#)? 存储对AD组的引用的最佳方法是什么? - What is the best way to store a reference to an AD group? 使应用程序在单个实例中处理来自Windows资源管理器的数百个同时文件打开请求的最有效方法是什么? - What is the most efficient way to make an application handle hundreds of simultaneous file open requests from Windows Explorer in a single instance? 从枚举中存储和读取其他信息的有效方法是什么? - What is the efficient way to store and read additional information from enums 从API提取XML并将其存储在本地的最有效方法是什么? - What is the most efficient way to take XML from API and store it locally? 比较“对象”类型的值的最有效方法是什么 - What is the most efficient way to compare values of type “object” 在XDocument中查找和设置元素值的最有效方法是什么? - What's the most efficient way to locate and set element values in an XDocument? 什么是将行中的值分配给属性的有效方法 - what is efficient way to assign values from rows to properties 根据列表的不同值迭代列表的有效方法是什么? - What is an efficient way of iterating through a list according to its distinct values?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM