How to properly serialize fields and store them for use in play mode?

Hello,

I’m stuck with this for ~3 days already, and I’m browsing tons of questions in UnityAnswers. Sadly, I’m still stuck.

I’m working on a mesh generator based off the terrain, so in future it will be used as an A* navigation graph. The mesh seems to be working fine (I’m using a “OnDrawGizmos()” function to check it), but as soon as I hit the play button, all the data is gone.
I could go the brute-force way and store it in a file after generation and load it as soon as the game starts, but… I want to do it the Unity way.

So, to sum it up: How can I properly declare the variables so they get saved when I’m done with my graph and stored for use when I press play?

The code is quite lengthy, so I’ll only paste the class definitions - tell me if I need to post more.

using UnityEditor;
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

//---------------------------------------------------------------
// Since Unity currently lacks a Tuple<T> implementation,
// lets use a simple one for coordinates
//---------------------------------------------------------------
[System.Serializable]
public class Coordinate : IEquatable< Coordinate >
{
	[SerializeField] private int _x;
	[SerializeField] private int _z;

	public int x { get { return _x;	} set { _x	= value; } }
	public int z { get { return _z;	} set { _z	= value; } }

	public Coordinate() {}
	
	public Coordinate(int first, int second)
	{
		this.x = first;
		this.z = second;
	}

	public override bool Equals( object obj )
	{
		var other = obj as Coordinate;
		if( other == null ) return false;
		
		return Equals (other);             
	}
	
	public override int GetHashCode()
	{
		int hash = 13;
		hash = (hash * 7) + x;
		hash = (hash * 11) + z;
		return hash;
	}

	public bool Equals( Coordinate other )
	{
		if	( other == null )					return false;
		if	( GetType() != other.GetType()	)	return false;
		if	( ReferenceEquals (this, other)	)	return true;
		if	( x == other.x && z == other.z	)	return true;
		return false;
	}

	public override string ToString()
	{
		return string.Format ("[Coordinate: x={0}, z={1}]", x, z);
	}
}

//---------------------------------------------------------------
// This is our GridNode class. The A* mesh will be stored here.
//---------------------------------------------------------------
[System.Serializable]
public class GridNode
{
	[SerializeField] private bool				_Valid;
	[SerializeField] private Vector3			_Position;
	[SerializeField] private List< Coordinate >	_Neighbours;
	
	public	bool				Valid			{ get { return _Valid;		}	private	set { _Valid		= value;	}	}	// Whether this node is usable or not.
	public	Vector3				Position		{ get { return _Position;	}			set { _Position		= value;	}	}	// Spatial position
	public	List< Coordinate >	Neighbours		{ get { return _Neighbours;	}			set { _Neighbours	= value;	}	}	// We store our neighbours here.

	public	void				MarkAsValid()	{ this.Valid = true;		}	// This seems to be kinda stupid, but lets just be VERY safe here.
	public	void				MarkAsInvalid()	{ this.Valid = false;		}

	public GridNode()
	{
		Valid		= false;
		Position	= Vector3.zero;
	}
}

[ExecuteInEditMode]
public class GridGenerator : MonoBehaviour
{
	#region Variable Declarations
	// The editor will complaint these should be either private or Capitalized. Keep as is for the Editor counterpart.
	[SerializeField] private	bool			m_bDrawGridArea		= true;			// Should we display the grid on the editor? 
	[SerializeField] private	bool			m_bDrawMeshNodes	= true;			// Should we display the nodes on the editor? 
	[SerializeField] private	bool			m_bDrawMesh			= true;			// Should we display the mesh on the editor?


	public	int					m_iSquareSideSize	= 50;			// Max size of the graph (and the matrix it generates ;) )
	public	int					m_iStartNodeX		= 0;			// Column coordinate
	public	int					m_iStartNodeZ		= 0;			// Row coordinate
	public	float				m_flScale			= 1.0f;			// Unit scale!
	public	float				m_flGroundDistance	= 0.1f;			// Desired ground distance. This is NEVER scaled!
	public	bool				m_bBuildingGraph	= false;		// Using this, we control the grid build stage (Internal control only, don't touch please.)
	public	float				m_flGroundTraceSize = 10.0f;		// Haw far can we search for the ground (halved for each axis)

	public	float				m_flMaxMeshNodeStraightDistance	= 1.42f;
	public	float				m_flMaxMeshNodeDiagonalDistance	= 2.0f;

	// Both variables are temporary and used for Mesh generation
	private HashSet<Coordinate>	m_Closed;							// Used in Grid generation
	private Queue<Coordinate>	m_ProcessQueue;						// Used in Grid generation

	[SerializeField]
	private	GridNode[,]			_m_Grid;							// Our sexy grid storage
	public	GridNode[,]			m_Grid
	{
		get { return _m_Grid;	}
		set { _m_Grid = value;	}
	}


	#endregion
	
	// Use this for initialization
	void Start ()
	{
//		Debug.Log("test: " + m_Grid[0,0].Position);
	}
	
	// Update is called once per frame
	void Update()
	{
//		Debug.Log("test: " + m_Grid[0,0].Position);
	}
}

For initialization (for tests, etc.) you can use this:

		for ( int f = 0; f < m_iSquareSideSize; f++ )
			for ( int g = 0; g < m_iSquareSideSize; g++ )
			{
				m_Grid[ f, g ] = new GridNode();
				m_Grid[ f, g ].Neighbours = new List< Coordinate >();
			}

Thank you in advance! I really hope someone out there knows a solution for this :slight_smile:
Also improvements are welcome!


PS: I found this link prior posting this question, but I couldn’t find what’s wrong with my code :frowning:

The class and variables declaration are fine - Sadly, Unity can’t serialize bidimensional arrays.
As a workaround, I changed my GridNode[,] to GridNode[] and I’m using this auxiliary function:

	private int SolveGridIndex( int x, int z )
	{
		return x * m_iSquareSideSize + z;
	}

I hope this save some time to someone having the same issue I had :slight_smile: