Si vous cherchez à créer un terrain destructible, la façon dont je l'ai fait dans Unity, c'est de placer les collisionneurs uniquement sur les blocs de bord de votre monde. Ainsi, par exemple, voici ce que vous aimeriez accomplir:
Tous ces blocs verts contiennent un collisionneur, et les autres non. Cela économise une tonne de calculs. Si vous détruisez un bloc, vous pouvez activer les collisionneurs sur les blocs adjacents assez facilement. Gardez à l'esprit que l'activation / la désactivation d'un collisionneur est coûteuse et doit être effectuée avec parcimonie.
Ainsi, la ressource Tile ressemble à ceci:
C'est un objet de jeu standard, mais il peut également être mis en commun. Notez également que le collisionneur de boîtes est désactivé par défaut. Nous ne l'activerions que s'il s'agit d'une tuile de bord.
Si vous chargez statiquement votre monde, il n'est pas nécessaire de regrouper vos tuiles. Vous pouvez simplement les charger en une seule fois, calculer leur distance par rapport au bord et appliquer un collisionneur si nécessaire.
Si vous chargez dynamiquement, il est préférable d'utiliser un pool de tuiles. Voici un exemple édité de ma boucle de rafraîchissement. Il charge les tuiles en fonction de la vue actuelle de la caméra:
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;
}
Idéalement, j'écrirais un article beaucoup plus détaillé, car il se passe beaucoup plus de choses en coulisses. Cependant, cela peut vous aider. S'il y a des questions, n'hésitez pas à me contacter ou à me contacter.