我正在努力为游戏添加声音效果,虽然我目前的代码工作正常,但我正在寻找一种简化它的方法。 基本上,游戏中的每个对象具有指示其材料的字符串值(即“木材”,“金属”等),并且当两个对象碰撞时,基于该组合播放声音效果。代码基本上如下所示:
44
但是我想知道是否有办法将if语句简化为这样:
if( (matA == "metal" && matB == "wood") || (matA == "wood" && matB == "metal") )
{
//play sound for metal-wood collision
}
答案 0 :(得分:52)
您应该使用enum
作为材料而不是字符串,并且可以使用Dictionary
来保存相应的声音组合。您可以跳过多个if
语句,并使用Dictionary
自动为每种材料选择相应的对象。例如:
[Flags]
enum Material
{
Wood=1,
Iron=2,
Glass=4
//...
}
Dictionary<Material,SoundObject> sounds = new Dictionary<Material,SoundObject>();
sounds.add(Material.Wood,woodSound);
sounds.add(Material.Iron,ironSound);
sounds.add(Material.Wood | Material.Iron,woodAndIronSound);
// And play corresponding sound directly without any if statement.
sounds[object.Material].Play();
sounds[matA | matB].Play();
您还可以使用此方法提高性能。因为Enum值或哈希码的整数比较肯定比字符串比较更容易,更快。关于字典VS多个if-else
语句,一系列if/else if
语句线性执行;所以它的性能很大程度上取决于if语句的数量和对象的相等比较;而Dictionary
基于Hashtable。它使用索引优化的集合来存储值,这有效地保持了访问时间。这意味着通常无论字典中有多少个键,您都可以在一个恒定的时间内访问值,在大多数情况下,它比多个if语句快得多。
我们将在此示例中比较两种方法的性能:
//If you want to try, just copy the code and see the result.
static Dictionary<char, short> myHashTable = Enumerable.Range((short)'A', (short)'z').ToDictionary((ch) => (char)ch, (sh) => (short)sh);
static void Main(string[] args)
{
System.Diagnostics.Stopwatch SW = new System.Diagnostics.Stopwatch();
short temp = 0;
SW.Start();
for(int i=0;i<10000000;i++)
temp = getValue('z');
SW.Stop();
Console.WriteLine(SW.ElapsedMilliseconds );
SW.Reset();
SW.Start();
for(int i =0;i<10000000;i++)
temp = myHashTable['a'];
SW.Stop();
Console.WriteLine(SW.ElapsedMilliseconds);
}
static short getValue(char input)
{
if (input == 'a')
return (short)'a';
else if (input == 'b')
return (short)'b';
else if (input == 'c')
return (short)'c';
else if (input == 'd')
return (short)'d';
else if (input == 'e')
return (short)'e';
else if (input == 'f')
return (short)'f';
else if (input == 'g')
return (short)'g';
else if (input == 'h')
return (short)'h';
else if (input == 'i')
return (short)'i';
else if (input == 'j')
return (short)'j';
else if (input == 'k')
return (short)'k';
else if (input == 'l')
return (short)'l';
else if (input == 'm')
return (short)'m';
else if (input == 'n')
return (short)'n';
else if (input == 'o')
return (short)'o';
else if (input == 'p')
return (short)'p';
else if (input == 'q')
return (short)'q';
else if (input == 'r')
return (short)'r';
else if (input == 's')
return (short)'s';
else if (input == 't')
return (short)'t';
else if (input == 'u')
return (short)'u';
else if (input == 'v')
return (short)'v';
else if (input == 'w')
return (short)'w';
else if (input == 'x')
return (short)'x';
else if (input == 'y')
return (short)'y';
else if (input == 'z')
return (short)'z';
return 0;
}
if
个陈述|字典122项。
593 254
579 256
572 252
570 246
587 248
574 291
576 246
685 265
599 282
723 338
表示字典比if/else if
语句快2倍以上。
答案 1 :(得分:12)
找到自己repeating code时的常规方法是提取方法:
if (IsWoodAndMetal(matA, matB) || IsWoodAndMetal(matB, matA))
{
// play sound for metal-wood collision
}
IsWoodAndMetal
定义为:
public static bool IsWoodAndMetal(string matA, string matB)
{
return matA == "wood" && matB == "metal";
}
这将与原始代码一样快,不像所有linq / list和字符串连接解决方案为频繁的游戏循环分配内存坏消息,因为它会导致更频繁和/或更长时间垃圾收集。
如果||
仍然困扰你,我们可以更进一步,提取:
public static bool EitherParameterOrder<T>(Func<T, T, bool> func, T a, T b)
{
return func(a, b) || func(b, a);
}
现在写着:
if (EitherParameterOrder(IsWoodAndMetal, matA, matB))
{
// play sound for metal-wood collision
}
我仍然喜欢其他解决方案的表现(除了字典解决方案,当你有一些条目)。
答案 2 :(得分:8)
它可能不是最现代化的解决方案,但使用素数作为材料的参考可以提高您的表现。我知道并理解&#34;在必要之前进行优化&#34;是许多程序员不推荐的,但是,在这种情况下,我认为它到目前为止并没有增加代码的复杂性,而是提高了这个(相当简单的)任务的性能。
public static class Materials
{
public static uint Wood = 2,
public static uint Metal = 3,
public static uint Dirt = 5,
// etc...
}
if(matA*matB == Materials.Wood*Materials.Metal)
{
//play sound for metal-wood collision
}
//or with enums but annoying casts are necessary...
enum Materials:uint
{
Wood = 2,
Metal = 3,
Dirt = 5,
// etc...
}
if((uint)matA*(uint)matB == (uint)Materials.Wood*(uint)Materials.Metal)
{
//play sound for metal-wood collision
}
这种方法与材料的顺序(交换乘法)无关,并且不需要对字符串或任何比整数更复杂的结构进行任何长时间的比较。
假设您希望保留所有引用数字4字节整数并且最大4字节整数的平方根大约为65535,这将使您在6535以下可以得到大约6550个可能的素数,这样就没有产品会导致整数溢出。对于任何普通游戏来说,这应该足够了。
答案 3 :(得分:5)
您应该将mat {A,B}类型更改为枚举。 其定义如下:
[Flags]
enum Materials {
Wood = 1,
Metal = 2,
Concrete = 4,
// etc ...
}
然后代码看起来像这样:
Meterials matA = Materials.Wood;
Meterials matB = Materials.Metal;
if ((matA | matB) == (Materials.Metal | Materials.Wood))
{
// Do something
}
这里唯一的问题是,matA现在可以同时使用Wood和Metal类型,但这个问题也出现在字符串解决方案中。
---编辑---
也可以为木材和金属创建枚举别名
[Flags]
enum Materials
{
Wood = 1,
Metal = 2,
WoodMetal = Wood | Metal,
Concrete = 4,
// etc
}
然后代码看起来像这样:
Materials matA = Materials.Wood;
Materials matB = Materials.Metal;
if ((matA | matB) == Materials.WoodMetal)
{
// Do something
}
答案 4 :(得分:5)
我觉得有必要发布我认为最明显的&#39;解决方案,其他人似乎还没有发布。如果接受的答案适合您,请使用该答案。我只是为了完整性而加上这个。
首先,定义一个静态辅助方法,以两种方式进行比较:
public static bool MaterialsMatch(string candidate1, string candidate2,
string expected1, string expected2)
{
return (candidate1 == expected1 && candidate2 == expected2) ||
(candidate1 == expected2 && candidate2 == expected1);
}
然后在if
语句中使用它:
if (MaterialsMatch(matA, matB, "wood", "metal"))
{
// play sound for wood-metal collision
}
else if (MaterialsMatch(matA, matB, "wood", "rubber"))
{
// play sound for wood-rubber collision
}
else if (MaterialsMatch(matA, matB, "metal", "rubber"))
{
// play sound for metal-rubber collision
}
// etc.