Search Unity

How to change the name of List elements in the inspector

Discussion in 'Scripting' started by Collin_Patrick, Jan 1, 2017.

Thread Status:
Not open for further replies.
  1. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    As the title suggests, I want to change the "Element 0, Element 1..." to something meaningful. I have tried looking around the internet for answers but they don't tell me what I want specifically. I want to be able to change the element names in the script that creates the list/array rather than renaming them directly in the inspector.

    Example:
    Capture.PNG

    I want to rename the elements to: Neutral, Sad, Happy, etc. so the facial expression textures are consistent between all characters and allow me to reference a specific expression with the same number across all characters. Also to be able to have the elements renamed by script and not manually each time would allow the images to be placed in the correct slot without having to reference similar assets.

    As always help is very much appreciated.
     
    Ideator likes this.
  2. Laperen

    Laperen

    Joined:
    Feb 1, 2016
    Posts:
    1,065
    If You create your own script to contain the texture then you can place in your own name variable.
    Code (CSharp):
    1. //An Array or List of a custom class like this will have a name variable for you to change
    2. [System.Serializable]//makes sure this shows up in the inspector
    3. public class TextureContain {
    4.     public string name;//your name variable to edit
    5.     public Texture tex;//place texture in here
    6. }
    This however, will cause the array or list to have a secondary drop down to reveal the name and texture, which you may feel is more of a hassle than what you currently have.

    What's wrong with just having your Texture name be your desired name for it?
     
  3. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    For the giggles, create an attribute:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class NamedArrayAttribute : PropertyAttribute
    5. {
    6.     public readonly string[] names;
    7.     public NamedArrayAttribute(string[] names) { this.names = names; }
    8. }
    9.  
    Create a PropertyDrawer (make sure this is in an 'editor' folder):
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. [CustomPropertyDrawer (typeof(NamedArrayAttribute))]public class NamedArrayDrawer : PropertyDrawer
    6. {
    7.     public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
    8.     {
    9.         try {
    10.             int pos = int.Parse(property.propertyPath.Split('[', ']')[1]);
    11.             EditorGUI.ObjectField(rect, property, new GUIContent(((NamedArrayAttribute)attribute).names[pos]));
    12.         } catch {
    13.             EditorGUI.ObjectField(rect, property, label);
    14.         }
    15.     }
    16. }
    Usage:

    Code (csharp):
    1. [NamedArrayAttribute (new string[] {"Neutral", "Happy", "Sad"})]
    2. public Texture[] textures;
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    @JohnnyA's suggestion would work. I suggest, however, that you don't use an array for this!

    Instead, make a class/struct (depending on use case) that wraps the faces. So:

    Code (csharp):
    1. [System.Serializeable]
    2. public struct FaceTextures {
    3.     public Texture neutral;
    4.     public Texture happy;
    5.     public Texture sad;
    6.     ...
    7. }
    And instead of doing this:

    Code (csharp):
    1. public Texture[] faces;
    2. ...
    3. SetFace(faces[1]);
    You do this:

    Code (csharp):
    1. public FaceTextures faces;
    2. ...
    3. SetFace(faces.happy);
    Then you don't have to remember which index is what.
     
    Grizznix, zsoltime, Proto-G and 10 others like this.
  5. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    No, No, no!

    these are all pretty usless. There are Key (string) variable like "Title" or "Key" in unity where if these are set inside the array object (as below) the element will be named to the value of this variable.


    Code (csharp):
    1.  
    2. public struct k
    3. {
    4. public string key;
    5. }
    6. public k[] Keys;
    7.  
    8.  

    However, this has two limitations, firstly the name has to be Key or Title (there may be others names), and the other limitation is it has to be a string.

    The solution to this is to make an attribute which lets you pick which variable inside the object will be the name of the element.
    Attribute
    Code (csharp):
    1.  
    2. public class ArrayElementTitleAttribute : PropertyAttribute
    3. {
    4.     public string Varname;
    5.     public ArrayElementTitleAttribute(string ElementTitleVar)
    6.     {
    7.         Varname = ElementTitleVar;
    8.     }
    9. }
    10.  

    Drawer:
    Code (csharp):
    1.  
    2. [CustomPropertyDrawer(typeof(ArrayElementTitleAttribute))]
    3. public class ArrayElementTitleDrawer : PropertyDrawer
    4. {
    5.     public override float GetPropertyHeight(SerializedProperty property,
    6.                                     GUIContent label)
    7.     {
    8.         return EditorGUI.GetPropertyHeight(property, label, true);
    9.     }
    10.     protected virtual ArrayElementTitleAttribute Atribute
    11.     {
    12.         get { return (ArrayElementTitleAttribute)attribute; }
    13.     }
    14.     SerializedProperty TitleNameProp;
    15.     public override void OnGUI(Rect position,
    16.                               SerializedProperty property,
    17.                               GUIContent label)
    18.     {
    19.         string FullPathName = property.propertyPath + "." + Atribute.Varname;
    20.         TitleNameProp = property.serializedObject.FindProperty(FullPathName);
    21.         string newlabel = GetTitle();
    22.         if (string.IsNullOrEmpty(newlabel))
    23.             newlabel = label.text;
    24.         EditorGUI.PropertyField(position, property, new GUIContent(newlabel, label.tooltip), true);
    25.     }
    26.     private string GetTitle()
    27.     {
    28.         switch (TitleNameProp.propertyType)
    29.         {
    30.             case SerializedPropertyType.Generic:
    31.                 break;
    32.             case SerializedPropertyType.Integer:
    33.                 return TitleNameProp.intValue.ToString();
    34.             case SerializedPropertyType.Boolean:
    35.                 return TitleNameProp.boolValue.ToString();
    36.             case SerializedPropertyType.Float:
    37.                 return TitleNameProp.floatValue.ToString();
    38.             case SerializedPropertyType.String:
    39.                 return TitleNameProp.stringValue;
    40.             case SerializedPropertyType.Color:
    41.                 return TitleNameProp.colorValue.ToString();
    42.             case SerializedPropertyType.ObjectReference:
    43.                 return TitleNameProp.objectReferenceValue.ToString();
    44.             case SerializedPropertyType.LayerMask:
    45.                 break;
    46.             case SerializedPropertyType.Enum:
    47.                 return TitleNameProp.enumNames[TitleNameProp.enumValueIndex];
    48.             case SerializedPropertyType.Vector2:
    49.                 return TitleNameProp.vector2Value.ToString();
    50.             case SerializedPropertyType.Vector3:
    51.                 return TitleNameProp.vector3Value.ToString();
    52.             case SerializedPropertyType.Vector4:
    53.                 return TitleNameProp.vector4Value.ToString();
    54.             case SerializedPropertyType.Rect:
    55.                 break;
    56.             case SerializedPropertyType.ArraySize:
    57.                 break;
    58.             case SerializedPropertyType.Character:
    59.                 break;
    60.             case SerializedPropertyType.AnimationCurve:
    61.                 break;
    62.             case SerializedPropertyType.Bounds:
    63.                 break;
    64.             case SerializedPropertyType.Gradient:
    65.                 break;
    66.             case SerializedPropertyType.Quaternion:
    67.                 break;
    68.             default:
    69.                 break;
    70.         }
    71.         return "";
    72.     }
    73. }
    74.  


    example

    Code (csharp):
    1.  
    2.     [System.Serializable]
    3.     public struct MyStruct
    4.     {
    5.         public enum MyEnum { hello, world }
    6.         public MyEnum m_MyEnum;
    7.     }
    8.     [ArrayElementTitle("m_MyEnum")]
    9.     public MyStruct[] m_MyStruct;
    10.  


    Note: there is no error handling for a incorrect variable name
    Note: this will take the string value (ToString) of most 5.5 variable types, ones I left out I didn't think made much sense, or would need additional formatting which you can do if you wish
     
  6. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    @BinaryCats the OP wanted to be able to name the 'slots' in an array. i.e slot one is always the 'happy' slot, slot two always the 'neutral' slot, etc.

    You may find this of questionable utility (to which @Baste provided the more typical solution), but solving a completely different problem doesn't really contribute to the discussion.
     
  7. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    This functionality is a all over solution to the same problem :- not being able to name array elements. Whether the field is hidden in inspector is up to you.

    He could for example, have a

    Code (csharp):
    1.  
    2. [hideininspector] public string SlotName.
    3.  
    Set that variable to what ever he likes "slots 1, slots2" and have the element in the array be called that. Solving a specific problem only helps one person, solving the actual problem helps everyone. Seen as this thread is the top thread that shows when googling, someone can use the attribute I provided to solve the problem.


    Again, to be clear, the real problem here is not being able to name elements what you would like them to be called, unless you use a custom editor.
     
    jorgeolothar and c_hirschi like this.
  8. sdclark79

    sdclark79

    Joined:
    Feb 6, 2017
    Posts:
    21
    I'd say your answer is a custom List, with 'public string name;' at the top, which will set the name of the Element0 to whatever the name string is. For example:
    Code (CSharp):
    1.  
    2. [System.Serializable]
    3. public class WeaponsList {
    4.  
    5.     public string name;                            // Inspector Element Name
    6.     public GameObject weaponGameObject;            // Weapon's physical GameObject
    7.     public string weaponName;                    // Weapon name
    8.     public string weaponDescription;            // Weapon description
    9.     public int weaponLevel;                        // Weapon level
    10.     public bool isRangedWeapon;                    // Is this a ranged weapon? (true=ranged; false=melee)
    11.     public bool isExplosive;                    // Is this an explosive weapon?
    12.     public float weaponRange;                    // Weapon range
    13.     public float weaponDamage;                    // Weapon damage
    14.     public float weaponFireRate;                // Weapon rate of fire
    15.  
    16.     public WeaponsList(GameObject newWeapon, string newName, string newDescription, int newLevel, bool newIsRangedWeapon, bool newIsExplosive,
    17.         float newWeaponRange, float newWeaponDamage, float newWeaponFireRate)
    18.     {
    19.         weaponGameObject = newWeapon;
    20.         weaponName = newName;
    21.         weaponDescription = newDescription;
    22.         weaponLevel = newLevel;
    23.         isRangedWeapon = newIsRangedWeapon;
    24.         isExplosive = newIsExplosive;
    25.         weaponRange = newWeaponRange;
    26.         weaponDamage = newWeaponDamage;
    27.         weaponFireRate = newWeaponFireRate;
    28.     }
    29.  
    30. }
    ... and you could definitely programmatically set the name, but your super-awesome custom List class'll look like this in the Inspector, with Element0 changed to whatever's in Name:

    Capture.PNG

    Code (CSharp):
    1.  
    2. public class Master_Inventory : MonoBehaviour {
    3.  
    4.     public static Master_Inventory Instance = null;
    5.  
    6.     public List<WeaponsList> m_WeaponsList = new List<WeaponsList> ();
    7.     public List<ResourcesList> m_ResourcesList = new List<ResourcesList> ();
    8.  
    9.     void Awake()
    10.     {
    11.         Instance = this;
    12.     }
    13. }
    14.  
     
    Last edited: Nov 28, 2017
    mamap, burningwipf, zeimhall and 10 others like this.
  9. Dsiak

    Dsiak

    Joined:
    Oct 11, 2015
    Posts:
    11
    Wonderful solutions, but I find JohnnyA's to be more practical for my case.

    Mine is a list of floats, and the index are enumerators, so it's somehow similar to the struct "face.happy" example, but the call would be face[(int)FaceTextures.happy]. The call is very a little ugly, but I have to iterate through those elements a lot and I'm more comfortable with a array for that.

    Another downside to using array is that the attribute's "new string[]" have to be compile-time constant, with means you have to manually write the name after creating a new enum / can't dynamically create a string[] with all the names of your enum's enums. This gets old pretty fast so do consider a struct / class.
     
  10. sand_lantern

    sand_lantern

    Joined:
    Sep 15, 2017
    Posts:
    210
    @Dsiak I found a way to clean up an array of (enum, value) pairs quite a bit, you don't need to worry about creating a string[] or any of that jazz. Check this out:

    Code (CSharp):
    1.  
    2. [CustomPropertyDrawer (typeof (LocalizationItem))]
    3. public class NamedArrayDrawer : PropertyDrawer {
    4.  
    5.    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    6.       SerializedProperty key = property.FindPropertyRelative ("key");
    7.       SerializedProperty value = property.FindPropertyRelative ("value");
    8.       GUIContent enumLabel = new GUIContent (key.enumDisplayNames[key.enumValueIndex]);
    9.  
    10.       EditorGUI.BeginProperty (position, label, property);
    11.       position = EditorGUI.PrefixLabel (position, GUIUtility.GetControlID (FocusType.Passive), enumLabel);
    12.       var indent = EditorGUI.indentLevel;
    13.       EditorGUI.indentLevel = 0;
    14.  
    15.       // Calculate rects
    16.       var unitRect = new Rect(position.x, position.y, position.width, position.height);
    17.       // Draw fields - passs GUIContent.none to each so they are drawn without labels
    18.       EditorGUI.PropertyField(unitRect, value.stringValue);
    19.  
    20.       // Set indent back to what it was
    21.       EditorGUI.indentLevel = indent;
    22.  
    23.       EditorGUI.EndProperty();
    24.    }
    25. }
     
  11. idbrii

    idbrii

    Joined:
    Aug 18, 2014
    Posts:
    51
    I'd agree with Baste in general, but when you are indexing the array with an enum and iterating over the array values, you really need an array. sand_lantern's solution didn't work for me.

    But thanks to JohnnyA's starter code, I figured out how to do this with an Attribute and PropertyDrawer:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. #if UNITY_EDITOR
    5. using System;
    6. using UnityEditor;
    7. #endif
    8.  
    9. // Defines an attribute that makes the array use enum values as labels.
    10. // Use like this:
    11. //      [NamedArray(typeof(eDirection))] public GameObject[] m_Directions;
    12.  
    13. public class NamedArrayAttribute : PropertyAttribute {
    14.     public Type TargetEnum;
    15.     public NamedArrayAttribute(Type TargetEnum) {
    16.         this.TargetEnum = TargetEnum;
    17.     }
    18. }
    19.  
    20. #if UNITY_EDITOR
    21. [CustomPropertyDrawer(typeof(NamedArrayAttribute))]
    22. public class NamedArrayDrawer : PropertyDrawer {
    23.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
    24.         // Properly configure height for expanded contents.
    25.         return EditorGUI.GetPropertyHeight(property, label, property.isExpanded);
    26.     }
    27.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    28.         // Replace label with enum name if possible.
    29.         try {
    30.             var config = attribute as NamedArrayAttribute;
    31.             var enum_names = System.Enum.GetNames(config.TargetEnum);
    32.             int pos = int.Parse(property.propertyPath.Split('[', ']')[1]);
    33.             var enum_label = enum_names.GetValue(pos) as string;
    34.             // Make names nicer to read (but won't exactly match enum definition).
    35.             enum_label = ObjectNames.NicifyVariableName(enum_label.ToLower());
    36.             label = new GUIContent(enum_label);
    37.         } catch {
    38.             // keep default label
    39.         }
    40.         EditorGUI.PropertyField(position, property, label, property.isExpanded);
    41.     }
    42. }
    43. #endif
    44.  
     
  12. jj_unity328

    jj_unity328

    Joined:
    Jun 7, 2018
    Posts:
    22
    If you just need Unity to NOT use the first string it finds as the name of that element (which might be totally unrelated or misleading) you can put a `[HideInInspector] public string dummy;` as first element in your serialized class.
     
    Tony1974 likes this.
  13. MerovingL

    MerovingL

    Joined:
    Sep 19, 2016
    Posts:
    11
    I used
    Very interesting and usefull thread.
    I used this sollution, and it works fine with textures.
    But when I made array of colors, it doesn't work.

    Code (csharp):
    1.  
    2.     [NamedArrayAttribute (new string[] {"Neutral", "Happy", "Sad"})]
    3.     public Color[] colors;
    4.  
    upload_2018-9-28_16-8-57.png
     
    Last edited: Sep 28, 2018
    adityaspt, DrivZone and Luc36 like this.
  14. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    It should be a PropertyField rather than an ObjectField, to support all kinds of properties.
     
  15. MerovingL

    MerovingL

    Joined:
    Sep 19, 2016
    Posts:
    11
    Thank you!
     
  16. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    After finding this thread, I fell in love with this attribute! It is a very neat enhancement. However, I stumbled upon the problem that it doesn't work with nested arrays. If you have a serialized array of a custom class, which also has a serialized array, with both having their own
    NamedArrayAttribute
    , it leads to odd behavior because of the assumptions behind the line
    int pos = int.Parse(property.propertyPath.Split('[', ']')[1]);
    in the attribute drawer.

    Such a path could look like
    modifiers.containers.Array.data[0].containers.Array.data[0]
    and adding a
    NamedArrayAttribute
    to both arrays fails to replace the array element label properly. I modified the implementation for
    OnGUI()
    by @idbrii as it follows, adding
    using System.Text.RegularExpressions;
    to the using directives:

    Code (CSharp):
    1.  public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2.     {
    3.         // Replace label with enum name if possible.
    4.         try
    5.         {
    6.             var config = attribute as NamedArrayAttribute;
    7.             var enum_names = Enum.GetNames(config.TargetEnum);
    8.  
    9.             var match = Regex.Match(property.propertyPath, "\\[(\\d)\\]", RegexOptions.RightToLeft);
    10.             int pos = int.Parse(match.Groups[1].Value);
    11.  
    12.             // Make names nicer to read (but won't exactly match enum definition).
    13.             var enum_label = ObjectNames.NicifyVariableName(enum_names[pos].ToLower());
    14.             label = new GUIContent(enum_label);
    15.         }
    16.         catch
    17.         {
    18.             // keep default label
    19.         }
    20.         EditorGUI.PropertyField(position, property, label, property.isExpanded);
    21.     }
    This works for nested objects having serialized arrays, each array having their own
    NamedArrayAttribute
    .
     
    arsenal4567 and joaoborks like this.
  17. thebarryman

    thebarryman

    Joined:
    Nov 22, 2012
    Posts:
    130
    What would be great is if the struct/class itself could provide the name, like this:

    Code (CSharp):
    1. string name => otherProperty.ToString();
    Or alternatively, use the value returned by the type's ToString() method. Instead "name" has to be a serialized field or requires a custom PropertyDrawer. Sigh...
     
    Last edited: Jul 21, 2020
    Ledii, lyzard, MaskedMouse and 6 others like this.
  18. Ledii

    Ledii

    Joined:
    May 30, 2013
    Posts:
    9
    Exactly my thoughts as well!
    I did how ever find a workaround which allows something simular to this.
    For my inventory system, I wanted something that looks like this...
    unity-inspector.png
    My solution for this looks as follows...
    Code (CSharp):
    1. [System.Serializable]
    2. public class Stack
    3. {
    4.     [HideInInspector]
    5.     public string name;
    6.     public ItemBase item;
    7.     public int amount;
    8.  
    9.     public void Validate()
    10.     {
    11.         name = (item) ? item.DisplayName + " (x" + amount + ")" : "No item";
    12.     }
    13. }
    Then call Validate from any available event source.
    OnValidate, OnDrawGizmosSelected, etc...

    PS: If you want to do this with structs, you will have to assign the modified struct back to the correct index. As they are not updated by reference, like a class.
     
    Last edited: Sep 11, 2021
    burakkurkcu, akuno, Mikael-H and 4 others like this.
  19. vapgames

    vapgames

    Joined:
    Aug 25, 2012
    Posts:
    9
    Hey, i just came up with a simple trick for adding custom array names
    Code (CSharp):
    1. [System.Serializable]
    2.     public class WeaponInfo
    3.     {
    4.         [HideInInspector]
    5.         public string name;
    6.         public CommonConstants.GunID gunID;
    7.         public GameObject gunRigPrefab;
    8.     }
    9.  
    10.     public WeaponInfo[] enemyWeaponInfos;
    11.  
    12.     private void OnDrawGizmosSelected() {
    13.         //NOW JUST SET THE NAMES TO WHATEVER YOU WANT
    14.         for (int i = 0; i < enemyWeaponInfos.Length; ++i) {
    15.             enemyWeaponInfos[i].name = enemyWeaponInfos[i].gunID.ToString();
    16.         }
    17.     }
    Well, i probably don't recommend using this if you have more than a hundred instances in the scene.
     
  20. sandstedt

    sandstedt

    Joined:
    May 24, 2015
    Posts:
    67
    Thanks @vapgames and @Ledii, made a nice mesh of your ideas here. This is the result:

    upload_2021-11-7_13-22-43.png

    A custom class to hold a Scriptable Object (MaterialSO) and a amount of them:

    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     public class MaterialStack
    4.     {
    5.         [HideInInspector]
    6.         public string name; // used used for the inspector list name, same as the MaterialSO.title
    7.         public MaterialSO material;
    8.         public byte amount;
    9.  
    10.         public MaterialStack(byte _amount, MaterialSO _material)
    11.         {
    12.             amount = _amount;
    13.             material = _material;
    14.         }
    15.     }
    And then another Scriptable Object called a Recipe with the Unity OnValidate function, looping through each Material and adding the amount to the hidden name string:

    Code (CSharp):
    1.     [CreateAssetMenu(fileName = " Material", menuName = "Game Name/Recipe", order = 1)]
    2.     public class RecipeSO : ScriptableObject
    3.     {
    4.         public string title;
    5.         public List<MaterialStack> materials;
    6.  
    7.         private void OnValidate()
    8.         {
    9.             foreach (var t in materials)
    10.             {
    11.                 t.name = t.material ? t.material.title + " (x" + t.amount + ")" : "No item";
    12.             }
    13.         }
    14.     }
     
  21. ButtCleaves

    ButtCleaves

    Joined:
    Jun 17, 2015
    Posts:
    9
    Based on what I read here. Add a hidden name field. Update the elements of your array in OnValidate
    Code (CSharp):
    1.    
    2.     [Serializable]
    3.     public class LessSimpleNode
    4.     {
    5.         [HideInInspector]
    6.         public string name;
    7.  
    8.         public AudioClip clip;  
    9.         public override string ToString()
    10.         {
    11.             if (clip == null)
    12.                 return "(no clip)";
    13.             return clip.name;
    14.         }
    15.     }
    16.  
    17.     [SerializeField]
    18.     private LessSimpleNode[] clips;
    19.  
    20.     private void OnValidate()
    21.     {
    22.         DopeArrayEditor(clips);
    23.     }
    24.  
    25.     static void DopeArrayEditor<T>(System.Collections.Generic.IEnumerable<T> clips)
    26.     {
    27.         if (clips == null)
    28.             return;
    29.         var prop = typeof(T).GetField("name");
    30.         if (prop == null)
    31.             return;
    32.         foreach (var n in clips)
    33.             prop.SetValue(n, n.ToString());
    34.     }
    35.  
     
  22. notagame

    notagame

    Joined:
    Sep 17, 2017
    Posts:
    17
    This is atrocious :D. @ButtCleaves has already a good approach, you can improve this further with serialization events:

    Code (CSharp):
    1. public class SkillEntry : ISerializationCallbackReceiver
    2.     {
    3.         [HideInInspector]
    4.         [SerializeField] private string name;
    5.         [SerializeField] private EntitySkills _id;
    6.      
    7.         public void OnBeforeSerialize()
    8.         {
    9.             name = _id.ToString();
    10.         }
    11.  
    12.         public void OnAfterDeserialize()
    13.         {
    14.             name = _id.ToString();
    15.         }
    16.     }
    This way you can chose the name consistently based on your data. Don't mind the Entity and Skill stuff, it's just an excerpt from my current project. The important part is ISerializationCallbackReceiver and the name property.
     
  23. qpuilie

    qpuilie

    Joined:
    Jan 14, 2020
    Posts:
    69
    Oh! Thanks,not all heroes wear capes!
     
  24. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    Please create your own thread rather than hijacking an existing one. This is why we have threads with lots of different subjects being discussed.

    Thanks.
     
  25. unity_FC1EEF5588E76CE80417

    unity_FC1EEF5588E76CE80417

    Joined:
    Jan 25, 2022
    Posts:
    11
    sure thing!
     
    MelvMay likes this.
  26. Xihan118

    Xihan118

    Joined:
    Apr 2, 2019
    Posts:
    4
    Yep, it's as simple as putting a string named "name" in code. Thanks @Laparen
    Code (CSharp):
    1. public string name;
     
  27. lgarczyn

    lgarczyn

    Joined:
    Nov 23, 2014
    Posts:
    68
    Instead of using serialized values for names, I tried to add a dynamic solution. You just need to add an interface to the struct you want to name

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. namespace Attributes
    5. {
    6.   public class ArrayElementTitleAttribute : PropertyAttribute
    7.   {
    8.     public string VarName { get; }
    9.  
    10.     public ArrayElementTitleAttribute(string elementTitleVar = "")
    11.     {
    12.       VarName = elementTitleVar;
    13.     }
    14.   }
    15.  
    16.   public interface IArrayElementTitle
    17.   {
    18.     public string Name
    19.     {
    20.       get;
    21.     }
    22.   }
    23. }
    24.  
    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. namespace Attributes.Editor
    6. {
    7.   [CustomPropertyDrawer(typeof(ArrayElementTitleAttribute))]
    8.   public class ArrayElementTitleAttributeDrawer : PropertyDrawer
    9.   {
    10.     SerializedProperty _titleNameProp;
    11.  
    12.     protected virtual ArrayElementTitleAttribute Attribute => (ArrayElementTitleAttribute)attribute;
    13.  
    14.     public override float GetPropertyHeight(SerializedProperty property,
    15.                                             GUIContent label)
    16.     {
    17.       return EditorGUI.GetPropertyHeight(property, label, true);
    18.     }
    19.  
    20.     public override void OnGUI(Rect position,
    21.                                SerializedProperty property,
    22.                                GUIContent label)
    23.     {
    24.       if (property.boxedValue is IArrayElementTitle titled)
    25.       {
    26.         label = new GUIContent(label) { text = titled.Name };
    27.       }
    28.       else
    29.       {
    30.         string fullPathName = property.propertyPath + "." + Attribute.VarName;
    31.         SerializedProperty nameProp = property.serializedObject.FindProperty(fullPathName);
    32.         if (nameProp != null)
    33.           label = new GUIContent(label) { text = GetTitle(nameProp) };
    34.         else
    35.           Debug.LogWarning(
    36.             $"Could not get name for property path {fullPathName}, did you define a path or inherit from IArrayElementTitle?");
    37.       }
    38.  
    39.       EditorGUI.PropertyField(position, property, label, true);
    40.     }
    41.  
    42.     string GetTitle(SerializedProperty prop)
    43.     {
    44.       switch (prop.propertyType)
    45.       {
    46.         case SerializedPropertyType.Generic:
    47.           break;
    48.         case SerializedPropertyType.Integer:
    49.           return prop.intValue.ToString();
    50.         case SerializedPropertyType.Boolean:
    51.           return prop.boolValue.ToString();
    52.         case SerializedPropertyType.Float:
    53.           return prop.floatValue.ToString("G");
    54.         case SerializedPropertyType.String:
    55.           return prop.stringValue;
    56.         case SerializedPropertyType.Color:
    57.           return prop.colorValue.ToString();
    58.         case SerializedPropertyType.ObjectReference:
    59.           return prop.objectReferenceValue.ToString();
    60.         case SerializedPropertyType.LayerMask:
    61.           break;
    62.         case SerializedPropertyType.Enum:
    63.           return prop.enumNames[prop.enumValueIndex];
    64.         case SerializedPropertyType.Vector2:
    65.           return prop.vector2Value.ToString();
    66.         case SerializedPropertyType.Vector3:
    67.           return prop.vector3Value.ToString();
    68.         case SerializedPropertyType.Vector4:
    69.           return prop.vector4Value.ToString();
    70.       }
    71.  
    72.       return "";
    73.     }
    74.   }
    75. }
    76.  
    Example usage

    Code (CSharp):
    1. using System;
    2. using Attributes;
    3. using UnityAtoms;
    4.  
    5. namespace Atoms.Preferences
    6. {
    7.   [Serializable]
    8.   public struct AtomPreference: IArrayElementTitle
    9.   {
    10.     public string key;
    11.     public AtomBaseVariable variable;
    12.     public string Name => key;
    13.   }
    14. }
    Code (CSharp):
    1.  
    2.     [ArrayElementTitle] [SerializeField]
    3.     AtomPreference[] preferences;
    4.  
     
    seeyam likes this.
  28. brunogattai1996

    brunogattai1996

    Joined:
    Jan 18, 2020
    Posts:
    5
    Awesome! Thank you very much.
     
  29. karldv

    karldv

    Joined:
    Jul 2, 2014
    Posts:
    11
    Hi
    Hi, I tested your code and it works great with the textures array variable, but when replace it with a string array I get a repeated error message in the console...

    type is not a supported pptr value
    UnityEditor.EditorGUI:ObjectField (UnityEngine.Rect,UnityEditor.SerializedProperty,UnityEngine.GUIContent)
    NamedArrayDrawer:OnGUI (UnityEngine.Rect,UnityEditor.SerializedProperty,UnityEngine.GUIContent) (at Assets/Editor/NamedArrayDrawer.cs:10)
    UnityEngine.GUIUtility:processEvent (int,intptr,bool&)

    tried with float, integer and the error message keeps showing in the console, can you help please?
     
  30. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    I have answered the same question in this thread!
     
    karldv likes this.
  31. karldv

    karldv

    Joined:
    Jul 2, 2014
    Posts:
    11
    Found it, thanks a lot Baste :)
     
  32. kinglotrich

    kinglotrich

    Joined:
    Jun 2, 2023
    Posts:
    29
    you are amazing and saved my future you cannot know how long ı was searching for it I even thought buy odin inspector only for this. Although it works perfect it gives some invisible errors, ı did not see them until I try to saw yellow error also saw others since they dont give any error in editor and vs code says no issue found alsı game works fine. do you know any idea?
    upload_2023-12-28_16-23-39.png
     
Thread Status:
Not open for further replies.