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 UnityCoach · Feb 13, 2018 at 01:45 PM · propertydrawer

PropertyDrawer, local members behave like static members

I've come across something that seems pretty odd to me, but I'm no expert at this.

I have a property (Object), for which I want to create a dropdown selector "on the fly", to set one of its properties.

Everything works, except for members declared in the PropertyDrawer. They behave like they all share the same value, always.

This is my custom Object and the component using them :

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using System;
 
 namespace Test
 {
     [Serializable]
     public class TestProperty : System.Object
     {
         public string testString = "None";
     }
 
     public class TestComponent : MonoBehaviour
     {
         public List<TestProperty> properties = new List<TestProperty>();
     }
 }


And this is the PropertyDrawer :

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEditor;
 
 namespace Test
 {
     [CustomPropertyDrawer(typeof(TestProperty))]
     public class TestPropertyDrawer : PropertyDrawer
     {
         SerializedProperty stringProp;
 
         void RefreshProperties ()
         {
             pNames = new string[5];
             for (int i = 0; i < 5; i++)
                 pNames[i] = Random.Range(0, 100).ToString();
         }
 
         public string [] pNames = new string[3] {"Test 1", "Test 2", "Test 3"}; // this behaves like static (common amongst all properties)
         private int _pSelection; // this behaves like static (common amongst all properties)
 
         public int pSelection
         {
             get {return _pSelection;}
             set
             {
                 if (_pSelection != value)
                 {
                     _pSelection = value;
                     stringProp.stringValue = pNames[value];
                 }
             }
         }
 
         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
         {
             stringProp = property.FindPropertyRelative("testString");
 
             var cRect = new Rect (position.x, position.y, position.width*0.20f, position.height);
             var pRect = new Rect (position.x+position.width*0.20f, position.y, position.width*0.40f, position.height);
             var sRect = new Rect (position.x+position.width*0.60f, position.y, position.width*0.40f, position.height);
 
             EditorGUI.BeginProperty (position, label, property);
 
             if (GUI.Button(cRect, "refresh"))
                 RefreshProperties();
 
             pSelection = EditorGUI.Popup (pRect, pSelection, pNames);
 
             EditorGUI.LabelField (sRect, stringProp.stringValue);
 
             EditorGUI.EndProperty();
         }
     }
 }

As a result, the list, and the selection appears to always be the same, although the mechanic works. alt text

screen-shot-2018-02-13-at-144353.png (8.7 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

2 Replies

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

Answer by Adam-Mechtley · Feb 13, 2018 at 07:51 PM

That said, it is worth acknowledging that you sometimes need per-property view state data (i.e. data that you don't actually want to store in your model). In these cases, the typical approach that we use internally is to key such data with the propertyPath for the property being drawn. Something like this:

 class MyDrawer : PropertyDrawer {
     class ViewData {
         public int someInteger;
         public string someString = string.Empty;
     }
     
     Dictionary<string, ViewData> m_PerPropertyViewData = new Dictionary<string, ViewData>();
 
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
         ViewData viewData;
         if (!m_PerPropertyViewData.TryGetValue(property.propertyPath, out viewData) {
             viewData = new ViewData();
             m_PerPropertyViewData[property.propertyPath] = viewData;
         }
         // do stuff with viewData as needed
     }
 }
Comment
Add comment · Show 1 · 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 UnityCoach · Feb 14, 2018 at 12:38 PM 0
Share

Thanks @Adam-$$anonymous$$echtley, this is exactly what I was hoping for. Let me try this.

avatar image
1

Answer by Bunny83 · Feb 13, 2018 at 04:36 PM

A property drawer should never be used to store any state that should be preserved for each instance. Unity will of course reuse the property drawer instance for all properties of the same type. It would be horrible inefficient to create a new property drawer instance for each element. The OnGUI method receives all parameters necessary to draw the property.

If you want to store information for each property seperately you would need to add that information to your "TestProperty" class

Comment
Add comment · Show 8 · 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 UnityCoach · Feb 13, 2018 at 04:49 PM 0
Share

Thanks @Bunny83, let me try that. I'll post the (hopefully) working solution and close the question.

avatar image Reahreic · Jul 07, 2021 at 11:53 AM 0
Share

Your answer, while partially accurate from a performance standpoint misses from a usability standpoint. If one is to never store property drawer display configuration data, then how would one go about using EditorGUI.Foldout in the drawer for children of a collection?

In order for the foldout to remain folded for more than one frame you'd need to store the value of the foldout's state while the drawer is visible. I really don't like having editor-only display data being stored in and cluttering up my runtime data. (even when using conditional compliation to strip it from the build)

As a gray, i'm partial to absolutes, but not everything is as cut and dry as never.

avatar image Bunny83 Reahreic · Jul 07, 2021 at 05:52 PM 0
Share

Every serializedProperty has an isExpanded property which is exactly for that usecase and is used exactly for this by the default drawer.


As for any other IMGUI related state information, there are the methods GUIUtility.GetStateObject and GUIUtility.QueryStateObject which are exactly for storing state of a certain IMGUI control ID. Those methods allows you to specify any pure data class which is created and remembered internally by the IMGUI system.

avatar image Reahreic Bunny83 · Jul 08, 2021 at 06:29 PM 0
Share

In my custom property drawer, EditorGUI.Foldout isn't bound to a runtime serialized property and as such isExpanded isn't available to me.

The below is a fragment from one of my property drawers (better?) articulating the use I'm describing. The drawer is for a serializable class that is used in a List.

   //A struct containing data returned from the dictionary for this 'view-state' 
   //as per OP's selected answer.
     
     public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label){
           //FindProperties, EditorGUI.BeginProperty, DrawRectBorder... layout boilerplate stuff.
           //Draw Object reference field
     
           //Foldout Group for Rotational Data
           //Define if has rotational data
           EditorGUI.PropertyField(rect, prop_hasRot);
           EditorGUI.BeginDisabledGroup(!prop_hasRot.boolValue);
           foldoutState.showRot = EditorGUI.Foldout(rect, prop_hasRot.boolValue && foldoutState.showRot, new GUIContent("Rotational data"));
           if(if (foldoutState.showRot) {
             //A bunch of labels, fields, and buttons for setting and manipulating the data.
           }
           EditorGUI.EndDisabledGroup();
           //Foldout Group for Translation Data (Similar to above)
           //More stuff...
     }


In the above if I didn't store the view-state data for the foldout, then opening one would open all foldouts in the list (static bool), or would open for 1 frame then close again (private bool)

I'm open to learning new things and the QueryStateObject sounds like it's an alternate to the answer given by Adam, but IsExpanded only works if I'm targeting an entire serialized class/struct as opposed to showing/hiding sections of that class/struct to reduce clutter.

p.s. The reply editor here really needs a preview to ensure formatting is working before posting lest there be 10,000 edits...

Show more comments
avatar image datnasty2_0 Reahreic · Jul 07, 2021 at 05:57 PM 0
Share

I didn't even realize this. Thanks for clearing that up.

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

79 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

Related Questions

Get SerializedProperty from outside class for use in popup (PropertyDrawer) 1 Answer

Using DuplicateCommand and DeleteCommand with Properties 0 Answers

Do PropertyDrawers support inheritance? 1 Answer

Texture2D on Property Drawers. 0 Answers

Property Drawers In Serializable Classes - GetPropertyHeight 2 Answers


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