Wenn Sie zerstörbares Terrain erschaffen möchten, besteht meine Vorgehensweise in Unity darin, Collider nur an den Randblöcken Ihrer Welt zu platzieren. So möchten Sie zum Beispiel Folgendes erreichen:
Alle diese grünen Blöcke enthalten einen Collider, der Rest nicht. Das spart eine Menge Rechenaufwand. Wenn Sie einen Block zerstören, können Sie die Collider auf benachbarten Blöcken ziemlich einfach aktivieren. Denken Sie daran, dass das Aktivieren / Deaktivieren eines Colliders teuer ist und sparsam erfolgen sollte.
Die Tile-Ressource sieht also folgendermaßen aus:
Es ist ein Standard-Spielobjekt, aber es kann auch gepoolt werden. Beachten Sie auch, dass der Box-Collider standardmäßig deaktiviert ist. Wir würden nur aktivieren, wenn es sich um ein Randplättchen handelt.
Wenn Sie Ihre Welt statisch laden, müssen Sie Ihre Kacheln nicht bündeln. Sie können sie alle auf einmal laden, ihren Abstand von der Kante berechnen und bei Bedarf einen Collider anwenden.
Wenn Sie dynamisch laden, empfiehlt es sich, einen Kachelpool zu verwenden. Hier ist ein bearbeitetes Beispiel für meine Aktualisierungsschleife. Es werden Kacheln basierend auf der aktuellen Kameraansicht geladen:
public void Refresh(Rect view)
{
//Each Tile in the world uses 1 Unity Unit
//Based on the passed in Rect, we calc the start and end X/Y values of the tiles presently on screen
int startx = view.x < 0 ? (int)(view.x + (-view.x % (1)) - 1) : (int)(view.x - (view.x % (1)));
int starty = view.y < 0 ? (int)(view.y + (-view.y % (1)) - 1) : (int)(view.y - (view.y % (1)));
int endx = startx + (int)(view.width);
int endy = starty - (int)(view.height);
int width = endx - startx;
int height = starty - endy;
//Create a disposable hashset to store the tiles that are currently in view
HashSet<Tile> InCurrentView = new HashSet<Tile>();
//Loop through all the visible tiles
for (int i = startx; i <= endx; i += 1)
{
for (int j = starty; j >= endy; j -= 1)
{
int x = i - startx;
int y = starty - j;
if (j > 0 && j < Height)
{
//Get Tile (I wrap my world, that is why I have this mod here)
Tile tile = Blocks[Helper.mod(i, Width), j];
//Add tile to the current view
InCurrentView.Add(tile);
//Load tile if needed
if (!tile.Blank)
{
if (!LoadedTiles.Contains(tile))
{
if (TilePool.AvailableCount > 0)
{
//Grab a tile from the pool
Pool<PoolableGameObject>.Node node = TilePool.Get();
//Disable the collider if we are not at the edge
if (tile.EdgeDistance != 1)
node.Item.GO.GetComponent<BoxCollider2D>().enabled = false;
//Update tile rendering details
node.Item.Set(tile, new Vector2(i, j), DirtSprites[tile.TextureID], tile.Collidable, tile.Blank);
tile.PoolableGameObject = node;
node.Item.Refresh(tile);
//Tile is now loaded, add to LoadedTiles hashset
LoadedTiles.Add(tile);
//if Tile is edge block, then we enable the collider
if (tile.Collidable && tile.EdgeDistance == 1)
node.Item.GO.GetComponent<BoxCollider2D>().enabled = true;
}
}
}
}
}
}
//Get a list of tiles that are no longer in the view
HashSet<Tile> ToRemove = new HashSet<Tile>();
foreach (Tile tile in LoadedTiles)
{
if (!InCurrentView.Contains(tile))
{
ToRemove.Add(tile);
}
}
//Return these tiles to the Pool
//this would be the simplest form of cleanup -- Ideally you would do this based on the distance of the tile from the viewport
foreach (Tile tile in ToRemove)
{
LoadedTiles.Remove(tile);
tile.PoolableGameObject.Item.GO.GetComponent<BoxCollider2D>().enabled = false;
tile.PoolableGameObject.Item.GO.transform.position = new Vector2(Int32.MinValue, Int32.MinValue);
TilePool.Return(tile.PoolableGameObject);
}
LastView = view;
}
Im Idealfall würde ich einen viel ausführlicheren Beitrag verfassen, da sich hinter den Kulissen einiges mehr abspielt. Dies kann Ihnen jedoch helfen. Bei Fragen stehe ich Ihnen gerne zur Verfügung.