Blog

Real-time Terrain Deformation in Unity3D

June 10, 2010 by Devin Reimer

Real-time Terrain Deformation Screenshot

A few weeks back I got thinking. Would it be possible to have a terrain within Unity deform in real-time? For example if an explosion went off, could it blow a hole in the terrain? I decided this would be a good project to learn the more ‘advanced’ features of Terrains within Unity.

It ended up being more challenging than I predicted, as it was difficult to get the framerate to remain stable while performing these operation. Using a few tricks I ended up getting it to work exactly as I had hoped.

The demo works like this. A script randomly spawns ‘shells’ that fall quickly to the ground. Each shell has an onTrigger collider attached to it. If this collides with a terrain that has a TerrainDeformer component, the shell tells that component its location and the force of the explosion. The shell then instantiate an explosion (supplied by Ben Throop great Detonator framework), and removes itself.

The TerrainDeformer script then translates that position to the correct location relative to it’s terrain and modifies both the heightmap (terrain height) and the alphamap (terrain texture). A bit of math is used to find all the heightmap and alphamap position within the area of the impact circle.

The texture which is used to re-texture the impact area is chosen from the list of terrain textures based on the numerical index value passed into the script (Terrain Deformation Texture Num). In this example it is set to one, so it will re-texture with the second texture in the list.

When creating your own terrains it is necessary for the height of the terrain to be higher than 0 meters so craters can be formed. I recommend a depth of at least 3 meters. This can done by setting the terrain height to something higher than 3 meters, then clicking Terrain->Flatten Heightmap and entering 3 meters.

For performance reasons it is important to keep your terrain size small and more importantly keep your Heightmap Resolution low. In this example it is set to 33.

The current version does have limitations like lack of terrain edge detection and support for multiple terrains. This example is more of proof of concept to show that not only can it be done, but done without much of a performance hit.

Thanks to Calin for creating the dirt texture.

To check out the demo click here.

To get the source (unitypackage) click here.

Source Requirements: Unity 2.6 and the Detonator Framework.

19 Responses to "Real-time Terrain Deformation in Unity3D"

  1. Anthony says:

    This will be very helpful and time saving to look at. I want to have buildings and other things break and deform too in an open world game idea I have. Keep up the good work!

  2. headkit says:

    nice one. thanx!
    I am looking for a way to crumble terrain. do you know a way for this, too?

    • Devin Reimer says:

      @headkit I don’t know exactly what you mean by crumble. If you mean make ‘crater’ slowly, well yes you could using the same technique. That being said editing the terrain at runtime is performance hog. So lots of small changes can impact performance.

  3. flowerfuneral says:

    Hi Devin. This post is pretty nice. I have a question here:

    I tried your demo and I feel that the depth of the crate depends on the ‘terrain height’ of the terrain. For example, if the height is 6, the crate is fleet and if the height is 60, then the crate will be very deep. I wonder if it is possible that when the height is 60 and I can make some shallow crates just like when the height is 6?

    I modified the codes and nothing changes and the properties of terrain in Unity 3d really confused me. Can you offer some help here? Thanks.

  4. flowerfuneral says:

    Hi Devin, it was my mistake. Please ignore my last post.

  5. RushRage.com says:

    Wow, awesome! Thank you sooooooo much!

  6. markus says:

    Nice i look for a script or a way to cut holes in the mesh of teh terrain for make a dungeon entrance any ideas?

  7. Devin Reimer says:

    Hi @markus

    Depending on what you are wanting you might be able to use a shader to not render part of the terrain and a tunnel model to achieved that effect. If you are wanting to do this a lot might want to look into using voxel terrain or modelling all terrain without the editor.

  8. Andrew says:

    Hi,
    Much thanks. I’m in the process of designing a game where the players can modify the terrain (i.e. build traps for other players/AI agents to succumb to).

    I’m not familiar with Unity3D at all, but is there object inheritance (specifically for properties, methods etc…). Main reason is that I originally did some coding on a LPMUD a while ago.

    Thanks again,
    A.

  9. Dustin says:

    I’m working on a game where users can build building using basic parts that snap. But if I can’t create holes in terrains or blocks (for floors and windows), multi-floor buildings suddenly get very complicated :-\

  10. NAnou says:

    Hi,

    I tried your package on an existing project of mine, and noticed that my terrain got replaced with another, which i do not like at all. How do i get it back? I worked hours on that and just because i wanted to try it does not mean that it should delete/deform/replace my shit.

    • TheWolf says:

      @NAnou

      That’s why you should never import packages into your working projects. That’s why you should create backups of your work. These are general rules that apply to any sort of workflow. The fault here lies with you, fella.

    • Dan says:

      Normal people would respectfully backup a project before importing new assets.

  11. Dan says:

    Would you have an updated version of this Demo? Unity 4.2 doesn’t seems to execute it.

  12. Satu ini adalah foto termewah yang pernah ambo panyau hiongga hari ini.

    gw juga menyukai hasil sepakbolawebsite segera lihat ke http://Www.nuGoal.Com

  13. Parham says:

    //This code is written by myself. maybe it can help you.

    using UnityEngine;
    using System.Collections;
    using System.Collections;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    using System.Linq;
    using System.Text;
    public class terrain : MonoBehaviour {

    bool shouldDraw = false;
    public Terrain myTerrain;
    TerrainData tData;
    int xResolution;
    int zResolution;
    float[,] heights;
    public float speed = 0.01f;
    string speedString;

    void OnGUI(){
    if (GUI.Button (new Rect (Screen.width / 200, Screen.height / 200, 50, 50), “DrawHeight”)) {
    shouldDraw = true;
    }
    if (GUI.Button (new Rect (Screen.width / 200, Screen.height / 200 + 55, 50, 50), “EndDrawing”)) {
    shouldDraw = false;
    }
    speedString = GUI.TextField (new Rect (Screen.width / 200, Screen.height / 200 + 110, 60, 20), speedString, 4);
    }

    // Use this for initialization
    void Start () {
    speedString = speed.ToString();
    tData = myTerrain.terrainData;
    xResolution = tData.heightmapWidth;
    zResolution = tData.heightmapHeight;
    heights = tData.GetHeights (0, 0, xResolution, zResolution);
    }

    // Update is called once per frame
    void Update () {
    speed = Convert.ToSingle (speedString);
    if (shouldDraw == true && Input.GetMouseButton (0) && Input.GetKey (KeyCode.RightControl)) {
    RaycastHit hit;
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    if(Physics.Raycast( ray, out hit)){
    raiseTerrain(hit.point);

    }
    }
    if (shouldDraw == true && Input.GetMouseButton (1) && Input.GetKey (KeyCode.RightControl)) {
    RaycastHit hit;
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    if(Physics.Raycast( ray, out hit)){
    lowerTerrain(hit.point);
    }
    }
    }

    private void raiseTerrain(Vector3 point){
    int mouseX = (int)((point.x / tData.size.x) * xResolution);
    int mouseZ = (int)((point.z / tData.size.z) * zResolution);
    float[,] modHeights = new float[1, 1];
    float y = heights [mouseX, mouseZ];
    y += (speed / 10) * Time.deltaTime;
    if (y > tData.size.y)
    y = tData.size.y;
    modHeights [0, 0] = y;
    heights [mouseX, mouseZ] = y;
    tData.SetHeights (mouseX, mouseZ, modHeights);

    }
    private void lowerTerrain(Vector3 point){
    int mouseX = (int)((point.x / tData.size.x) * xResolution);
    int mouseZ = (int)((point.z / tData.size.z) * zResolution);
    float[,] modHeights = new float[1, 1];
    float y = heights [mouseX, mouseZ];
    y -= (speed / 10) * Time.deltaTime;
    if (y < 0.0f)
    y = 0.0f;
    modHeights [0, 0] = y;
    heights [mouseX, mouseZ] = y;
    tData.SetHeights (mouseX, mouseZ, modHeights);
    }

    }

Leave a response