Search code examples
unity-game-engineenumsbitmaskunity-editor

EditorGuiLayout.MaskField issue with large enums


I'm working on an input system that would allow the user to translate input mappings between different input devices and operating systems and potentially define their own.

I'm trying to create a MaskField for an editor window where the user can select from a list of RuntimePlatforms, but selecting individual values results in multiple values being selected.

Mainly for debugging I set it up to generate an equivalent enum RuntimePlatformFlags that it uses instead of RuntimePlatform:

[System.Flags]
public enum RuntimePlatformFlags: long
{
    OSXEditor=(0<<0),
    OSXPlayer=(0<<1),
    WindowsPlayer=(0<<2),
    OSXWebPlayer=(0<<3),
    OSXDashboardPlayer=(0<<4),
    WindowsWebPlayer=(0<<5),
    WindowsEditor=(0<<6),
    IPhonePlayer=(0<<7),
    PS3=(0<<8),
    XBOX360=(0<<9),
    Android=(0<<10),
    NaCl=(0<<11),
    LinuxPlayer=(0<<12),
    FlashPlayer=(0<<13),
    LinuxEditor=(0<<14),
    WebGLPlayer=(0<<15),
    WSAPlayerX86=(0<<16),
    MetroPlayerX86=(0<<17),
    MetroPlayerX64=(0<<18),
    WSAPlayerX64=(0<<19),
    MetroPlayerARM=(0<<20),
    WSAPlayerARM=(0<<21),
    WP8Player=(0<<22),
    BB10Player=(0<<23),
    BlackBerryPlayer=(0<<24),
    TizenPlayer=(0<<25),
    PSP2=(0<<26),
    PS4=(0<<27),
    PSM=(0<<28),
    XboxOne=(0<<29),
    SamsungTVPlayer=(0<<30),
    WiiU=(0<<31),
    tvOS=(0<<32),
    Switch=(0<<33),
    Lumin=(0<<34),
    BJM=(0<<35),
}

In this linked screenshot, only the first 4 options were selected. The integer next to "Platforms: " is the mask itself.

I'm not a bitwise wizard by a large margin, but my assumption is that this occurs because EditorGUILayout.MaskField returns a 32bit int value, and there are over 32 enum options. Are there any workarounds for this or is something else causing the issue?


Solution

  • First thing I've noticed is that all values inside that Enum is the same because you are shifting 0 bits to left. You can observe this by logging your values with this script.

    // Shifts 0 bits to the left, printing "0" 36 times.
    for(int i = 0; i < 36; i++){
        Debug.Log(System.Convert.ToString((0 << i), 2));
    }
    
    // Shifts 1 bits to the left, printing values up to 2^35.
    for(int i = 0; i < 36; i++){
        Debug.Log(System.Convert.ToString((1 << i), 2));
    }
    

    The reason inheriting from long does not work alone, is because of bit shifting. Check out this example I found about the issue:

    UInt32 x = ....;
    UInt32 y = ....;
    UInt64 result = (x << 32) + y;
    

    The programmer intended to form a 64-bit value from two 32-bit ones by shifting 'x' by 32 bits and adding the most significant and the least significant parts. However, as 'x' is a 32-bit value at the moment when the shift operation is performed, shifting by 32 bits will be equivalent to shifting by 0 bits, which will lead to an incorrect result.


    So you should also cast the shifting bits. Like this:

    public enum RuntimePlatformFlags : long {
        OSXEditor = (1 << 0),
        OSXPlayer = (1 << 1),
        WindowsPlayer = (1 << 2),
        OSXWebPlayer = (1 << 3),
    
        // With literals.
        tvOS = (1L << 32),
        Switch = (1L << 33),
    
        // Or with casts.
        Lumin = ((long)1 << 34),
        BJM = ((long)1 << 35),
    }