In a more than 32 options, when I select only one there is a problem of checking other options, how to solve it please
An enum using [Flags] produces a bitmask (int) and only has 32-bits where each bit is an option. Therefore it only has 32-bits.
It does depend on the underlying type used by the enum in script but MaskField uses an int.
I’m sorry I don’t fully understand, can you provide a more detailed explanation, such as an example
The MaskField works with “bit masks”. Essentially a single 32 bit integer value. As you probably know data in a computer is stored in binary 1 and 0. A normal integer value has 32 bits and is composed of 4 bytes (a byte has 8 bits).
So the decimal value 42
in binary would be
00000000 00000000 00000000 00101010
Just like in the decimal system each position in a number has a different base value. In the decimal system you have at the far right the units, going left you have the 10s, then the 100s, 1000s and so on.
In binary everything is based on the base “2”. So we again have the units at the far right and to the left the 2s, then the 4s, 8s, 16s, 32s, 64s, … Each bit can only have a value of either 0 or 1. So either you have the value of a certain place or not.
A bit mask now uses the individual bits in an integer to represent 32 boolean values that can be on or off. That’s why the MaskField is limited to 32 values as there are only 32 bits to work with. That’s also why Unity only has 32 layers for example since working with layermasks is extremely quick and efficient.
So if you have more than 32 items you want to select, this can not be represented by a 32 bit integer and you can not use the MaskField. You may construct your own GenericMenu and handle the “selection” yourself. It’s not completely clear what your goal is, so it’s kinda hard to give you more detailed directions.
Just in case you’re looking for a solution to display a selection menu to select certain assemblies, you can do something like this:
AssemblyList
namespace B83.AssemblyListField
{
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
[System.Serializable]
public class AssemblyItem
{
[System.NonSerialized]
private Assembly m_Assembly;
public string displayName;
public string fullName;
public Assembly assembly => GetAssembly();
public AssemblyItem(Assembly aAssembly)
{
m_Assembly = aAssembly;
fullName = aAssembly.FullName;
displayName = aAssembly.GetName().Name;
}
public Assembly GetAssembly()
{
if (m_Assembly == null)
AssemblyCache.assemblies.TryGetValue(fullName, out m_Assembly);
return m_Assembly;
}
public override bool Equals(object obj)
{
if (obj is AssemblyItem other)
return fullName == other.fullName;
return false;
}
public override int GetHashCode()
{
return fullName.GetHashCode();
}
}
public class AssemblyCache
{
public static Dictionary<string, Assembly> assemblies;
public static List<AssemblyItem> assemblyList;
static AssemblyCache()
{
assemblies = new Dictionary<string, Assembly>();
assemblyList = new List<AssemblyItem>();
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
{
assemblies.Add(a.FullName, a);
assemblyList.Add(new AssemblyItem(a));
}
assemblyList.Sort((a, b) => a.fullName.CompareTo(b.fullName));
}
}
[System.Serializable]
public class AssemblyList
{
public List<AssemblyItem> list = new List<AssemblyItem>();
}
#if UNITY_EDITOR
namespace Editor
{
using UnityEditor;
[CustomPropertyDrawer(typeof(AssemblyList))]
public class AssemblyListPropertyDrawer : PropertyDrawer
{
private class Item
{
public AssemblyItem item;
public int index;
}
SerializedProperty list;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
list = property.FindPropertyRelative("list");
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, label);
if (GUI.Button(position,"Select", EditorStyles.toolbarDropDown))
{
OpenMenu();
}
EditorGUI.EndProperty();
}
void OpenMenu()
{
// purge outdated elements
for (int i = list.arraySize - 1; i>=0; i--)
{
var e = list.GetArrayElementAtIndex(i);
var fName = e.FindPropertyRelative("fullName").stringValue;
if (!AssemblyCache.assemblies.ContainsKey(fName))
list.DeleteArrayElementAtIndex(i);
}
list.serializedObject.ApplyModifiedPropertiesWithoutUndo();
// create drop down menu
var menu = new GenericMenu();
foreach (var a in AssemblyCache.assemblyList)
{
int index = -1;
int count = list.arraySize;
for (int i = 0; i < count; i++)
{
var e = list.GetArrayElementAtIndex(i);
var fName = e.FindPropertyRelative("fullName").stringValue;
if (fName == a.fullName)
{
index = i;
break;
}
}
string name = a.displayName.Replace(".", "/");
menu.AddItem(new GUIContent(name), index >= 0, OnClick, new Item { index=index, item = a });
}
menu.ShowAsContext();
}
private void OnClick(object aItem)
{
Item item = (Item)aItem;
if (item.index >= 0)
{
list.DeleteArrayElementAtIndex(item.index);
list.serializedObject.ApplyModifiedProperties();
}
else
{
int index = list.arraySize;
list.arraySize++;
var element = list.GetArrayElementAtIndex(index);
element.FindPropertyRelative("fullName").stringValue = item.item.fullName;
element.FindPropertyRelative("displayName").stringValue = item.item.displayName;
list.serializedObject.ApplyModifiedProperties();
}
}
}
}
#endif
}
With this in your project you can delcare a field like this
public AssemblyList assemblies;
and you get a similar drop down button that displays all loaded assemblies and allows you to select as many as you with. It actually stores "AssemblyItem"s inside a List. An AssemblyItem internally just stores the fullname and the displayname of the assembly. Though through the AssemblyCache you can access the actual assembly directly from the AssemblyItem.
When you click an item in the dropdown menu, it will simply be added at the end of the list if it’s not yet in the list. If it is already in the list it will be removed. Since projects could change, whenever you open the dropdown menu it first removes any elements from the list that could not be found in the assembly cache. This avoids having invalid lingering items in the list that do no longer exist (and therefore you could not deselect anymore).
Note that assembly list is not sorted. So they appear in the order you have selected them. Feel free to sort the list if you need to.
Finally since the list of assemblies is quite long, I decided to replace the dots in the assembly names with slashes. This would automatically create submenus which essentially groups assemblies which start with the same name like UnityEngine.XXXX or Unity.XXXX. This makes the dropdown a bit cleaner. Though the UnityEngine list is still quite long as there are many assemblies that start with UnityEngine.
Thank you very much for the explanation, I will try your suggestion, thanks again!
I would love for MaskField to support 64 bit.