Estou fazendo um projeto low pixel, e gostaria de fazer o solo destrutivo durante o combate, o que eu faço? faço o solo ter vida? e área de dano no solo? além disso a ideia é ser um mapa imenso futuramente, seria melhor fazer vários mapas pequenos e ir anexando e sobrepondo? ou um só?
Existe várias formas de fazer isso. Aqui está um script bem simples que você pode colocar em um Cubo (com Rigidbody e Collider). Quando ele colidir com o Terrain da Unity, ele vai cavar um pequeno buraco no local da colisão e depois se destruir:
using UnityEngine;
public class TerrainDeformer : MonoBehaviour
{
public float radius = 5f;
public float depth = 0.02f;
private static float[,] originalHeights;
private static bool terrainBackupDone = false;
private static Terrain terrain;
private void Start()
{
// Pega o terreno da cena uma vez
if (!terrain)
{
terrain = FindObjectOfType<Terrain>();
}
// Faz backup das alturas originais apenas uma vez
if (terrain && !terrainBackupDone)
{
originalHeights = terrain.terrainData.GetHeights(0, 0,
terrain.terrainData.heightmapResolution,
terrain.terrainData.heightmapResolution);
terrainBackupDone = true;
}
}
private void OnCollisionEnter(Collision collision)
{
if (!terrain) return;
if (collision.collider.GetComponent<Terrain>())
{
TerrainData terrainData = terrain.terrainData;
Vector3 collisionPoint = collision.contacts[0].point;
Vector3 terrainPos = terrain.transform.InverseTransformPoint(collisionPoint);
int posX = Mathf.FloorToInt((terrainPos.x / terrainData.size.x) * terrainData.heightmapResolution);
int posZ = Mathf.FloorToInt((terrainPos.z / terrainData.size.z) * terrainData.heightmapResolution);
int holeRadius = Mathf.RoundToInt(radius);
float[,] heights = terrainData.GetHeights(posX - holeRadius, posZ - holeRadius, holeRadius * 2, holeRadius * 2);
for (int x = 0; x < holeRadius * 2; x++)
{
for (int z = 0; z < holeRadius * 2; z++)
{
float dist = Vector2.Distance(new Vector2(x, z), new Vector2(holeRadius, holeRadius));
if (dist < holeRadius)
{
heights[x, z] -= depth;
if (heights[x, z] < 0) heights[x, z] = 0;
}
}
}
terrainData.SetHeights(posX - holeRadius, posZ - holeRadius, heights);
Destroy(gameObject);
}
}
// Esse método é chamado automaticamente quando sai do Play Mode (somente no Editor)
#if UNITY_EDITOR
[UnityEditor.Callbacks.DidReloadScripts]
private static void OnReloadScripts()
{
RestoreTerrain();
}
[UnityEditor.InitializeOnLoadMethod]
private static void RegisterExitPlaymodeCallback()
{
UnityEditor.EditorApplication.playModeStateChanged += state =>
{
if (state == UnityEditor.PlayModeStateChange.ExitingPlayMode)
{
RestoreTerrain();
}
};
}
private static void RestoreTerrain()
{
if (terrain && originalHeights != null)
{
terrain.terrainData.SetHeights(0, 0, originalHeights);
Debug.Log("Terreno restaurado após o Play Mode.");
terrainBackupDone = false;
}
}
#endif
}
Como usar:
- Crie um terreno na cena (
GameObject > 3D Object > Terrain
). - Crie um cubo (
GameObject > 3D Object > Cube
) e posicione acima do terreno. - Adicione um Rigidbody no cubo.
- Anexe o script acima no cubo.
- Dê
Play
. Quando o cubo colidir com o terreno, ele cria um buraco e some.
Esse é apenas um exemplo básico de deformação de terreno em tempo real. Já sobre o tamanho do mapa: se a ideia for criar um mundo bem grande, o mais recomendado é dividir o cenário em vários terrenos menores e interconectados. Essa abordagem não só facilita o gerenciamento, como também é bem mais eficiente em termos de performance.