簡體   English   中英

擺脫不必要的循環

[英]Getting rid of unnecessary loops

在我的游戲中,我將要進行很多互動,其中我需要查看玩家是否有物品,如果他有物品並且其他事情是正確的,那么就采取行動。 在以下代碼中描述。

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

這具有在找到項目時使回路短路的附加優勢。 它還可以輕松檢查名稱以外的其他內容(例如“ IsFlammable”或“ CanContainFire”屬性),因此您可以使用多個項目,而不僅僅是“ tinder”和“ firepit”。

如果您實際上打算移除所有火堆和火種,請使用:

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。

請注意,這是在記事本中而不是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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM