Jump to content

GeNa terrain decorator deformations persist after runtime operation


FoxTheDog
 Share

Go to solution Solved by Manny,

Recommended Posts

I am using GeNa spawners and terrain decorators in a procedural environment. I have a few Gaia terrains that I modify with prefabs / biomes every time the player visits them. Over the course of 1) playtesting, and 2) actually playing the build, I've noticed that the flattened region where structures had been spawned in previous visits to the planet remain in subsequent visits. 

Is there a way to 'undo' the terrain deformation caused by a runtime executed decorator spawn on exiting play mode? 

Thanks for any advice you can provide.

Link to comment
Share on other sites

On 1/26/2022 at 9:19 AM, FoxTheDog said:

I am using GeNa spawners and terrain decorators in a procedural environment. I have a few Gaia terrains that I modify with prefabs / biomes every time the player visits them. Over the course of 1) playtesting, and 2) actually playing the build, I've noticed that the flattened region where structures had been spawned in previous visits to the planet remain in subsequent visits. 

Is there a way to 'undo' the terrain deformation caused by a runtime executed decorator spawn on exiting play mode? 

Thanks for any advice you can provide.

We are looking into this. 

Link to comment
Share on other sites

Unity terrain stores its data in what essentially acts like a scriptable object. What this means is that changes at design time and runtime are persisted.

You would need to backup and restore the terraindata to revert this behaviour. GeNa has an in memory undo, you would need to call that when you exit play mode.

 

 

 

Link to comment
Share on other sites

Thanks for the reply. I've been digging into the API docs and found GeNaSpawner.UndoAll(). Although the function is public, I'm having a hard time calling it. My idea was to use the onApplicationQuit event to undo all the spawns that a single spawner made (those with terrain decorators). Something like this:

[SerializeField] GeNa.Core.GeNaSpawner spawner
void OnApplicationQuit()
    {
        spawner.UndoAll();
    }

I'm clearly not understanding the docs though, UndoAll doesn't appear to be a public method of the GenaSpawner class (but I am admittedly very new to all this!)

5 hours ago, Adam said:

You would need to backup and restore the terraindata to revert this behaviour. GeNa has an in memory undo, you would need to call that when you exit play mode.

I've looked into this, its nothing I've done before and a bit complex. Are you suggesting that GeNa's in memory undo would handle the backup and restore of the terrain data, or are those separate tasks? Im guessing saving and loading the terrain data isnt going to be very performant in a build.. but I did find this post (2013, recent comment 2019) about the subject, in case anyone else is interested.

 https://answers.unity.com/questions/390209/how-to-prevent-terrain-changes-from-being-permanen.html

 

 

Link to comment
Share on other sites

  • Solution

Hi there @FoxTheDog,

[The Problem]
Allow me to try to eliminate any confusion about what GeNa does during runtime. Unity has designed the terrain system to be persistent even when making runtime edits. Therefore, if you are spawning objects with GeNa that modify the terrain heights, those will be persisted. We can't change this behavior because to update the terrain at runtime, those processes that need to happen to require it gets written to disk. 

That being said, there is a system in GeNa that would make it indeed possible to revert changes made (by GeNa at least) to the terrains in your scene at runtime. However, it would take some digging around the API because the API documentation is will a work-in-progress. The undo system in particular is something that we are currently working on to improve at runtime. And yes, you are correct. Even if you were to use the undo system in GeNa, the next biggest problem at runtime is all of the data stored during the game session (not persisted) that would be needed to undo all of the changes made to the terrain. 

[The Possible Solution]
However, after all of this is said. I do agree with @Adam's above comment about storing the terrain data prior to the edits. But may I suggest that the best solution would be to make a 'Deep Copy' of the Terrain's data when starting your game so that any changes made will not be persisted as they are loaded into cache. 

Here's a link to an article where they talk about deep copying the terrain at runtime for edits:
https://forum.unity.com/threads/solved-how-to-modify-a-terrain-at-runtime-and-get-back-to-original-terrain-on-exit.487505/ 

And here is the terrain cloner script they mentioned in the above article:
https://gist.github.com/Eldoir/d5a438dfedee55552915b55097dda1d4

Give it a try if you can,
Hope this helps! 😁

  • Like 1
Link to comment
Share on other sites

@Manny This is indeed a huge help. I either need to rethink how I'm using the procedural spawners or implement some sort of deep copy / reload of the terraindata. I'll look into the latter and if its reasonable and works, I'll update this thread to share the solution with the community. Thanks again, you guys are really helping me out!

Link to comment
Share on other sites

51 minutes ago, FoxTheDog said:

@Manny This is indeed a huge help. I either need to rethink how I'm using the procedural spawners or implement some sort of deep copy / reload of the terraindata. I'll look into the latter and if its reasonable and works, I'll update this thread to share the solution with the community. Thanks again, you guys are really helping me out!

That would be great mate. 

Good luck! 🙂

Link to comment
Share on other sites

Well this was incredibly simple after all, the solution suggested by @Manny and @Adam works like a charm. I fixed up some deprecated code but used the script linked above mostly as is. 

For the sake of completeness, I wrote a tiny script that takes the Terrain component from Gaia and assigns the terrain data and collider components to the deep copy. I only have one tile per scene here, but you easily could iterate over the hierarchy and get the terrain component at runtime rather than assign it in the editor like I do here. ,Literally copy pasted from the thread above.

using UnityEngine;

// Uses TerrainDataCloner to create a deep copy of the terrain.
// Subsequent edits to the terrain and its collider at runtime will be
// made to the deep copy rather than the original.
// Useful if you re-use a handful of terrains with procedurally spawned
// prefabs that modify the mesh and/or details.
//
// TerrainDataCloner: https://gist.github.com/Eldoir/d5a438dfedee55552915b55097dda1d4
// Unity thread: https://tinyurl.com/ykbd2fze
public class TerrainCloneRestore : MonoBehaviour
{

    [SerializeField] Terrain terrain;
    private void Awake()
    {
        terrain.terrainData = TerrainDataCloner.Clone(terrain.terrainData);
        terrain.GetComponent<TerrainCollider>().terrainData = terrain.terrainData; 
    }

}

 

Thanks again!

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, FoxTheDog said:

Well this was incredibly simple after all, the solution suggested by @Manny and @Adam works like a charm. I fixed up some deprecated code but used the script linked above mostly as is. 

For the sake of completeness, I wrote a tiny script that takes the Terrain component from Gaia and assigns the terrain data and collider components to the deep copy. I only have one tile per scene here, but you easily could iterate over the hierarchy and get the terrain component at runtime rather than assign it in the editor like I do here. ,Literally copy pasted from the thread above.

using UnityEngine;

// Uses TerrainDataCloner to create a deep copy of the terrain.
// Subsequent edits to the terrain and its collider at runtime will be
// made to the deep copy rather than the original.
// Useful if you re-use a handful of terrains with procedurally spawned
// prefabs that modify the mesh and/or details.
//
// TerrainDataCloner: https://gist.github.com/Eldoir/d5a438dfedee55552915b55097dda1d4
// Unity thread: https://tinyurl.com/ykbd2fze
public class TerrainCloneRestore : MonoBehaviour
{

    [SerializeField] Terrain terrain;
    private void Awake()
    {
        terrain.terrainData = TerrainDataCloner.Clone(terrain.terrainData);
        terrain.GetComponent<TerrainCollider>().terrainData = terrain.terrainData; 
    }

}

 

Thanks again!

 

Well done mate!

Glad to hear our solution worked for you! 😁

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...