Если вы хотите создать разрушаемую местность, то, как я это сделал в Unity, - это устанавливать коллайдеры только на краевые блоки вашего мира. Например, вот что вы хотели бы достичь:
Все эти зеленые блоки содержат коллайдер, а остальные нет. Это экономит массу вычислений. Если вы уничтожите блок, вы можете легко активировать коллайдеры на соседних блоках. Имейте в виду, что активация / деактивация коллайдера является дорогостоящей и должна выполняться экономно.
Итак, ресурс Tile выглядит так:
Это стандартный игровой объект, но он также может быть объединен. Также обратите внимание, что коллайдер бокса по умолчанию отключен. Мы активируем только если это край плитки.
Если вы статически загружаете свой мир, вам не нужно объединять свои плитки. Вы можете просто загрузить их все за один выстрел, рассчитать их расстояние от края и при необходимости применить коллайдер.
Если вы загружаете динамически, лучше всего использовать пул плиток. Вот отредактированный пример моего цикла обновления. Он загружает плитки в зависимости от текущего вида камеры:
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;
}
В идеале я бы написал гораздо более подробный пост, поскольку за кулисами происходит гораздо больше. Тем не менее, это может помочь вам. Если есть вопросы, не стесняйтесь спрашивать или свяжитесь со мной.