Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 11 Next capture
2021 2022 2023
1 capture
11 Jun 22 - 11 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by bawenang · Jul 29, 2015 at 11:57 AM · arraycustom editorcustom-inspectorcustom-editor

Initializing 2D array via inspector

Hi, I've got this script that will be used for some kind of a grid map system:

 using UnityEngine;
 using System.Collections;
 
 [System.Serializable]
 public class GridMap : MonoBehaviour {
 #region Member Variables
     private static GridMap instance = null; /**< The instance for singleton */
 
     [SerializeField] private Grid gridPrefab = null; /**< The prefab of the base grid */
     [SerializeField] private float gridScaleFactor = 1; /**< The size / scale factor of the grids */
     [SerializeField] private Vector2 mapSize = new Vector2(10, 10); /**< The size of the map. In [width, height]. */
     
     public int[,] mapLayout; /**< The representation of the whole grid in the map. 0 = passabale / floor, 1 = unpassable / wall. */
     
     private Grid[,] gridArray; /**< The array that holds all the grids in the map */
     
 #endregion
 
 #region Member Properties
     /**
      * Map size property
      */
     public Vector2 MapSize
     {
         get{return mapSize;}
     }
 #endregion
 
 #region Monobehaviour Methods
     /** 
      * Use this for initialization BEFORE Start()
      */
     void Awake () 
     {
         //Setting the instance
         if (instance == null) //Set instance if null
         {
             instance = this;
         }
         else if (instance != this)
         {
             DestroyImmediate(this.gameObject); //Destroy the double occurence.
         }
         
         //Populate the map with grids
         PopulateMap();
     }
 
     /** 
      * Use this for initialization
      */
     void Start () 
     {
     }
 
     /** 
      * Update is called once per frame
      */    
     void Update () 
     {
     }
     
     /** 
      * Use this for deinitialization
      */
     void OnDestroy()
     {
         if (instance == this)
         {
             instance = null;
         }
     }
 #endregion
 
 #region Static Member Methods
     /**
      * Populate the grid map with grids
      */
     public void PopulateMap()
     {
         gridArray = new Grid[(int)MapSize.x, (int) MapSize.y];
     
         float xOffset = this.transform.position.x - (mapLayout.GetLength(0) * gridPrefab.Size / 2.0f);
         float zOffset = this.transform.position.z - (mapLayout.GetLength(1) * gridPrefab.Size / 2.0f);
         Vector2 offset = new Vector2( xOffset, zOffset);
         for (int i = 0; i < mapLayout.GetLength(0); ++i)
         {
             for(int j = 0; j < mapLayout.GetLength(1); j++)
             {
                 gridArray[i, j] = CreateGrid(i, j, offset);
             }
         }
     }
     
     /**
      * Create a grid at index [i,j]
      *
      * @param[in] i The index of the grid along the width of the map
      * @param[in] j The index of the grid along the height of the map
      * @param[in] offset The offset of the position at [0,0] index
      *
      * @return The instance of the grid
      */
     private Grid CreateGrid(int i, int j, Vector2 offset)
     {
         Grid grid = Instantiate(gridPrefab).GetComponent<Grid>();
         grid.transform.localScale *= gridScaleFactor; //Scale by the factor
         
         //Reposition the grid
         float xPos = offset.x + (i * grid.Size);
         float zPos = offset.y + (j * grid.Size);
         grid.transform.position = new Vector3(xPos, this.transform.position.y, zPos);
         
         return grid;
     }
 #endregion
 
 #region Member Methods
     /**
      * Create the grids
      */
     public static bool Exists()
     {
         if (instance == null)
         {
             Debug.LogError("GridMap doesn't exist! Please add a game object with an instance of the GridMap.");
             return false;
         }
         
         return true;
         
     }
     
     /**
      * Get the size of the grids
      */
     public static float GetGridScaleFactor()
     {
         float size = Exists() ? instance.gridScaleFactor : 0.0f;
         return size;
     }
 #endregion
 }


And it has an editor class: using UnityEngine; using UnityEditor; using System.Collections;

 [CustomEditor(typeof(GridMap))]
 public class GridMapEditor : Editor {
 #region Member Variables
     private SerializedObject gridMapSO;
     private GridMap gridMap;
 
     bool isShowing;
     
     int width;
     int height;
 #endregion
 
 #region Member Properties
 #endregion
 
 #region Monobehaviour Methods
     void OnEnable () {
         gridMapSO = new SerializedObject(target);
         gridMap = (GridMap)target;
     }
 
     public override void OnInspectorGUI()
     {
         gridMapSO.Update();
     
         base.OnInspectorGUI();
         
         //Check the size of the array
         CheckArray(gridMap);
         
         isShowing = EditorGUILayout.Foldout(isShowing, "Map Layout");
         if(isShowing)
         {
         
             for (int i = 0; i < gridMap.mapLayout.GetLength(1); ++i)
             {
                 EditorGUILayout.BeginHorizontal();
                 for(int j = 0; j < gridMap.mapLayout.GetLength(0); j++)
                 {
                     gridMap.mapLayout[j,i] = EditorGUILayout.IntField(gridMap.mapLayout[j,i], GUILayout.Width(20));
                 }
                 EditorGUILayout.EndHorizontal();
             }
         }
         
         if(GUI.changed){
             EditorUtility.SetDirty(target);
             EditorUtility.SetDirty(gridMap);
         }
         
         gridMapSO.ApplyModifiedProperties();
     }
 #endregion
 
 #region Member Methods
     private void CheckArray(GridMap gridMap)
     {
         width = (int) gridMap.MapSize.x;
         height = (int) gridMap.MapSize.y;
         
         //int gridmapWidth = gridMap.mapLayout.GetLength(0);
         //int gridmapHeight = gridMap.mapLayout.GetLength(1);
         
         //Debug.Log("width = " + width + " height = " + height + " gridmapWidth = " + gridmapWidth + " gridmapHeight = " + gridmapHeight );
         
         if (gridMap.mapLayout == null || width != gridMap.mapLayout.GetLength(0) || height != gridMap.mapLayout.GetLength(1))
         {
             RecreateArray(gridMap, width, height);
         }
     }
     
     private void RecreateArray(GridMap gridMap, int width, int height)
     {
     
         Debug.Log("TEST!!!");
         int[,] tempArray = gridMap.mapLayout;
         
         gridMap.mapLayout = new int[width, height];
         for (int i = 0; i < gridMap.mapLayout.GetLength(0); ++i)
         {
             if (i >= tempArray.GetLength(0))
                 break;
                 
             for(int j = 0; j < gridMap.mapLayout.GetLength(1); j++)
             {
                 if (j >= tempArray.GetLength(1))
                     break;
                     
                 gridMap.mapLayout[i,j] = tempArray[i,j];
             }
         }
     }
 #endregion
 }
 

It worked pretty good in the editor. But when I pressed "play", it shows this error:

NullReferenceException: Object reference not set to an instance of an object

GridMap.PopulateMap () (at Assets/Scripts/GridSystem/GridMap.cs:93)

GridMap.Awake () (at Assets/Scripts/GridSystem/GridMap.cs:56)

It turns out, the mapLayout 2D array (that is used as the data that should be inserted from the editor) is not instantiated. I can change the declaration of mapLayout into this:

 public int[,] mapLayout = new int[10,10];

But the array's length will always be the same 10x10 value. Even when I changed the array into something like 12x5 in the inspector. I need the length of the mapLayout array to be variable and dynamic. How do I do this?

Thanks in advance.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

3 Replies

· Add your reply
  • Sort: 
avatar image
3
Best Answer

Answer by Bunny83 · Jul 29, 2015 at 12:19 PM

Multidimensional arrays aren't supported by the Unity serializer. You have to store your information in a data type that is supported by the serializer. One way is to use a serializable helper class and use seperate arrays:

 [Serializable]
 public class Row
 {
     public int[] rowdata;
 }
 
 public class GridMap : MonoBehaviour
 {
     // ...
     public Row[] mapLayout; 
     // ...
 
 }

This is supported by the serialization system. You don't have a multidimensional array anymore but some kind of jagged array so access would look like this:

 mapLayout[row].rowdata[column]

An alternative is to use the new ISerializationCallbackReceiver interface and implement some kind of conversion into a datatype that is supported. For multi dim array i would suggest using a flattend array as well as a row or column count:

 public class GridMap : MonoBehaviour, ISerializationCallbackReceiver
 {
     public int[,] mapLayout;
 
     [HideInInspector]
     [SerializeField]
     private int[] m_FlattendMapLayout;
 
     [HideInInspector]
     [SerializeField]
     private int m_FlattendMapLayoutRows;

  
     public void OnBeforeSerialize()
     {
         int c1 = mapLayout.GetLength(0);
         int c2 = mapLayout.GetLength(1);
         int count = c1*c2;
         m_FlattendMapLayout = new int[count];
         m_FlattendMapLayoutRows = c1;
         for(int i = 0; i < count; i++)
         {
             m_FlattendMapLayout[i] = mapLayout[i % c1, i / c1];
         }
     }
     public void OnAfterDeserialize()
     {
         int count = m_FlattendMapLayout.Length;
         int c1 = m_FlattendMapLayoutRows;
         int c2 = count / c1;
         mapLayout = new int[c1,c2];
         for(int i = 0; i < count; i++)
         {
             mapLayout[i % c1, i / c1] = m_FlattendMapLayout[i];
         }
     }
 }

This would work as well. Everytime Unity wants to serialize your class it will call "OnBeforeSerialize" which will copy all items into a one-dim-array which then can be serialized by unity. When your class is deserialized OnAfterDeserialize is being called which will recreate your multidim array and copy the items back.

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image bawenang · Jul 30, 2015 at 08:55 AM 0
Share

Thank you. Thank you so much. It actually worked. I used the first method. :)

avatar image ThomLaurent · Sep 17, 2016 at 12:04 AM 0
Share

The second solution is better as it lets you create a true 2D array plus lets you manage its serialized form. Thanks for mentioning it! PS: I knew about ISerializationCallbackReceiver

avatar image AuggoDoggo · Feb 08, 2018 at 08:25 PM 0
Share

Thanks a lot for providing multiple solutions. If I were making something more scalable I'd dig deeper into option two, but seeing as its such a small part of my project that uses a couple 2D arrays, I really only needed the quick and dirty jagged array. Looking at your solution. I'm surprised I didn't think to do this, but I just wanted to say thanks and that there are still people using this on serious projects multiple years later.

avatar image
2

Answer by Xarbrough · Dec 21, 2017 at 01:36 AM

The accepted answers the questions correctly, but I'd like to add my personal opinion:

It's much better to just always use a single-dimensional array, than 2D or jagged arrays. Internally, all array types are actually stored as 1D arrays in memory. Using the C# syntax only adds conversion math and additional checks. Implementing this yourself is actually faster and doesn't require any additional steps for serialization in Unity.

This is how it could look:

 public class TileMap : MonoBehaviour
 {
     public int width = 12;
     public int height = 10;
 
     [SerializeField]
     private string[] tiles;
 
     public void SetTile(int x, int y, string type)
     {
         // 2D representation stored in row-major order.
         tiles[y * width + x] = type;
     }
 
     public string GetTile(int x, int y)
     {
         return tiles[y * width + x];
     }
 
     public Vector2Int GetCoordinate(int index)
     {
         int x = index % width;
         int y = index / width;
         return new Vector2Int(x, y);
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by Eldoir · Dec 20, 2017 at 04:35 PM

Hello there, I just came across this question today at work and solved it,

I made a GitHub for this: https://github.com/Eldoir/2DArrayEditor

Hope you'll like it :)

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

26 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Prefabs aren't saving with Undo.RecordObject 4 Answers

Getting a target's gameobject in custom editor 1 Answer

Generic CustomEditor 0 Answers

Events in custom inspector 1 Answer

Handles not displaying 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges