【问题标题】:Getting an error for one of my Queues.. need help figuring it out我的一个队列出现错误..需要帮助来解决
【发布时间】:2014-11-03 19:33:02
【问题描述】:

我正在使用 Unity 开发一款游戏,该游戏在六角棋盘上玩,由您控制的单位。当一个单位被选中时,它周围的格子会亮起,表示(通过颜色 - 蓝色移动,橙色攻击),你可以移动到它们,或者攻击它们上面的任何东西。这是一个截图供参考:

这是我显示十六进制的函数:

//breadth first search
//has built in range checker so you dont have to check the path length of every individual node
//works whether attackRange or moveRange is longer
public void showUnitsHexes(ArmyUnit unitScript, bool trueShowFalseHide)
{
    //HIDING
    if(trueShowFalseHide == false)
    {
        foreach(TileNode node in nodesToHide)
        {
            //change the mat back to the default white
            comReader.changeNodeMat(node, CommandReader.hexMats.defaultt);

            node.Hide();
        }

        nodesToHide.Clear();
        return;
    }

    //SHOWING
    //Only show if its your turn (return on enemy turn)
    if ((comReader.playerNum == getWhichButtonAndPlayer.playerType.P1 && comReader.CurrPlayerTurn == 2)
    || (comReader.playerNum == getWhichButtonAndPlayer.playerType.P2 && comReader.CurrPlayerTurn == 1))
        return;

    #region SETUP
    //these were originally parameters but it's easier to just set them in the funct itself
    TileNode startNode = unitScript.node;
    int attackRange = unitScript.attackRange;
    int moveRange = unitScript.moveRange;

    //safety-net
    if (startNode == null || (attackRange < 1 && moveRange < 1))
        return;

    //how to know when you're done
    int finishedRadius;
    if (moveRange > attackRange)
        finishedRadius = moveRange;
    else
        finishedRadius = attackRange;
    #endregion

    //get the list of nodes not to go over twice, and the queue of nodes to go through
    List<TileNode> nodesPassedAlready = new List<TileNode>();
    nodesPassedAlready.Add(startNode);
    Queue<TileNode> nodesToGoToInCurrentRadius = new Queue<TileNode>();
    Queue<TileNode> nodesToGoToInNextRadius = new Queue<TileNode>();
    int currentRadius = 1;
    bool finished = false;

    //add the 6 nodes surrounding the initial node to the queue to start things off
    foreach (TileNode n in startNode.nodeLinks)
        nodesToGoToInCurrentRadius.Enqueue(n);

    //while the queue is not empty...
    while (finished == false)
    {
        //END CHECK
        //if done in current radius, need to go to next radius
        if (nodesToGoToInCurrentRadius.Count < 1)
        {
            if (currentRadius == finishedRadius)
            {
                finished = true;
                continue;
            }
            else
            {
                currentRadius++;
                nodesToGoToInCurrentRadius = nodesToGoToInNextRadius;
                nodesToGoToInNextRadius = new Queue<TileNode>();
            }
        }

        //...get the next node and...
        TileNode n = nodesToGoToInCurrentRadius.Dequeue();

        //...if there's no issue...
        #region safety check
        //...if the node is null, has already been shown, is inhabited by a non-unit, don't bother with it
        if (n == null || nodesPassedAlready.Contains(n) || comReader.hexIsInhabited(n, false, true))
            continue;

        //...if is inhabited and outside of attackRange, or inhabited by a friendly unit, don't bother with it
        //NOTE: rather than combining this with the above if check, leave it separated (and AFTER) to avoid errors when n == null)
        ArmyUnit currUnit = comReader.CurrentlySelectedUnit;
        if (comReader.hexIsInhabited(n, true, true) && (currentRadius > currUnit.attackRange || comReader.unitIsMine(comReader.getUnitOnHex(n).GetComponent<ArmyUnit>())))
            continue;
        #endregion

        //...1) show it
        #region show nodes
        //show as requested, add it to the list

        //change the mat to whatever color is relevant: if n is inhabited and in attack range, color atkColor, otherwise color moveColor
        if (comReader.hexIsInhabited(n, true, true) && currentRadius <= attackRange)
        {
            Debug.Log("attackRange: " + attackRange);
            Debug.Log(n.name);
            if (n.name.Equals("node345"))
                Debug.Log("checked it");

            comReader.changeNodeMat(n, CommandReader.hexMats.attack);
            n.Show();
            nodesToHide.Add(n);
        }

        //make sure hex is in moveRange.  possible that it isnt, if attack range > moveRange
        else if (moveRange >= currentRadius)
        {
            comReader.changeNodeMat(n, CommandReader.hexMats.move);
            n.Show();
            nodesToHide.Add(n);
        }

        //do not take n.show() out of those braces.  If you do, it will sometimes show white nodes and you don't want to show them
        #endregion

        //...2) don't go over it a second time
        nodesPassedAlready.Add(n);

        //...and 3) add all surrounding nodes to the queue if they haven't been gone over yet AND ARE NOT ALREADY IN THE QUEUE
        foreach (TileNode adjacentNode in n.nodeLinks)
        {
            #region safety check
            //...if the node is null or has already been shown or is inhabited by a non-unit, don't bother with it
            if (adjacentNode == null || nodesPassedAlready.Contains(adjacentNode) || comReader.hexIsInhabited(adjacentNode, false, true))
                continue;

            //...if is inhabited and outside of attackRange, don't bother with it
            //NOTE: rather than combining this with the above if check, leave it separated (and AFTER) to avoid errors when n == null)
            //currUnit already defined in the above safetycheck
            if (comReader.hexIsInhabited(adjacentNode, true, true) && currentRadius > currUnit.attackRange)
                continue;
            #endregion

            nodesToGoToInNextRadius.Enqueue(adjacentNode);
        }
    }
}

我的问题是,当我将攻击范围设置为大于地图大小(设置为 30,地图为 12x19)时,出现错误:

InvalidOperationException:由于对象的当前状态,操作无效 System.Collections.Generic.Queue`1[TileNode].Peek()

同样,当我将射程设置为 17 或 18 时——足以从你在图片中看到我的单位的位置攻击敌方基地——该基地的节点甚至从未在函数中查看过,尽管它在攻击范围内。

这个错误信息是什么意思?我的逻辑错误在哪里?抱歉,如果这写得很草率-我很乐意回答您可能遇到的任何问题。谢谢!

【问题讨论】:

    标签: c# generics collections unity3d queue


    【解决方案1】:

    nodesToGoToInNextRadius 有没有可能是空的?

    Queue.Dequeue 在下面调用 Queue.Peek 并在队列为空时抛出 InvalidOperationException。 您将 nodesToGoToInNextRadius 分配给 nodesToGoToInCurrentRadius:

    nodesToGoToInCurrentRadius = nodesToGoToInNextRadius;
    

    并且不再检查队列中是否有任何东西。

        if (nodesToGoToInCurrentRadius.Count < 1) // that's fine 
        {
            if (currentRadius == finishedRadius)
            {
                finished = true;
                continue;
            }
            else
            {
                currentRadius++;
                //1. now you are swapping queues
    
                nodesToGoToInCurrentRadius = nodesToGoToInNextRadius; 
                nodesToGoToInNextRadius = new Queue<TileNode>();
            }
        }
    
        //2. and calling Dequeue on swapped queue without checking if it's empty.
        TileNode n = nodesToGoToInCurrentRadius.Dequeue();
    

    【讨论】:

    • 感谢您非常详细的回答!我在 if 案例之后添加了一个检查(在 TileNoden = ...Dequeue() 之前)。它所做的只是说如果 nodesToGoToInCurrentRadius 为空,请继续。我没有得到错误,所以这可能是导致它的原因。然而,它仍然根本没有检查敌人基地所在的节点
    • 我看不到任何明显的东西。您是否尝试过在每个重要部分设置断点并逐步执行?尝试添加更多 Debug.Log() 以查看所有已处理节点的完整视图。
    猜你喜欢
    • 2023-04-09
    • 2020-08-06
    • 2013-06-10
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多