在我的游戏中,我有许多不同类型的单位,这些单位经常被实例化/销毁。它们具有某些值,如MaxHP或Level或Defense,需要在实例化时引用,并且可能会根据某些因素而有所不同。例如,如果等级增加,则MaxHP可能更高,但不是基于严格的公式,该公式随单位而变化。所以,例如:
士兵等级3最高HP:8
Mage Level 1 Max HP:3
班级名称很容易被引用,单位的级别会存储在其他地方,我需要知道我一直想要查找哪个特征。因此,对我来说有意义的是将它们存储为键/值对。我可以以编程方式检查类似(className + lvlString + traitName)的内容作为键,因此键最终将成为Mage2MaxHP,值将为4.
通常我在这方面的直觉非常糟糕,因为我从未学过很多关于数据结构的知识。所以,我想知道是否有更好的方法来实现这一目标并组织这些数据。例如,对于字典而言,这似乎非常大。也许它可以更容易管理分成几个词典(一个用于MaxHP,一个用于防御等),但我仍然觉得我忽略了一个更适合这个目的的数据结构。有什么建议吗?
答案 0 :(得分:3)
"数百"在现代CPU方面真的不是很多,字典增加了很多复杂性并且将你与一种访问模式联系在一起。通常最好从简单开始:使用-180 431.845
-180 434.337
-180 436.819
-180 439.289
-180 469.936
-180 472.152
-180 474.343
-180 476.509
-180 478.649
-180 480.761
-180 482.846
-180 484.902
-180 486.929
-180 488.926
-175 387.566
-175 384.891
-175 382.216
-175 379.541
-175 376.868
-175 374.197
-175 371.53
-175 368.867
-175 366.209
-175 363.557
-175 360.912
-175 358.275
-175 355.648
-175 353.03
-175 350.422
-175 347.826
-175 345.243
-175 342.673
-175 340.117
-175 337.576
-175 335.05
-175 332.541
-175 330.05
-175 327.576
并找出您的访问模式。您可以使用LINQ来查询类名,hp,级别或其任何组合。一旦你有了它的工作,如果你发现你需要提高一些查询的性能,那么,那时候继续并重构它可能使用List<T>
作为索引。
但永远不要低估现代编译器和现代CPU迭代连续内存块的能力,快速击败许多其他数据结构,而不是为非常大的N值提供更好的理论性能。
答案 1 :(得分:3)
以下内容将使用继承和字典来处理您的问题,以避免使用类名字符串等。
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
就像你一样,我也认为可以用不同的方式解决这个问题。
我喜欢这种特殊的方法是它基于OOP-Principles并且它减少了冗余的结构元素(例如避免了特征类型的枚举等)。您可以访问单元的属性 - 单元属性。
这里使用的字典相当小。 但是,在尺寸限制方面,我同意评论/答案中的人。无论您选择何种方法,您都可能不会使您的词典接近其极限。
答案 2 :(得分:2)
我通常使用如下代码:
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; }
}
答案 3 :(得分: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
方法。
有关详细信息,请参阅示例Why is it important to override GetHashCode when Equals method is overridden?和MSDN。
答案 4 :(得分: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");
}
}
答案 5 :(得分: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
}
}