Help saving Voxel Terrain between scenes

Hi I was wondering if anyone could help me figure out a script to save my voxel terrain between scene changes. I’ve tried using unity serializer but Since theyre generated on the fly It doesn’t recognize the chunk clones or their positions. I’m new to unity so any help would be greatly appreciated. here are the scripts I’M working with.

Chunk Script

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using SimplexNoise;

public enum BrickType {
	None,
	
	RoughStone,
	SmoothStone,
	DarkStone,
	PowderStone,
	Granite,
	Dirt, 
	Sand,
	GreenLattice,
	
	Taint,
	Dust,
	Rust,
	Ice,
	Snow,
	DirtyIce,
	Streaks,
	Lava
}

[RequireComponent (typeof(MeshRenderer))]
[RequireComponent (typeof(MeshCollider))]
[RequireComponent (typeof(MeshFilter))]
public class Chunk : MonoBehaviour {
	
	public static List<Chunk> chunks = new List<Chunk>();
	public static int width {
		get { return World.currentWorld.chunkWidth; }
	}
	public static int height {
		get { return World.currentWorld.chunkHeight; }
	}
	public static float brickHeight {
		get { return World.currentWorld.brickHeight; }
	}
	
	public byte[,,] map;
	public Mesh visualMesh;
	protected MeshRenderer meshRenderer;
	protected MeshCollider meshCollider;
	protected MeshFilter meshFilter;

	// Use this for initialization
	void Start () {
		
		chunks.Add(this);
		
		meshRenderer = GetComponent<MeshRenderer>();
		meshCollider = GetComponent<MeshCollider>();
		meshFilter = GetComponent<MeshFilter>();
		
		
	
		CalculateMapFromScratch();
		StartCoroutine(CreateVisualMesh());
		
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	public static byte GetTheoreticalByte(Vector3 pos) {
		Random.seed = World.currentWorld.seed;
		
		Vector3 grain0Offset = new Vector3(Random.value * 10000, Random.value * 10000, Random.value * 10000);
		Vector3 grain1Offset = new Vector3(Random.value * 10000, Random.value * 10000, Random.value * 10000);
		Vector3 grain2Offset = new Vector3(Random.value * 10000, Random.value * 10000, Random.value * 10000);
		
		return GetTheoreticalByte(pos, grain0Offset, grain1Offset, grain2Offset);
		
	}
	
	public static byte GetTheoreticalByte(Vector3 pos, Vector3 offset0, Vector3 offset1, Vector3 offset2)
	{
		float clusterValue = CalculateNoiseValue(pos, offset2,  0.02f);		
		int biomeIndex = Mathf.FloorToInt(clusterValue * World.currentWorld.biomes.Length);
		Biome biome = World.currentWorld.biomes[biomeIndex];
		
		
		float heightBase = biome.minHeight;
		float maxHeight = biome.maxHeight;
		float heightSwing = maxHeight - heightBase;
		
		
		float blobValue = CalculateNoiseValue(pos, offset1,  0.05f);
		float mountainValue = CalculateNoiseValue(pos, offset0,  0.009f);
		
		mountainValue += biome.mountainPowerBonus;
		if (mountainValue < 0) mountainValue = 0;
		
		mountainValue = Mathf.Pow(mountainValue, biome.mountainPower);  //Mathf.Sqrt(mountainValue);
		
		byte brick = biome.GetBrick(Mathf.FloorToInt(pos.y), mountainValue, blobValue);
		
		
		mountainValue *= heightSwing;
		mountainValue += heightBase;
		
		mountainValue += (blobValue * 10) - 5f;
					
					
		if (mountainValue >= pos.y)
			return brick;
		return 0;
	}
	
	public virtual void CalculateMapFromScratch() {
		map = new byte[width, height, width];
		
		Random.seed = World.currentWorld.seed;
		Vector3 grain0Offset = new Vector3(Random.value * 10000, Random.value * 10000, Random.value * 10000);
		Vector3 grain1Offset = new Vector3(Random.value * 10000, Random.value * 10000, Random.value * 10000);
		Vector3 grain2Offset = new Vector3(Random.value * 10000, Random.value * 10000, Random.value * 10000);
		
		
		
		for (int x = 0; x < World.currentWorld.chunkWidth; x++)
		{
			for (int y = 0; y < height; y++)
			{
				for (int z = 0; z < width; z++)
				{
					map[x, y, z] = GetTheoreticalByte(new Vector3(x, y, z) + transform.position, grain0Offset, grain1Offset, grain2Offset);
				
				}
			}
		}
		
	}
	
	public static float CalculateNoiseValue(Vector3 pos, Vector3 offset, float scale)
	{
		
		float noiseX = Mathf.Abs((pos.x + offset.x) * scale);
		float noiseY = Mathf.Abs((pos.y + offset.y) * scale);
		float noiseZ = Mathf.Abs((pos.z + offset.z) * scale);
		
		return Mathf.Max(0, Noise.Generate(noiseX, noiseY, noiseZ));

		
	}
	
	
	public virtual IEnumerator CreateVisualMesh() {
		visualMesh = new Mesh();
		
		List<Vector3> verts = new List<Vector3>();
		List<Vector2> uvs = new List<Vector2>();
		List<int> tris = new List<int>();
		
		
		for (int x = 0; x < width; x++)
		{
			for (int y = 0; y < height; y++)
			{
				for (int z = 0; z < width; z++)
				{
					if (map[x,y,z] == 0) continue;
					
					byte brick = map[x,y,z];
					// Left wall
					if (IsTransparent(x - 1, y, z))
						BuildFace (brick, new Vector3(x, y, z), Vector3.up, Vector3.forward, false, verts, uvs, tris);
					// Right wall
					if (IsTransparent(x + 1, y , z))
						BuildFace (brick, new Vector3(x + 1, y, z), Vector3.up, Vector3.forward, true, verts, uvs, tris);
					
					// Bottom wall
					if (IsTransparent(x, y - 1 , z))
						BuildFace (brick, new Vector3(x, y, z), Vector3.forward, Vector3.right, false, verts, uvs, tris);
					// Top wall
					if (IsTransparent(x, y + 1, z))
						BuildFace (brick, new Vector3(x, y + 1, z), Vector3.forward, Vector3.right, true, verts, uvs, tris);
					
					// Back
					if (IsTransparent(x, y, z - 1))
						BuildFace (brick, new Vector3(x, y, z), Vector3.up, Vector3.right, true, verts, uvs, tris);
					// Front
					if (IsTransparent(x, y, z + 1))
						BuildFace (brick, new Vector3(x, y, z + 1), Vector3.up, Vector3.right, false, verts, uvs, tris);
					
					
				}
			}
		}
					
		visualMesh.vertices = verts.ToArray();
		visualMesh.uv = uvs.ToArray();
		visualMesh.triangles = tris.ToArray();
		visualMesh.RecalculateBounds();
		visualMesh.RecalculateNormals();
		
		meshFilter.mesh = visualMesh;
		
		
		meshCollider.sharedMesh = null;
		meshCollider.sharedMesh = visualMesh;
		
		yield return 0;
		
	}
	public virtual void BuildFace(byte brick, Vector3 corner, Vector3 up, Vector3 right, bool reversed, List<Vector3> verts, List<Vector2> uvs, List<int> tris)
	{
		int index = verts.Count;
		
		
		float uvRow = ((corner.y + up.y) % 7);
		if (uvRow >= 4) uvRow = 7 - uvRow;
		uvRow /= 4f;
		Vector2 uvCorner = new Vector2(0.00f, uvRow);
		
		if (brick < 8)
			uvCorner.y += 0.125f;
		
		
		
		corner.y *= brickHeight;
		up.y *= brickHeight;
		right.y *= brickHeight;
		
		
		verts.Add (corner);
		verts.Add (corner + up);
		verts.Add (corner + up + right);
		verts.Add (corner + right);
		
		Vector2 uvWidth = new Vector2(0.125f, 0.125f);
		
		uvCorner.x += (float)((brick - 1) % 8) / 8f;
		
		uvs.Add(uvCorner);
		uvs.Add(new Vector2(uvCorner.x, uvCorner.y + uvWidth.y));
		uvs.Add(new Vector2(uvCorner.x + uvWidth.x, uvCorner.y + uvWidth.y));
		uvs.Add(new Vector2(uvCorner.x + uvWidth.x, uvCorner.y));
		
		if (reversed)
		{
			tris.Add(index + 0);
			tris.Add(index + 1);
			tris.Add(index + 2);
			tris.Add(index + 2);
			tris.Add(index + 3);
			tris.Add(index + 0);
		}
		else
		{
			tris.Add(index + 1);
			tris.Add(index + 0);
			tris.Add(index + 2);
			tris.Add(index + 3);
			tris.Add(index + 2);
			tris.Add(index + 0);
		}
		
	}
	public virtual bool IsTransparent (int x, int y, int z)
	{
		if ( y < 0) return false;
		byte brick = GetByte(x,y,z);
		switch (brick)
		{
		case 0: 
			return true;
		default:
			return false;
		}
	}
	public virtual byte GetByte (int x, int y , int z)
	{
		
		if ((y < 0) || (y >= height))
			return 0;
		
		if ( (x < 0) || (z < 0)  || (x >= width) || (z >= width))
		{
			
			Vector3 worldPos = new Vector3(x, y, z) + transform.position;
			Chunk chunk = Chunk.FindChunk(worldPos);
			if (chunk == this) return 0;
			if (chunk == null) 
			{
				return GetTheoreticalByte(worldPos);
			}
			return chunk.GetByte (worldPos);
		}
		return map[x,y,z];
	}
	public virtual byte GetByte(Vector3 worldPos) {
		worldPos -= transform.position;
		int x = Mathf.FloorToInt(worldPos.x);
		int y = Mathf.FloorToInt(worldPos.y);
		int z = Mathf.FloorToInt(worldPos.z);
		return GetByte (x, y, z);
	}
	
	public static Chunk FindChunk(Vector3 pos) {
		
		for (int a = 0; a < chunks.Count; a++)
		{
			Vector3 cpos = chunks[a].transform.position;
			
			if ( ( pos.x < cpos.x) || (pos.z < cpos.z) || (pos.x >= cpos.x + width) || (pos.z >= cpos.z + width) ) continue;
			return chunks[a];
			
		}
		return null;
		
	}
	
	public bool SetBrick (byte brick, Vector3 worldPos)
	{
		worldPos -= transform.position;
		return SetBrick(brick, Mathf.FloorToInt(worldPos.x),Mathf.FloorToInt(worldPos.y),Mathf.FloorToInt(worldPos.z));
	}
	public bool SetBrick (byte brick, int x, int y, int z)
	{
		if ( ( x < 0) || (y < 0) || (z < 0) || (x >= width) || (y >= height) || (z >= width) )
		{
			return false;
		}
		
		if (map[x,y,z] == brick) return false;
		map[x,y,z] = brick;
		StartCoroutine(CreateVisualMesh());		
		
		if (x == 0)
		{
			Chunk chunk = FindChunk( new Vector3(x - 2, y, z) + transform.position);
			if (chunk != null)
				StartCoroutine(chunk.CreateVisualMesh());		
		}
		if (x == width - 1)
		{
			Chunk chunk = FindChunk( new Vector3(x + 2, y, z) + transform.position);
			if (chunk != null)
				StartCoroutine(chunk.CreateVisualMesh());		
		}
		if (z == 0)
		{
			Chunk chunk = FindChunk( new Vector3(x, y, z - 2) + transform.position);
			if (chunk != null)
				StartCoroutine(chunk.CreateVisualMesh());		
		}
		if (z == width - 1)
		{
			Chunk chunk = FindChunk( new Vector3(x, y, z + 2) + transform.position);
			if (chunk != null)
				StartCoroutine(chunk.CreateVisualMesh());		
		}
		
		return true;
	}
}

world script

using UnityEngine;
using System.Collections;

public class World : MonoBehaviour {

public Biome[] biomes;

public static World currentWorld;
public int chunkWidth = 20, chunkHeight = 20, seed = 0;
public float viewRange = 30;

public float brickHeight = 1;

public Chunk chunkFab;

// Use this for initialization
void Awake () {
	currentWorld = this;
	if (seed == 0)
		seed = Random.Range(0, int.MaxValue);
}

// Update is called once per frame
void Update () {
	
	
	for (float x = transform.position.x - viewRange; x < transform.position.x + viewRange; x+= chunkWidth)
	{
		for (float z = transform.position.z - viewRange; z < transform.position.z + viewRange; z+= chunkWidth)
		{
			
			Vector3 pos = new Vector3(x, 0, z);
			pos.x = Mathf.Floor(pos.x / (float)chunkWidth) * chunkWidth;
			pos.z = Mathf.Floor(pos.z / (float)chunkWidth) * chunkWidth;
			
			Chunk chunk = Chunk.FindChunk(pos);
			if (chunk != null) continue;
			
			chunk = (Chunk)Instantiate(chunkFab, pos, Quaternion.identity);
			
			
			
		}
	}

}

}

Thank you in advance in a noobie.

I added this answer some minutes ago but It didn’t show I’ll do it again…

I hope this helps you my friend, this will keep your GameObject alive between the scenes but it won’t be saved if you quit the application

If you want to saveto file, you can use Unity Serializer or your own Serializer to just save the information from your 3-dimension array and then load it when loading the saved file again, and then rebuilding your chunks/meshes.

Be aware that you can not save multi-dimensional arrays though, so you have to put the block info into one long array and when loading, use the xyz size values to put that long array back into 3-dimensional form.

int x = 0;
int y = 0;
int z = 0;
for(int i = 0; i < yourFlattenedArray; i ++) {

     yourWorld[x,y,z].fillData = loadedGame.fillDataList*;*

z++;

if (z == zSize) {
z = 0;
if(y < ySize - 1) {
y++;
}
else {
x++;
y = 0;
}
}
}