去掉不必要的循环

本文关键字:循环 不必要 | 更新日期: 2023-09-27 18:26:57

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

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

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

去掉不必要的循环

似乎可以通过使用一些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;
    }
}

这还有一个额外的优点,即当找到物品时会使回路短路。它还可以很容易地检查名称以外的东西(如"Is易燃"或"CanContainFire"属性),这样你就可以使用多个项目,而不仅仅是"火绒"answers"火坑"。

如果实际上打算清除所有火坑和火绒,请使用:

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

快速警告;此代码将删除所有带有第一个火绒的火坑,使其他火绒毫发无损。我可以解开循环来删除所有内容,但这个功能与提供的功能相匹配;此外,我认为这不是预期的行为。

请注意,在任何位置都不需要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();
    }
}

我对现有代码所做的唯一真正的更改是在早期移动对鼠标状态的检查,以避免在循环中多次检查。此外,我会使用Linq来缩短条件(通过删除"if"语句):

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;

另一个想法是,由于您正在寻找一种方法来消除"丑陋"的代码,将把一些功能移到播放器对象中。

我可能会更多地使用LINQ。

请注意,这是在Notepad中编写的,而不是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;
}