我正在嘗試制作一個掃雷游戲,在該游戲中我制作了這種方法來使用佇列集合迭代地淹沒未知瓷磚
private void FloodIterative(Tile tile)
{
Queue<Tile> queue = new Queue<Tile>();
queue.Enqueue(tile);
while (queue.Count != 0)
{
Tile b = queue.Dequeue();
if (b.isDiscovered) continue;
if (b.type == Tile.Type.Mine || b.type == Bunka.Type.Invalid) continue;
b.isDiscovered = true;
state[b.position.x, b.position.y] = b;
if (b.type == Bunka.Type.Empty)
{
queue.Enqueue(GetBunka(b.position.x - 1, b.position.y));
queue.Enqueue(GetBunka(b.position.x 1, b.position.y));
queue.Enqueue(GetBunka(b.position.x, b.position.y - 1));
queue.Enqueue(GetBunka(b.position.x, b.position.y 1));
queue.Enqueue(GetBunka(b.position.x - 1, b.position.y - 1));
queue.Enqueue(GetBunka(b.position.x 1, b.position.y 1));
queue.Enqueue(GetBunka(b.position.x 1, b.position.y - 1));
queue.Enqueue(GetBunka(b.position.x - 1, b.position.y 1));
}
}
}
此方法效果很好,但僅適用于有限數量的未探索瓷磚。如果瓦數太大,我的統一引擎輸出:
OutOfMemoryException:記憶體不足
System.Collections.Generic.Queue 1[T].SetCapacity (System.Int32 容量) (at <5a2009c85b134970925993880e2ecb2e>:0) System.Collections.Generic.Queue 1[T].Enqueue (T item) (at <5a2009c85b134970925993880e2ecb2e>: 0)
有什么解決辦法嗎?我是否可能需要手動釋放記憶體?非常感謝你。
uj5u.com熱心網友回復:
你提到的數字(0.5GB 和 5GB)讓我擔心除了@joshua-robinson 指出的之外還有其他問題。如果我們假設您有一個 100x100 的網格,并且您當前的方法可能每Tile
8 次添加到佇列中,這意味著5GB / (100 * 100 * 8) == 65kB
每個圖塊。這對于單個來說是很多Tile
記憶體- 這是假設GetBunka
實體化一個新Tile
物件而不是在給定位置重用同一個Tile
物件。如果您測驗的網格更小,則每個的記憶體量Tile
會更高。
在不知道內容的情況下,Tile
我認為它應該占用的空間遠小于 1kB。
但是為了解決手頭的問題,我們可以使用 aHashSet
來跟蹤我們已經查看過的圖塊,以避免將它們多次添加到佇列中。
private void FloodIterative(Tile tile)
{
Queue<Tile> queue = new Queue<Tile>();
queue.Enqueue(tile);
// Tiles we have had a peak at
// I.e. they were added to the queue at some point
var tilesPeakedAt = new HashSet<Tile>(queue);
while (queue.Count != 0)
{
Tile b = queue.Dequeue();
if (b.isDiscovered) continue;
if (b.type == Tile.Type.Mine || b.type == Bunka.Type.Invalid) continue;
b.isDiscovered = true;
state[b.position.x, b.position.y] = b;
if (b.type == Bunka.Type.Empty)
{
var tilesToCheck = GetAllNeighbors(b)
.Where(t => !tilesPeakedAt.Contains(t));
foreach (var t in tilesToCheck)
{
queue.Enqueue(t);
tilesPeakedAt.Add(t);
}
}
}
}
private Tile[] GetAllNeighbors(Tile b) // Position as input would be enough
{
return new []
{
GetBunka(b.position.x - 1, b.position.y)
GetBunka(b.position.x 1, b.position.y)
GetBunka(b.position.x, b.position.y - 1)
GetBunka(b.position.x, b.position.y 1)
GetBunka(b.position.x - 1, b.position.y - 1)
GetBunka(b.position.x 1, b.position.y 1)
GetBunka(b.position.x 1, b.position.y - 1)
GetBunka(b.position.x - 1, b.position.y 1)
};
}
這應該會將放入佇列的圖塊數量減少多達 8 倍。
uj5u.com熱心網友回復:
這是我不久前制作的掃雷游戲的片段。
它有助于了解給定圖塊周圍有多少地雷。這可以在網格的初始構建期間設定。
private void RevealEmptyAdjacentTiles(Point point)
{
// Create a list to store the next tiles we need to check
//
var nextTilesToCheck = new List<Tile>();
// Loop through the adjacent tiles that are not yet revealed
//
foreach (var tile in GetAdjacentUnrevealedTiles(point))
{
// Make sure this tile is not a mine
//
if (!tile.IsMine)
{
// Reveal the tile
//
tile.Reveal();
// We also want to check this tiles adjacent unrevealed tiles, add it to our list for checking after this loop completes
//
nextTilesToCheck.Add(tile);
}
}
// Loop through all the new tiles we need to check
//
foreach (var adjacentTile in nextTilesToCheck)
{
// If there are no surrounding mines, lets run this function again for this coordinate
//
if (adjacentTile.SurroundingMinesCount == 0)
{
// Recursion
//
RevealEmptyAdjacentTiles(adjacentTile.Coordinate);
}
}
}
我們只關心尚未顯示的瓷磚。在檢查那些未顯示的相鄰圖塊時,我們會顯示該圖塊(除非它是地雷),隨后這些圖塊將永遠不需要再次檢查。
uj5u.com熱心網友回復:
似乎您為每個取出的物品排隊 8 件,并且您排隊重復。在將專案放入佇列之前檢查它是否已經存在。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/461608.html
上一篇:Unity輸入不作業,如何解決?