Terrain API at runtime : how to correctly set / reset / init ?

Hi,

I am having difficulties with object Terrain and runtime modifications - things works well in editor, but not in build -

according to this


So i forget the idea of creating terrain at runtime, and try to redesign the code to use a pool a terrain object pre-created in editor. X terrain, with X own terrain datas.

Now the problem is elsewhere,

  • at first execution, the world script modify all the terrains object / grass, tree, heighmap … -
  • back to the editor: warning & console error everywhere “Terrain detail doesnt exist” “invalid prototype” “terrain layer missing” …refuse to rebuild (but play mode works)

I tried the following approch to “clean terrain” on application exit

void OnApplicationQuit()
    {
        // reassign a fake persistent terrain layer
        TerrainLayer refLayer = Resources.Load<TerrainLayer>("....f");
        List<TerrainLayer> listeTerrainLayers = new List<TerrainLayer>();
        listeTerrainLayers.Add(refLayer);
        float[,,] map = new float[128, 128, 1];
        for( int i = 0 ; i < 128 ; ++i)
        {
            for( int j = 0 ; j < 128 ; ++j)
            {
                map[i,j,0] = 1;
            }
        }
         terrain.terrainData.terrainLayers = listeTerrainLayers.ToArray();           
         terrain.terrainData.SetAlphamaps(0, 0, map);           

// remove all trees
            List<TreePrototype> tlTrress = new List<TreePrototype>();
            terrain.terrainData.treePrototypes = tlTrress.ToArray();

            // remove all details
            List<DetailPrototype> tlDetails = new List<DetailPrototype>();
            terrain.terrainData.detailPrototypes = tlDetails.ToArray();
            terrain.Flush();    
    }

But it doesnt “reset” correctly the terrain. On editor, the terrain has changed as wished, and seems reseted, but there is still multiple “under the hood” red terrain Errors, the terrain has reference to “previous” objects somewhere.

I am not successfull to correctly refresh the object :frowning:


blocked :sweat_smile:

  • can’t dynamicly create the terrain and populate it via script - cause it doesn’t work in build mode
  • with pre-created terrains instead, it works in build mode, but the terrains objects end being “corrupted in some way”, and it’s misery in editor to reinit things…

Thanks if anyone can help !

I tried to upgrade to unity 2022.3.8
Build mode => crash

in log :

 t.terrainData.detailPrototypes = herbeProto;

only implemented in editor
followed by
Crash!!!
SymInit: Symbol-SearchPath: ’


is it a new restriction ? or a bug because of “script generated terrain” ?
If it is, how to calculate and set “grass” at runtime ?

thanks

Here’s my script to remove all tree instances and tree prototypes. You might be getting an error because you are removing the prototypes without removing the instances first. Hopefully this will help you also remove the detail instances and prototypes.

using UnityEngine;
// Clear existing tree instances
// https://discussions.unity.com/t/how-to-clear-treeinstance-list/147166
public class ClearTreeInstances : MonoBehaviour
{
[ContextMenu("Remove Trees")]
public void RemoveTrees()
{
Terrain terrain = Terrain.activeTerrain;
if (terrain == null)
{
return;
}
TerrainData terrainData = terrain.terrainData;
if (terrainData == null)
{
return;
}
TreeInstance[ ] instances = new TreeInstance[0];
terrainData.SetTreeInstances(instances, false);
TreePrototype[ ] treePrototypes = new TreePrototype[0];
terrainData.treePrototypes = treePrototypes;
terrainData.RefreshPrototypes();
terrain.Flush();
}
void OnDisable()
{
RemoveTrees();
}
void OnApplicationQuit()
{
RemoveTrees();
}
}

According to the docs you need to have at least one scene with a terrain in it as part of your build before you can create a terrain at runtime. Unity - Manual: Using Terrain at runtime

1 Like

Thanks,

I dropped all dynamic gen terrain, with your function, i can now build & get backto editor and build again ! (adding a hidden terrain didnt solve the textures not apparing on createTerrain unfortunatly)

But i still have the weird crash, it seems to occur the second time when i recycle a terrain and set a new detail prototype array on a terrain.

only implemented in editor :
terrainData.detailPrototypes = herbeProto;

a bit weird, the first “init” is the exact same code portion and it doesn’t make this error

any other tips for grass :slight_smile: ?

I can’t see herbeProto in your script, but it may be the same problem where you’re deleting the prototypes before clearing the terrain of the details that rely on the prototype.

Before terrainData.RefreshPrototypes(); try adding this:

// The DetailPrototype array must be overwritten
DetailPrototype[ ] detailPrototypes = new DetailPrototype[0];
terrainData.detailPrototypes = detailPrototypes;```

If that doesn't work then try this:
```int detailPrototypesCount = terrainData.detailPrototypes.Length;
for (int i = detailPrototypesCount - 1; i >= 0; i--)
{
terrainData.RemoveDetailPrototype(i);
}```

thanks, the issue progress

Your are right, it is a similar issue.
Changing prototype while previous “instance” still in use

With this code i fixed the “crash”
Remove all terrainDetailLayer before changing them

// remove previous details location if any
        int detailPrototypesCount = t.terrainData.detailPrototypes.Length;
        if (detailPrototypesCount > 0)
        {
            // empty map 
            var emptyMap = t.terrainData.GetDetailLayer(0, 0, t.terrainData.detailWidth, t.terrainData.detailHeight, 0);
            for (int y = 0; y < t.terrainData.detailHeight; y++)
            {
                for (int x = 0; x < t.terrainData.detailWidth; x++)
                {
                    emptyMap[x, y] = 0;
                }
            }

           // for each layer remove
            for (int i= detailPrototypesCount - 1; i>= 0; i--) {
                t.terrainData.SetDetailLayer(0, 0, i, emptyMap);
                Debug.Log("MMM delete detail map " + i);
            }
    
        }

// new details
terrainData.detailPrototypes = herbeProto;

But still have the red warning “only implemented in editor” :frowning:


```csharp
// ( a detail looks like )
    public static DetailPrototype LoadDetailsTexture(string strpath)
    {
        DetailPrototype aDetail = new DetailPrototype();
        aDetail.minWidth = 0.3f;
        aDetail.maxWidth = 0.5f;
        aDetail.minHeight = 0.3f;
        aDetail.maxHeight = 0.5f;
        aDetail.noiseSpread = 0.8f;
        aDetail.noiseSeed= Random.Range(1500,10000);

        Color c1 = new Color(
            Random.Range(0f, 1f),
            Random.Range(0f, 1f),
            Random.Range(0f, 1f)
        );

        Color c2 = new Color(
            Random.Range(0f, 1f),
            Random.Range(0f, 1f),
            Random.Range(0f, 1f)
        );


        aDetail.healthyColor = c1;
        aDetail.dryColor = c2;
        aDetail.prototype = (GameObject)null;
        aDetail.prototypeTexture = Resources.Load<Texture2D>(strpath);
        //aDetail.renderMode = DetailRenderMode.GrassBillboard;// !this.m_Billboard ? DetailRenderMode.Grass : DetailRenderMode.GrassBillboard;
        aDetail.renderMode =DetailRenderMode.Grass;
        aDetail.usePrototypeMesh = false;
        return aDetail;
    }

I haven’t seen that error before. Running your code in my script didn’t cause that issue.

Here’s a thread where people are having issues with the game behaving differently outside of the editor:
https://discussions.unity.com/t/669308

Is the terrain or anything else in your scene using the “Editor Only” tag?
Are you using OnValidate() instead of Start(), Awake() or OnEnable()?
Is strpath in the Resources folder? If not it might not work outside of the editor.

I dont see this in the code.

I have an idea of what is happening :

  1. “test”
t.terrainData.RemoveDetailPrototype(i); 
 // <-- red error in build mode : only implemented in editor
  1. I have a terrain “pool / tile” system, the message happens when i ‘recycle’ an existing terrain (in game while walking i take the “-1” terrain and re compute it so it become the “+1” futur terrain)
    this line
terrainData.detailPrototypes = herbeProto;
 // <-- red error in build mode : 40x only implemented in editor  if i have 40 details..
// all the proto are supposed to be valide
 for (int i = 0 ; i < herbeProto.Length ; ++i) {
            Debug.Log("herbe" + i + " validate ?" + herbeProto[i].Validate());
        }

i Think that when i reassign a detailPrototypes, it calls underthehood “RemoveDetailPrototype” on each of the existing one, but it trigger the error. Maybe i am wrong.
I suppose its ‘no’, but can we ‘look’ the terrainData class code somewhere to see exactly what is happening, and try to locate the source of the problem ?

i keep trying things, and thanks for your time btw :slight_smile:

Is terrain.bakeLightProbesForTrees set to true? That’s the only property in either Terrain or TerrainData that mentions being editor only.

In TerrainData there’s also this function, but I assume you aren’t calling it.
public void SetTerrainLayersRegisterUndo(TerrainLayer[ ] terrainLayers, string undoName)

Edit: If you have using UnityEditor; in your script, it’s better to wrap it in an ifdef like this:

#if UNITY_EDITOR
using UnityEditor;
#endif

You may have to copy each detailPrototype in a loop instead of just copying the array with terrainData.detailPrototypes = herbeProto. If you don’t, the new array will just have references to the contents of the old array, so when the old array is deleted or cleared then it will also affect the new one.

terrainData.detailPrototypes = new DetailPrototype[herbeProtoCount];
for (int i = 0 ; i < herbeProtoCount ; ++i) {
terrainData.detailPrototypes _= new DetailPrototype(herbeProto*);*_
_*}```*_

There are definitely problems since Unity 2021.2 with procedurally generated Detail objects.

In Editor, it all works fine (I’m using Built-In Render Pipeline myself, but having very similar problems)…

…but once you make a Build, the Detail objects do not appear.

Unity changed some stuff about how Detail objects worked between 2021.1.28 and 2021.2, and something in those changes stopped procedurally generated Detail objects from working anymore.

I’ve been banging my head against the problem for about 4 days now, and I can’t find a solution in the documentation, or on the forums. And all I can find is some issues that Unity have said “Won’t Fix… the terrain team has other priorities”, which leaves us who are making proc gen games without a working engine.

Too bad about those 5 years of work I’ve put into the project.

@muzboz How are your detail objects loaded? Are they direct references, Asset Bundles or Addressables?

Try enabling Reload Domain and Reload Scene to get the editor to behave more like a build.

If you’re instantiating your terrain as a prefab, make sure there’s at least one terrain actually placed in a scene included in your build to make sure the terrain code isn’t been stripped from the build.

If none of these work, you could just find the diff between the version of Unity that worked and the one that didn’t to see what changed in the terrain code:
2021.1.28
2021.2.1
Diff
Although I’d do a diff between local copies with a tool like WinMerge instead of using GitHub directly.