Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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 Omti1990 · Oct 17, 2020 at 11:36 AM · editorguiundo

How do I add an undo functionality to my custom editor?

Hello,

I've been trying to add an undo functionality to my custom editor script, but in its current state if I change anything in the editor it's not registering in unity's undo/redo function. What do I have to change to make it work?

 using UnityEngine;
 using UnityEditor;
 
 
 [CustomEditor(typeof(SpaceObject))]
 public class SpaceObjectEditor : Editor
 {
 
     public override void OnInspectorGUI()
     {
         SpaceObject spaceObject = (SpaceObject)target;
 
         base.OnInspectorGUI(); //Default Kram
 
         EditorGUI.BeginChangeCheck();
 
         EditorGUILayout.BeginHorizontal();
         EditorGUILayout.PrefixLabel("Position of the Space-Object");
         EditorGUIUtility.labelWidth = 10f;
         
         double newX = EditorGUILayout.DoubleField("x: ", spaceObject.origin.x, GUILayout.MaxWidth(100f));
         double newY = EditorGUILayout.DoubleField("y: ", spaceObject.origin.y, GUILayout.MaxWidth(100f));
         double newZ = EditorGUILayout.DoubleField("z: ", spaceObject.origin.z, GUILayout.MaxWidth(100f));
         EditorGUILayout.EndHorizontal();
 
         if (EditorGUI.EndChangeCheck())
         {
             Undo.RecordObject(spaceObject, "Changed Position in Space");
             spaceObject.origin.x = newX;
             spaceObject.origin.y = newY;
             spaceObject.origin.z = newZ;
         }
     }
 }
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

2 Replies

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

Answer by Bunny83 · Oct 17, 2020 at 01:44 PM

Well I'm sure the issue is that you are mixing the new serializedobject stuff (by using base.OnInspectorGUI(); ) with the old way using the target property. This most likely will interfer with your undo code afterwards. If you want to implement a proper editor you want to use the SerializedObject / SerializedProperty approach. Though if the type of your origin field is a custom struct, it would make more sense to implement a PropertyDrawer for your type (which also uses the SerializedProperty class). Your approach would display your custom struct twice unless you explicitly use [SerializeField, HideInInspector]


If you really want to roll your own custom inspector for your whole SpaceObject class, use an inspector like this:

 [CustomEditor(typeof(SpaceObject))]
 public class SpaceObjectEditor : Editor
 {
     SerializedProperty xProperty;
     SerializedProperty yProperty;
     SerializedProperty zProperty;
 
     private void OnEnable()
     {
         xProperty = serializedObject.FindProperty("origin.x");
         yProperty = serializedObject.FindProperty("origin.y");
         zProperty = serializedObject.FindProperty("origin.z");
     }
 
     public override void OnInspectorGUI()
     {
         base.OnInspectorGUI();
 
         EditorGUI.BeginChangeCheck();
 
         EditorGUILayout.BeginHorizontal();
         EditorGUILayout.PrefixLabel("Position of the Space-Object");
         EditorGUIUtility.labelWidth = 10f;
 
         xProperty.doubleValue = EditorGUILayout.DoubleField("x: ", xProperty.doubleValue, GUILayout.MaxWidth(100f));
         yProperty.doubleValue = EditorGUILayout.DoubleField("y: ", yProperty.doubleValue, GUILayout.MaxWidth(100f));
         zProperty.doubleValue = EditorGUILayout.DoubleField("z: ", zProperty.doubleValue, GUILayout.MaxWidth(100f));
         EditorGUILayout.EndHorizontal();
 
         if (EditorGUI.EndChangeCheck())
         {
             serializedObject.ApplyModifiedProperties();
         }
     }
 }




Though as I said it usually makes more sense to create a property drawer for your custom type. For example If you have a struct like this:


 [System.Serializable]
 public struct DVector3
 {
     public double x;
     public double y;
     public double z;
 }


With a property drawer like this:

 [CustomPropertyDrawer(typeof(DVector3))]
 public class DVector3Drawer : PropertyDrawer
 {
     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
     {
         return base.GetPropertyHeight(property.FindPropertyRelative("x"), label);
     }
 
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         var xProperty = property.FindPropertyRelative("x");
         var yProperty = property.FindPropertyRelative("y");
         var zProperty = property.FindPropertyRelative("z");
         Rect r = EditorGUI.PrefixLabel(position, label);
         float width = r.width / 3;
         r.width = width;
         EditorGUIUtility.labelWidth = 10f;
         EditorGUI.PropertyField(r, xProperty);
         r.x += width;
         EditorGUI.PropertyField(r, yProperty);
         r.x += width;
         EditorGUI.PropertyField(r, zProperty);
     }
 }

You don't have to do anything special. So when you use this type in a class it will show up as specified by the drawer:

 public class SpaceObject : MonoBehaviour
 {
     public DVector3 origin ;
     // [ ... ]
 }


Here you get multi object editing and undo support out of the box.

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 Omti1990 · Oct 20, 2020 at 06:14 PM 0
Share

Thank you,

your property drawer script works really well. I actually used the hide in inspector option, but this is obviously better.

Anyways, I don't actually understand how it does what it does. Specifically, what does

      public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
      {
          return base.GetPropertyHeight(property.FindPropertyRelative("x"), label);
      }

actually do?

I also don't really understand where it gets the X,Y,Z from that it does display. Especially since the variable name are actually in lower case: x,y,z. Or is this something unity does automatically?

avatar image Bunny83 Omti1990 · Oct 21, 2020 at 04:09 PM 1
Share

Well, by default the "combined" / parent property for the whole struct requires several "lines" of space to display. Since we want to display the struct in a single line, I simply get the property height of one of it's sub properties (which usually occupies a single line). AFAIK a single line is usually just "15f". Even though Unity itself uses hardcoded values like that from time to time I like to keep it more organised.


About the property names, Unity generally has a "beautify" method it applies to all field names it displays in the inspector. This generally converts any "camelCaseText" to "Camel Case Text". So they always start the name with a capital letter and split the string at upper case letters into seperate words. This behaviour has often lead to confusion between users.


The "PropertyField" method actually just takes care of drawing a whole property. It may even call other PropertyDrawers or DecoratorDrawer if necessary. Of course depending on how the drawer is implemented they might not necessarily be compatible with each other.

avatar image Bunny83 Omti1990 · Oct 21, 2020 at 04:12 PM 1
Share

I forgot to mention that "GetPropertyHeight" is actually called before the property is drawn to deter$$anonymous$$e how much vertical space the property needs. This space could be variable depending on many factors. A common factor is the "expanded" boolean value that every property has. It can be used to expand / collapse the property like the default behaviour for structs and sub fields.

avatar image
1

Answer by Omti1990 · Oct 17, 2020 at 01:13 PM

Found the solution myself. The problem was that the struct I was trying to put in the interface wasn't made serializable yet.

I just needed to put a [System.Serializable] in front of my struct declaration and it worked.

The code above works fine if that is considered.

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

137 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 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

BeginChangeCheck & EndChangeCheck not working? 1 Answer

Initialising List array for use in a custom Editor 1 Answer

How to properly handle Undo events in custom inspector? 0 Answers

How do I add a drop-down menu to an EditorWindow? 3 Answers

Editor Scripts in Dll 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