摆脱不必要的循环

时间:2014-10-16 18:14:05

标签: c# xna

在我的游戏中,我将进行很多交互,我需要查看玩家是否有物品,如果他有,其他事情是真的,那么就做一个动作。在以下代码中描述。

private void SetTinderInPit()
{
    MouseState currentMouseState = Mouse.GetState();

    if (player.NextToFirePit == true)
    {
        foreach (Item item in player.PlayerInventory.Items)
        {
            if (item.ItemName == "tinder")
            {
                foreach (Item pit in allItemsOnGround)
                {
                    if (pit.ItemName == "firepit" && 
                        pit.ItemRectangle.Contains(MouseWorldPosition) &&
                        currentMouseState.LeftButton == ButtonState.Pressed &&
                        oldMouseState.LeftButton == ButtonState.Released)
                    {
                        item.ItemName = "empty";
                        pit.ItemName = "firepitwithtinder";
                        pit.Texture = Content.Load<Texture2D>("firepitwithtinder");
                    }
                }
            }
        }
        oldMouseState = currentMouseState;
    }
}

正如你所看到的,这看起来很丑陋,我认为有更好的方法可以做到这一点,但我不确定如何做到这一点。由于会有很多这类方法,我想知道最好的方法是什么?

3 个答案:

答案 0 :(得分:6)

似乎你可以通过使用一些LINQ完全摆脱(实际上隐藏)循环:

private void SetTinderInPit()
{
    MouseState currentMouseState = Mouse.GetState();

    if (player.NextToFirePit)
    {
        Item tinder = player.PlayerInventory.Items.FirstOrDefault(i => i.ItemName == "tinder");
        if (tinder != null)
        {
            Item firepit = allItemsOnGround.FirstOrDefault(i => i.ItemName == "firepit" && i.ItemRectangle.Contains(MouseWorldPosition));
            if (firepit != null && 
                currentMouseState.LeftButton == ButtonState.Pressed &&
                oldMouseState.LeftButton == ButtonState.Released)
            {
                tinder.ItemName = "empty";
                firepit.ItemName = "firepitwithtinder";
                firepit.Texture = Content.Load<Texture2D>("firepitwithtinder");
            }
       }
       oldMouseState = currentMouseState;
    }
}

这具有在找到物品时短路回路的附加优点。它还可以轻松检查除名称之外的其他内容(例如&#34; IsFlammable&#34;或&#34; CanContainFire&#34;属性),因此您可以使用多个项目而不仅仅是&#34; tinder&# 34;和&#34; firepit&#34;。

如果实际打算删除所有火坑和火种,请使用:

private void SetTinderInPit()
{
    MouseState currentMouseState = Mouse.GetState();

    if (player.NextToFirePit)
    {
        foreach (Item tinder in player.PlayerInventory.Items.Where(i => i.ItemName == "tinder")
        {
            foreach (Item firepit in allItemsOnGround.Where(i => i.ItemName == "firepit"))
            {
               if (firepit.ItemRectangle.Contains(MouseWorldPosition) &&
                currentMouseState.LeftButton == ButtonState.Pressed &&
                oldMouseState.LeftButton == ButtonState.Released)
                {
                   tinder.ItemName = "empty";
                   firepit.ItemName = "firepitwithtinder";
                   firepit.Texture = Content.Load<Texture2D>("firepitwithtinder");
                }
             }
       }
       oldMouseState = currentMouseState;
    }
}

快速警告;此代码将使用第一个 tinder删除所有 firepits,而其他修补程序将不会受到影响。我可以解开循环以删除所有内容,但此函数与提供的函数匹配;此外,我假设这不是预期的行为。

请注意,在任何地方都不需要ToList,因为您在枚举期间未修改集合。您始终可以修改集合中的项目,并通过以下测试进行验证:

class IntWrapper
{
    public int value;

    public IntWrapper(int value)
    {
        this.value = value;
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<IntWrapper> test = new List<IntWrapper>() { new IntWrapper(1), new IntWrapper(2), new IntWrapper(3), new IntWrapper(4), new IntWrapper(5) };

        foreach (IntWrapper i in test.Where(i => i.value == 1))
        {
            i.value = 0;
        }

        foreach (IntWrapper i in test)
        {
            Console.WriteLine(i.value);
        }

        Console.ReadLine();
    }
}

答案 1 :(得分:1)

我对现有代码进行的唯一真正改变是尽早移动对鼠标状态的检查,以避免在循环中多次检查。另外我会使用Linq来缩短条件(通过删除&#39; if&#39;语句):

MouseState currentMouseState = Mouse.GetState();

// I would get all the conditional checks out of the way up front first
if (player.NextToFirePit &&
    currentMouseState.LeftButton == ButtonState.Pressed &&
    oldMouseState.LeftButton == ButtonState.Released)
{
    foreach (var tinderItem in player.PlayerInventory.Items
        .Where(item => item.ItemName == "tinder"))
    {
        foreach (var firePit in allItemsOnGround
            .Where(item => item.ItemName == "firepit" &&
                           item.ItemRectangle.Contains(MouseWorldPosition)))
        {
            tinderItem.ItemName = "empty"; 
            firePit.ItemName = "firepitwithtinder";
            firePit.Texture = Content.Load<Texture2D>("firepitwithtinder");
        }

    }
}

oldMouseState = currentMouseState;

另一个想法,因为你正在寻找摆脱丑陋&#39;的方法。代码,将一些此功能移动到播放器对象。

答案 2 :(得分:0)

我可能会更多地使用LINQ。

请注意,这是用记事本编写的,而不是visual studio,因此更多的是伪代码。

private void SetTinderInPit()
{
    var currentMouseState = Mouse.GetState();
    if (!player.NextToFirePit) return;

    player.PlayerInventory.Items.Where(item => item.ItemName == "tinder").ToList().ForEach(item =>
    {
        allItemsOnGround.Where(x => x.ItemName == "firepit" &&
            x.ItemRectangle.Contains(MouseWorldPosition) &&
            currentMouseState.LeftButton == ButtonState.Pressed &&
            oldMouseState.LeftButton == ButtonState.Released)
                .ToList().ForEach(pit =>
                {
                    item.ItemName = "empty";
                    pit.ItemName = "firepitwithtinder";
                    pit.Texture = Content.Load<Texture2D>("firepitwithtinder");
                });
    });
    oldMouseState = currentMouseState;
}