繁体   English   中英

是否可以简化检查组合的if语句?

[英]Is it possible to simplify an if-statement that checks for a combination?

我正在努力为游戏添加声音效果,虽然我目前的代码工作正常,但我正在寻找一种简化它的方法。 基本上,游戏中的每个对象具有指示其材料的字符串值(即“木材”,“金属”等),并且当两个对象碰撞时,基于该组合播放声音效果。 代码基本上如下所示:

if( (matA == "metal" && matB == "wood") || (matA == "wood" && matB == "metal") )
{
    //play sound for metal-wood collision
}

但我想知道是否有办法将if语句简化为这样的事情:

if( one of the materials is wood && one of the materials is metal )
{
    //play sound for metal-wood collision
}

你应该使用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语句包含26个项目| 字典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倍以上。

当您发现自己重复代码时,正常的方法是提取方法:

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
}

而且我仍然喜欢其他解决方案的性能(除了字典解决方案,当你有一些条目)。

它可能不是最现代的解决方案,但使用素数作为材料的参考可以提高您的性能。 我知道并理解“在必要之前进行优化”是许多程序员不推荐的,但是,在这种情况下,我认为它到目前为止并没有增加代码的复杂性,而是提高了这个(相当简单的)任务的性能。

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个可能的素数,这样任何产品都不会导致整数溢出。 对于任何普通游戏来说,这应该足够了。

您应该将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
}

我觉得有必要发布我认为最明显的解决方案,其他人似乎还没有发布。 如果接受的答案适合您,请使用该答案。 我只是为了完整性而添加它。

首先,定义一个静态辅助方法,以两种方式进行比较:

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.

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM