Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 Smehi · May 30, 2019 at 09:38 PM · serializationcustom editorserializedpropertygenericsserializedobject

Finding property with serializedObject on script with a generic

Context

I am working on a tower defense game where towers and enemies have types and elements. The script consists of an array with a type or element, where each one has another array that contains the versus type or element and a multiplier value. So it's basically a 2D array. I wanted to be a bit fancy and also try some stuff out so I decided to make a custom editor for it. Because this is basically a matrix where one of the items is compared to the other I wanted to make it look like the Layer Collision Matrix, found under Project Settings > Physics. Here is what I got:alt text


What I have

After finishing the custom editor I quickly ran into some problems namely, whenever I go to another scene or close unity the data is not serialized. First I tried working around this by saving the matrix to a JSON file and loading it back in when I need it. This works fine when I play from the editor and if I make the script load the JSON file in the Start method is works when building as well. But ultimately I would like the script to just remember the values of the fields as I edit them. I looked up how to do this and I found out I have to use serializedObject to get the property I want and then apply the changed properties at the end. I tried doing this multiple times but couldn't manage to get it to work, serializedObject would never actually find the property. I thought it may have been a bug with my Unity version so I decided to quickly make a similar setup in other versions, but I found the actual problem instead. When the script that contains the array has a generic that needs to be supplied serializedObject returns null everytime. When I tried without the generic I got back the property no problem.


Problem

Now the issue is that I put that generic there so I could make a base class instead of two very similar classes. The classes for the types matrix and elements matrix are indectical except for the enum they use. Now, my question is: there is any way I can get the property mainArray in my case, from this base class that has the generic? If this is not possible, is there a way I can remove the generic and keep the base class?


If there isn't another way about this I will just have to deal with the way I have it set up now I guess. If you got all the way here I would like to thank you for at least taking the time to read this post.


Scripts

Generics meaning:
1. DM: Damage matrix implementation script.
2. TE: "Type" or "Element" enum from the TypesAndElements script.

Matrix Editor Base

 /// <summary>
 /// Base class for the custom editor of the damage matrices.
 /// </summary>
 public class DamageMatrixBaseEditor<TE, DM> : Editor where TE : Enum where DM : DamageMatrixBase<TE>
 {
     private int enumLength = Enum.GetNames(typeof(TE)).Length;
     private DM myTarget;
 
     private void OnEnable()
     {
         // The target will be a DamageMatrix class (ie. DamageMatrixTypes or DamageMatrixElements).
         myTarget = target as DM;
     }
 
     public override void OnInspectorGUI()
     {
         #region Styling
         // Styling for the different matrix elements.
         GUIStyle table = new GUIStyle("box")
         {
             stretchHeight = true,
             stretchWidth = true,
             padding = new RectOffset(0, 0, 0, 0)
         };
 
         GUIStyle cornerLabel = new GUIStyle("box")
         {
             fixedHeight = 20,
             fixedWidth = (EditorGUIUtility.currentViewWidth - 25) / (enumLength + 1),
             alignment = TextAnchor.LowerRight,
             margin = new RectOffset(0, 0, 0, 0)
         };
 
         GUIStyle colLabel = new GUIStyle("box")
         {
             fixedHeight = 20,
             fixedWidth = (EditorGUIUtility.currentViewWidth - 25) / (enumLength + 1),
             alignment = TextAnchor.LowerCenter,
             margin = new RectOffset(0, 0, 0, 0)
         };
 
         GUIStyle rowLabel = new GUIStyle("box")
         {
             fixedHeight = 20,
             fixedWidth = (EditorGUIUtility.currentViewWidth - 25) / (enumLength + 1),
             alignment = TextAnchor.MiddleRight,
             margin = new RectOffset(0, 0, 0, 0)
         };
 
         GUIStyle floatStyle = new GUIStyle("box")
         {
             fixedHeight = 20,
             fixedWidth = (EditorGUIUtility.currentViewWidth - 25) / (enumLength + 1),
             alignment = TextAnchor.MiddleCenter,
             margin = new RectOffset(0, 0, 0, 0)
         };
         #endregion
 
         #region Matrix visualization
         EditorGUILayout.BeginVertical(table);
 
         // First for-loop is for the length of the table.
         for (int y = -1; y < enumLength; y++)
         {
             EditorGUILayout.BeginHorizontal();
 
             // Second for-loop is for the width of the table.
             for (int x = -1; x < enumLength; x++)
             {
                 // Top-Left corner label should be empty.
                 if (y == -1 && x == -1)
                 {
                     EditorGUILayout.LabelField("", cornerLabel);
                 }
                 // When the y is increasing so we are going down (row labels).
                 else if (y > -1 && x == -1)
                 {
                     EditorGUILayout.LabelField(Enum.GetName(typeof(TE), y), rowLabel);
                 }
                 // When the x is increasing so we are going right (column labels).
                 else if (y == -1 && x > -1)
                 {
                     EditorGUILayout.LabelField(Enum.GetName(typeof(TE), x), colLabel);
                 }
                 // When both the x and y are increasing so we are in the cross section of the matrix (input labels).
                 else if (y > -1 && x > -1)
                 {
                     // The array is populated however the correct type or element hasn't been assigned yet.
                     myTarget.GetArray()[y].typeElement = EnumConverter<TE>.Convert(y);
                     myTarget.GetArray()[y].versus[x].typeElement = EnumConverter<TE>.Convert(x);
 
                     // Set the multiplier value to whatever the input is at the cross.
                     myTarget.GetArray()[y].versus[x].multiplier = EditorGUILayout.FloatField(myTarget.GetArray()[y].versus[x].multiplier, floatStyle);
                 }
             }
 
             EditorGUILayout.EndHorizontal();
         }
 
         #region Load/Save buttons
         EditorGUILayout.BeginHorizontal();
 
         if (GUILayout.Button("Load Matrix"))
         {
             myTarget.LoadMatrix();
         }
 
         if (GUILayout.Button("Save Matrix"))
         {
             myTarget.SaveMatrix();
         }
 
         EditorGUILayout.EndHorizontal();
         #endregion
 
         EditorGUILayout.EndVertical();
         #endregion
     }
 
     /// <summary>
     /// Converter class to help going from a int to a generic enum field
     /// </summary>
     static class EnumConverter<TEnum> where TEnum : Enum
     {
         public static readonly Func<int, TEnum> Convert = GenerateConverter();
 
         private static Func<int, TEnum> GenerateConverter()
         {
             var parameter = Expression.Parameter(typeof(int));
             var dynamicMethod = Expression.Lambda<Func<int, TEnum>>(
                 Expression.ConvertChecked(parameter, typeof(TEnum)),
                 parameter);
 
             return dynamicMethod.Compile();
         }
     }
 }


Editor Implementation

 [CustomEditor(typeof(DamageMatrixTypes))]
 public class DamageMatrixTypesEditor : DamageMatrixBaseEditor<TypesAndElements.Types, DamageMatrixTypes>
 {
 }
 
 [CustomEditor(typeof(DamageMatrixElements))]
 public class DamageMatrixElementsEditor : DamageMatrixBaseEditor<TypesAndElements.Elements, DamageMatrixElements>
 {
 }


Matrix Base

 public class DamageMatrixBase<TE> : MonoBehaviour where TE : Enum
     {
         // The path will be the name of the files. The name of the file will end in either of the enums in TypesAndElements.
         private string filePath = "DamageMatrix" + typeof(TE).ToString().Split('+')[1] + ".json";
         public MainArray[] mainArray = new MainArray[Enum.GetNames(typeof(TE)).Length];
         private bool hasPopulated = false;
 
         private void Start()
         {
             LoadMatrix();
         }
 
         /// <summary>
         /// This method is called to get a multiplier value for the damage.
         /// Intended use: tower vs enemy.
         /// </summary>
         /// <param name="towerTypeElement">The type or element of the tower.</param>
         /// <param name="enemyTypeElement">The type or element of the enemy.</param>
         public virtual float Multiplier(TE towerTypeElement, TE enemyTypeElement)
         {
             return mainArray[Convert.ToInt16(towerTypeElement)].versus[Convert.ToInt16(enemyTypeElement)].multiplier;
         }
 
         /// <summary>
         /// Used to get the main array of types/elements that contains everything.
         /// </summary>
         public virtual MainArray[] GetArray()
         {
             Populate();
             return mainArray;
         }
 
         /// <summary>
         /// This will populate all the versus arrays for each item in the main array.
         /// </summary>
         public void Populate()
         {
             if (!hasPopulated)
             {
                 for (int i = 0; i < mainArray.Length; i++)
                 {
                     mainArray[i].versus = new VersusArray[Enum.GetNames(typeof(TE)).Length];
                 }
 
                 hasPopulated = true;
             }
         }
 
         /// <summary>
         /// Load the matrix file from the StreamingAssets folder.
         /// </summary>
         public virtual void LoadMatrix()
         {
             Populate();
             MatrixLoadSaveHelper<MainArray, TE>.LoadMatrix(mainArray, filePath, Enum.GetNames(typeof(TE)).Length);
         }
 
         /// <summary>
         /// Save the current matrix to the StreamingAssets (will override previous matrix).
         /// </summary>
         public virtual void SaveMatrix()
         {
             MatrixLoadSaveHelper<MainArray, TE>.SaveMatrix(mainArray, filePath);
         }
 
         [System.Serializable]
         public struct MainArray
         {
             [JsonConverter(typeof(StringEnumConverter))]
             public TE typeElement;
             public VersusArray[] versus;
         }
 
         [System.Serializable]
         public struct VersusArray
         {
             [JsonConverter(typeof(StringEnumConverter))]
             public TE typeElement;
             public float multiplier;
         }
     }


Matrix Implementation

 public class DamageMatrixTypes : DamageMatrixBase<TypesAndElements.Types>
 {
 }
 
 public class DamageMatrixElements : DamageMatrixBase<TypesAndElements.Elements>
 {
 }


Script with enums

 public class TypesAndElements : ScriptableObject
     {
         public enum Types
         {
             Light,
             Medium,
             Heavy
         }
 
         public enum Elements
         {
             Normal,
             Earth,
             Fire,
             Water,
             Air
         }
     } 

unity-4kiyjarvec.png (4.6 kB)
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

0 Replies

· Add your reply
  • Sort: 

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

111 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 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 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 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 avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Error when trying to Serialize a field that is in a class 0 Answers

How to update serializedField showed on the inspector by script? 1 Answer

ApplyModifiedProperties() for UnityEvent ignores method name 0 Answers

Calling a method on a serialized property (Custom Editor with Reorderable List) 0 Answers

Custom Inspector: Using 'serializedObject' to access inherited members 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