2014-11-03 53 views
1

我正在Unity中進行一個遊戲,這個遊戲是在您控制的單位的六面板上播放的。當一個單位被選中時,它周圍的格子被點亮,表示(通過顏色 - 藍色移動,橙色攻擊),你可以移動到他們身上,或者攻擊他們。下面是引用一個截圖:獲取我的一個隊列的錯誤..需要幫助搞清楚

http://i.imgur.com/UbijlD8.jpg

這裏是我的功能,顯示不吉利的東西:

//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: Operation is not valid due to the current state of the object System.Collections.Generic.Queue`1[TileNode].Peek()

同樣的,當我設定的範圍,以17或18 - 足以能夠攻擊從那裏你看到敵人的基地,我圖片中的單位 - 儘管它在攻擊範圍內,該基地的節點甚至從未在該功能中看過。

這個錯誤信息是什麼意思?我的邏輯錯誤在哪裏?對不起,如果這是不經意地寫 - 我會很樂意回答你可能有任何問題。謝謝!

回答

2

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(); 
+0

謝謝你的非常詳細的答案!我在if case(在TileNoden = ... Dequeue())之前添加了一個檢查。它所做的只是說,如果nodesToGoToInCurrentRadius爲空,請繼續。我沒有得到這個錯誤,所以這可能是導致它的原因。但是,它甚至沒有檢查到敵方基地的節點。 – user3808547 2014-11-04 05:57:27

+0

我看不出任何明顯的東西。你有沒有嘗試在每個重要部分設置中斷點並逐步完成?嘗試添加更多的Debug.Log()以獲得所有處理節點的完整視圖。 – b2zw2a 2014-11-04 07:20:40

相關問題