Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question How do I store a component type in a variable?

Discussion in 'Scripting' started by Muium, May 15, 2024.

  1. Muium

    Muium

    Joined:
    Feb 17, 2021
    Posts:
    7
    I want to be able to have a public variable in inspector where I can hold components. I don't want a reference to a specific component on a specific object, I want a reference to a component type.

    For example, If I want to check if an object has a component, I would do as below:

    Code (CSharp):
    1.     public Component componentVar;
    2.  
    3.     private void Start()
    4.     {
    5.         if (gameObject.GetComponent<componentVar>())
    6.         {
    7.             //do stuff
    8.         }
    9.     }
    The Component class holds references to specific components on objects, not the type. I would like for componentVar to be able to hold any component type, e.g. BoxCollider or Transform.

    I also have a second problem where GetComponent doesn't recognise componentVar, and doesn't seem to work with any variables of any kind afaik. I'm unsure what to do about that as well. I would like to use ComponentVar as a variable for GetComponent specifically, so I need that to be working as well... So this is kinda two questions.

    Apologies for any misuse of terminology, any help is appreciated
     
  2. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    840
    I don't believe there's a built-in way to serialize a type, but the Addressables package has the SerializedType struct which you can use as a field in your component along with SerializedTypeRestrictionAttribute which you can use to restrict the field's accepted types to subclasses of Component. Once you have a type, you just need to use the non-generic overload of GetComponent or TryGetComponent.

    Code (csharp):
    1. [SerializedTypeRestriction(type = typeof(Component))]
    2. public SerializedType componentType;
    3.  
    4. private void Start()
    5. {
    6.   Type componentTypeValue = componentType.Value;
    7.   if (componentTypeValue != null
    8.       && TryGetComponent(componentTypeValue, out Component component))
    9.   {
    10.  
    11.   }
    12. }
    Edit: looking around the internet a bit, I also see this code by Bunny83 right here on the Unity forum. That should handle the whole serialization bit without needing a big package. Not sure about the editor UI aspect of this, Addressables itself uses a bunch of IMGUI stuff to get it done...
     
    Last edited: May 15, 2024
    SisusCo and spiney199 like this.
  3. Muium

    Muium

    Joined:
    Feb 17, 2021
    Posts:
    7

    Thanks! I've got it working, unfortunately there's new problems related to it. I wanted to use this variable inside an array of a nested class, but this throws errors and doesn't show in the inspector. It breaks some of the other UI as well, any component below is missing the label, but can still be opened and edited. The "add component" button also goes missing. And of course, the variable itself is not visible. The image below should show what I mean.
    Screenshot 2024-05-15 171929.png
    The gap between "Element 0" and "parent" is where it has been declared, but it doesn't show, and "Script GV" is the start of a new component.

    I tried to get around this by using an array instead, but it still wouldn't work. Image below.
    Screenshot 2024-05-15 172056.png
    This is set in the main class, not nested.

    I then tried to see if it would work if the nested class was not in an array, but it did not work.
    Screenshot 2024-05-15 171704.png
    Again, the gap is where it should appear.

    It works fine when the variable is set not nested, and not an array. That is exactly what I asked for, however I didn't anticipate that it wouldn't work in some situations. I need it to work in the first case, In a nested class within an array. The other two were for testing, to try and solve the issue, but I don't know how to fix it, so I'm hoping someone here can help.

    Here is my current code to declare this array of nested classes, if it helps:
    Code (CSharp):
    1. public class Manager : MonoBehaviour
    2. {
    3.     [System.Serializable]
    4.     public class CollectOptions
    5.     {
    6.         [SerializedTypeRestriction(type = typeof(Component))]
    7.         public SerializedType componentType;
    8.  
    9.         public GameObject parent = null;
    10.         public string tag;
    11.     }
    12. }
    There is more code within the class Manager, but that's removed here for convenience.

    And of course, the error in question, is:
    NullReferenceException: Object reference not set to an instance of an object
    UnityEditor.AddressableAssets.GUI.SerializedTypeDrawer.OnGUIMultiple (UnityEngine.Rect position, UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean showMixed)
    It throws twice.

    When clicked, it takes me to this:
    Code (CSharp):
    1. foreach (var type in m_Types)
    2.                 typeContent.Add(new GUIContent(AddressableAssetUtility.GetCachedTypeDisplayName(type), ""));
    The first line has the error, this is in SerializedTypeDrawer, part of the Addressables package. Worth noting I know nothing about the Addressables package so if that's the solution, please keep that in mind.

    It also caused a stack overflow when I kept interacting with the UI, maybe wasn't the smartest move.


    I'm unsure what to do and am giving as much info as i can about the issue in the hopes that someone has a solution to this. I'm hoping its something silly because I'm not experienced with this type of thing.
     
  4. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    840
    That seems quite buggy, not what I expected from a first-party package... Which version of Unity are you using, just so we're on the same page?

    Also, I tried my hand at implementing a UI for Bunny's SerializableType. It's tweaked to use strings for UI Toolkit bindability and generics for filtering which types can be assigned. It seems to work well enough on my end when using it in an array (and a list) and multi-selecting, on Unity 6000.0.1f1. Maybe this works a little better?

    Usage example:
    Code (csharp):
    1.     public SerializableType<Component> serializableType;
    2.     void Start()
    3.     {
    4.         Debug.Log(serializableType.type);
    5.     }
    Code:
    Code (csharp):
    1. using System;
    2. using System.Buffers;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.IO;
    6. using System.Linq;
    7. #if UNITY_EDITOR
    8. using UnityEditor;
    9. using UnityEditor.UIElements;
    10. #endif
    11. using UnityEngine;
    12. using UnityEngine.UIElements;
    13.  
    14. [Serializable]
    15. public struct SerializableType<T>
    16. {
    17.     public Type type
    18.     {
    19.         get
    20.         {
    21.             return SerializableTypeUtility.ReadFromString(data);
    22.         }
    23.         set
    24.         {
    25.             data = SerializableTypeUtility.WriteToString(value);
    26.         }
    27.  
    28.     }
    29.  
    30.     public string data;
    31. }
    32.  
    33. public static class SerializableTypeUtility
    34. {
    35.     // Type serialization from https://forum.unity.com/threads/serializable-system-type-get-it-while-its-hot.187557/#post-8786302
    36.     public static Type Read(BinaryReader aReader)
    37.     {
    38.         var paramCount = aReader.ReadByte();
    39.         if (paramCount == 0xFF)
    40.             return null;
    41.         var typeName = aReader.ReadString();
    42.         var type = System.Type.GetType(typeName);
    43.         if (type == null)
    44.             throw new Exception("Can't find type; '" + typeName + "'");
    45.         if (type.IsGenericTypeDefinition && paramCount > 0)
    46.         {
    47.             var p = new Type[paramCount];
    48.             for (int i = 0; i < paramCount; i++)
    49.             {
    50.                 p[i] = Read(aReader);
    51.             }
    52.             type = type.MakeGenericType(p);
    53.         }
    54.         return type;
    55.     }
    56.  
    57.     public static Type ReadFromString(string text)
    58.     {
    59.         int n = (int)((long)text.Length * 3 / 4);
    60.         if (string.IsNullOrWhiteSpace(text))
    61.         {
    62.             return null;
    63.         }
    64.         byte[] tmp = ArrayPool<byte>.Shared.Rent(n);
    65.         try
    66.         {
    67.  
    68.             if (!Convert.TryFromBase64String(text, tmp, out int nActual))
    69.             {
    70.                 return null;
    71.             }
    72.             using (var stream = new MemoryStream(tmp, 0, nActual))
    73.             using (var r = new BinaryReader(stream))
    74.             {
    75.                 return Read(r);
    76.             }
    77.         }
    78.         finally
    79.         {
    80.             ArrayPool<byte>.Shared.Return(tmp);
    81.         }
    82.     }
    83.  
    84.     public static string WriteToString(Type type)
    85.     {
    86.         using (var stream = new MemoryStream())
    87.         using (var w = new BinaryWriter(stream))
    88.         {
    89.             Write(w, type);
    90.             return Convert.ToBase64String(stream.ToArray());
    91.         }
    92.     }
    93.  
    94.     public static void Write(BinaryWriter aWriter, Type aType)
    95.     {
    96.         if (aType == null)
    97.         {
    98.             aWriter.Write((byte)0xFF);
    99.             return;
    100.         }
    101.         if (aType.IsGenericType)
    102.         {
    103.             var t = aType.GetGenericTypeDefinition();
    104.             var p = aType.GetGenericArguments();
    105.             aWriter.Write((byte)p.Length);
    106.             aWriter.Write(t.AssemblyQualifiedName);
    107.             for (int i = 0; i < p.Length; i++)
    108.             {
    109.                 Write(aWriter, p[i]);
    110.             }
    111.             return;
    112.         }
    113.         aWriter.Write((byte)0);
    114.         aWriter.Write(aType.AssemblyQualifiedName);
    115.     }
    116. }
    117.  
    118. #if UNITY_INCLUDE_TESTS
    119. internal class Tests
    120. {
    121.     private static readonly Type[] s_types =
    122.         {
    123.             typeof(int),
    124.             typeof(List<UnityEngine.Object>),
    125.             typeof(Dictionary<UnityEngine.Object, HashSet<Renderer>>),
    126.             typeof(Component[])
    127.         };
    128.  
    129.     [NUnit.Framework.Test]
    130.     public void Conversion_Success([NUnit.Framework.ValueSource(nameof(s_types))] Type type)
    131.     {
    132.         var converted = SerializableTypeUtility.WriteToString(type);
    133.         NUnit.Framework.Assert.That(SerializableTypeUtility.ReadFromString(converted), NUnit.Framework.Is.EqualTo(type));
    134.     }
    135. }
    136. #endif
    137.  
    138. #if UNITY_EDITOR
    139. [CustomPropertyDrawer(typeof(SerializableType<>), true)]
    140. internal class SerializableTypePropertyDrawer : PropertyDrawer
    141. {
    142.     public override VisualElement CreatePropertyGUI(SerializedProperty property)
    143.     {
    144.         var targetObjectType = DetermineTargetType(fieldInfo.FieldType);
    145.         var field = new SerializableTypeField(targetObjectType, property.displayName);
    146.         var targetProperty = property.FindPropertyRelative("data");
    147.         field.TrackPropertyValue(targetProperty, field.UpdateDisplay);
    148.         field.BindProperty(targetProperty);
    149.         field.UpdateDisplay(targetProperty);
    150.         return field;
    151.     }
    152.  
    153.     private static Type GetElementType(Type t)
    154.     {
    155.         if (t.IsGenericType)
    156.         {
    157.             return t.GetGenericArguments()[0];
    158.         }
    159.         return t;
    160.     }
    161.  
    162.     private static Type DetermineTargetType(Type t)
    163.     {
    164.         if (typeof(IEnumerable).IsAssignableFrom(t) && t.IsGenericType)
    165.         {
    166.             return GetElementType(t.GetGenericArguments()[0]);
    167.         }
    168.         if (t.IsArray)
    169.         {
    170.             return GetElementType(t.GetElementType());
    171.         }
    172.         return GetElementType(t);
    173.     }
    174. }
    175.  
    176. internal partial class SerializableTypeField : BaseField<string>
    177. {
    178.     private readonly VisualElement _content;
    179.     private readonly TextField _textField;
    180.     private readonly Button _selectButton;
    181.     private readonly TypeSelectorPopupWindowContent _typeSelectorPopupWindowContent;
    182.     private readonly List<Type> _filteredTypes;
    183.  
    184.     public SerializableTypeField(Type filterType) : this(filterType, null)
    185.     {
    186.     }
    187.  
    188.     public SerializableTypeField(Type filterType, string label) : this(filterType, label, new VisualElement())
    189.     {
    190.     }
    191.  
    192.     public SerializableTypeField(Type filterType, string label, VisualElement visualInput) : base(label, visualInput)
    193.     {
    194.         AddToClassList(alignedFieldUssClassName);
    195.         _content = visualInput;
    196.         _content.style.flexDirection = FlexDirection.Row;
    197.         _content.Add(_textField = new TextField() { style = { marginBottom = 0, marginLeft = 0, marginRight = 0, marginTop = 0, flexShrink = 1, flexGrow = 1 }, enabledSelf = false });
    198.         _content.Add(_selectButton = new Button(OpenSelector) { style = { marginBottom = 0, marginLeft = 0, marginRight = 0, marginTop = 0 }, text = ">" });
    199.         _typeSelectorPopupWindowContent = new TypeSelectorPopupWindowContent(this);
    200.         _filteredTypes = TypeCache.GetTypesDerivedFrom(filterType).OrderBy(static s => s.FullName, StringComparer.InvariantCulture).ToList();
    201.     }
    202.  
    203.     protected override void UpdateMixedValueContent()
    204.     {
    205.         UpdateDisplay("\u2014");
    206.         //_selectButton.SetEnabled(false);
    207.     }
    208.  
    209.     internal void UpdateDisplay(SerializedProperty serializedProperty)
    210.     {
    211.         UpdateDisplay(SerializableTypeUtility.ReadFromString(serializedProperty.stringValue));
    212.         //_selectButton.SetEnabled(true);
    213.     }
    214.  
    215.     private void UpdateDisplay(Type type)
    216.     {
    217.         if (type != null)
    218.         {
    219.             UpdateDisplay(GetFormattedType(type));
    220.         }
    221.         else
    222.         {
    223.             UpdateDisplay("Not Set");
    224.         }
    225.  
    226.     }
    227.     private void UpdateDisplay(string text)
    228.     {
    229.         _textField.value = text;
    230.     }
    231.  
    232.     internal static string GetFormattedType(Type t)
    233.     {
    234.         return $"{t.Name} ({t.FullName})";
    235.     }
    236.  
    237.     public List<Type> GetFilteredTypes()
    238.     {
    239.         return _filteredTypes;
    240.     }
    241.  
    242.     private void OpenSelector()
    243.     {
    244.         UnityEditor.PopupWindow.Show(worldBound, _typeSelectorPopupWindowContent);
    245.     }
    246.  
    247.     private class TypeSelectorPopupWindowContent : PopupWindowContent
    248.     {
    249.         private const int itemHeight = 18;
    250.         private readonly SerializableTypeField _field;
    251.         private readonly Toolbar _toolbar;
    252.         private readonly ToolbarSearchField _search;
    253.         private readonly ListView _listView;
    254.         private List<Type> _types;
    255.         private bool _pauseSelectionCheck;
    256.  
    257.         internal TypeSelectorPopupWindowContent(SerializableTypeField field)
    258.         {
    259.             _field = field;
    260.             _listView = new ListView
    261.             {
    262.                 reorderable = false,
    263.                 selectionType = SelectionType.Single,
    264.                 showAlternatingRowBackgrounds = AlternatingRowBackground.All,
    265.                 virtualizationMethod = CollectionVirtualizationMethod.FixedHeight,
    266.                 fixedItemHeight = itemHeight
    267.             };
    268.             _listView.makeItem = () => new Label();
    269.             _listView.bindItem = (v, i) =>
    270.             {
    271.                 Type t = ((IReadOnlyList<Type>)_listView.itemsSource)[i];
    272.                 ((Label)v).text = GetFormattedType(t);
    273.             };
    274.             _listView.selectionChanged += (s) =>
    275.             {
    276.                 SetFromSelection(s);
    277.             };
    278.             _listView.itemsChosen += (s) =>
    279.             {
    280.                 SetFromSelection(s);
    281.                 editorWindow.Close();
    282.             };
    283.             _toolbar = new Toolbar();
    284.             _search = new ToolbarSearchField();
    285.             var searchState = new DelayedSearchController(_search);
    286.             searchState.SearchTextChanged += s => { SetFilter(s); };
    287.             _toolbar.Add(_search);
    288.         }
    289.  
    290.         private void SetFromSelection(IEnumerable<object> selection)
    291.         {
    292.             if (_pauseSelectionCheck)
    293.             {
    294.                 return;
    295.             }
    296.             Type type = selection.FirstOrDefault() as Type;
    297.             _field.value = SerializableTypeUtility.WriteToString(type);
    298.         }
    299.  
    300.         private void SetList(List<Type> types)
    301.         {
    302.             _listView.itemsSource = types;
    303.             int index = types.IndexOf(SerializableTypeUtility.ReadFromString(_field.value));
    304.             if (index >= 0)
    305.             {
    306.                 _listView.SetSelection(index);
    307.             }
    308.             else
    309.             {
    310.                 _listView.ClearSelection();
    311.             }
    312.         }
    313.  
    314.         private void SetFilter(string filter)
    315.         {
    316.             _pauseSelectionCheck = true;
    317.             if (string.IsNullOrEmpty(filter))
    318.             {
    319.                 SetList(_types);
    320.             }
    321.             else
    322.             {
    323.                 List<Type> filtered = new();
    324.                 foreach (var type in _types)
    325.                 {
    326.                     if (type.FullName.Contains(filter, StringComparison.InvariantCultureIgnoreCase))
    327.                     {
    328.                         filtered.Add(type);
    329.                     }
    330.                 }
    331.                 SetList(filtered);
    332.             }
    333.             _pauseSelectionCheck = false;
    334.         }
    335.  
    336.         public override Vector2 GetWindowSize()
    337.         {
    338.             float width = Math.Max(200, _field.resolvedStyle.width);
    339.             return new Vector2(width, 500);
    340.         }
    341.  
    342.         public override void OnOpen()
    343.         {
    344.             editorWindow.rootVisualElement.Clear();
    345.             editorWindow.rootVisualElement.Add(_toolbar);
    346.             editorWindow.rootVisualElement.Add(_listView);
    347.             _types = _field.GetFilteredTypes();
    348.             SetList(_types);
    349.             editorWindow.rootVisualElement.RegisterCallback<NavigationCancelEvent>(HandleNavigationCancelEvent);
    350.             editorWindow.rootVisualElement.schedule.Execute(() =>
    351.             {
    352.                 _search.Focus();
    353.             });
    354.         }
    355.  
    356.         public override void OnClose()
    357.         {
    358.             editorWindow.rootVisualElement.UnregisterCallback<NavigationCancelEvent>(HandleNavigationCancelEvent);
    359.         }
    360.  
    361.         private void HandleNavigationCancelEvent(NavigationCancelEvent evt)
    362.         {
    363.             editorWindow.Close();
    364.         }
    365.  
    366.         private class DelayedSearchController : IDisposable
    367.         {
    368.             private const int DelayMs = 300;
    369.  
    370.             public event Action<string> SearchTextChanged;
    371.  
    372.             private readonly VisualElement _search;
    373.             private readonly int _delayMs;
    374.             private string _activeText;
    375.             private readonly IVisualElementScheduledItem _scheduledItem;
    376.             private bool _disposed;
    377.  
    378.             public DelayedSearchController(VisualElement search, int delayMs = DelayMs)
    379.             {
    380.                 _search = search;
    381.                 _delayMs = delayMs;
    382.                 _activeText = "";
    383.                 _scheduledItem = null;
    384.                 search.RegisterCallback<ChangeEvent<string>>(OnSearchChanged);
    385.                 search.RegisterCallback<NavigationSubmitEvent>(OnSearchEnter);
    386.                 _scheduledItem = search.schedule.Execute(UpdateItem);
    387.                 _scheduledItem.Pause();
    388.             }
    389.  
    390.             public void Dispose()
    391.             {
    392.                 if (_disposed)
    393.                 {
    394.                     return;
    395.                 }
    396.                 _disposed = true;
    397.                 _search.UnregisterCallback<ChangeEvent<string>>(OnSearchChanged);
    398.                 _search.UnregisterCallback<NavigationSubmitEvent>(OnSearchEnter);
    399.                 _scheduledItem.Pause();
    400.             }
    401.  
    402.             private void OnSearchEnter(NavigationSubmitEvent evt)
    403.             {
    404.                 if (_disposed)
    405.                 {
    406.                     return;
    407.                 }
    408.                 _scheduledItem?.ExecuteLater(0);
    409.             }
    410.  
    411.             private void OnSearchChanged(ChangeEvent<string> evt)
    412.             {
    413.                 if (_disposed)
    414.                 {
    415.                     return;
    416.                 }
    417.                 _activeText = evt.newValue;
    418.                 if (string.IsNullOrEmpty(_activeText))
    419.                 {
    420.                     _scheduledItem?.ExecuteLater(0);
    421.                 }
    422.                 else
    423.                 {
    424.                     _scheduledItem?.ExecuteLater(_delayMs);
    425.                 }
    426.             }
    427.  
    428.             private void UpdateItem()
    429.             {
    430.                 SearchTextChanged?.Invoke(_activeText);
    431.             }
    432.         }
    433.     }
    434. }
    435. #endif
     
    Last edited: May 15, 2024
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,922
    Why?

    There's likely a better way to satisfy the requirement. Like a factory that creates components based on an enum.

    This would also make the GetComponent lookup easier or unnecessary.
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,391
    I don't really think 'using an enum' is ever a good option for something that potentially needs to scale by a large amount.

    FWIW's for OP, Odin Inspector + Serialiser readily supports serialising and drawing System.Type with a very good drawer for it. Alternatively, using some good old OOP and making your behaviour more pluggable with scriptable objects or SerializeReference might be get you the flexibility you need.
     
  7. Muium

    Muium

    Joined:
    Feb 17, 2021
    Posts:
    7
    I'm using the same unity version.

    I am trying to implement your code, however I don't know where it should go. I've put both the "usage" and "code" parts into separate csharp scripts but there are a few errors in the code. maybe because I copy-pasted it directly, not really knowing what to do with it... I'm not versed in coding for the editor, what do I do with the code to make it work?
     
  8. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    840
    The Tests class in the second file (L118 to L136) is not necessary, so you can remove that. If errors remain, copying the compiler errors from the Console would help.

    I'll just attach the modified file here:

    The UIElements module (another name for UI Toolkit) may need to be enabled, you can do so in the Package Manager:
    https://docs.unity3d.com/6000.0/Documentation/Manual/upm-ui-disable.html
     

    Attached Files:

  9. Muium

    Muium

    Joined:
    Feb 17, 2021
    Posts:
    7
    I have the updated version now and also the module enabled.

    The errors do remain, but they don't show up in the console. I have taken screenshots of them in visual studio instead.
    Screenshot 2024-05-15 213207.png
    This is on line 303. I'm entirely unsure what any of this means, to be honest. I tweaked it a little but couldn't figure it out.

    There is also this:
    Screenshot 2024-05-15 213309.png
    Line 180. There are 8 different errors in this line. I'm not sure what the issue is. I can show the other errors too if needed.

    And finally, my last problem, is that I still have zero idea how either of these files are supposed to be used in the project...
     
  10. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,684
    Unity is on an old release of .NET and C#. It's confused and misidentifying the feature because of that. You want one of these instead.
    Code (csharp):
    1. List<Type> filtered = new List<Type>();
    or
    Code (csharp):
    1. var filtered = new List<Type>();
    Same problem. Static lambdas are a C# 9 feature. Unity doesn't fully support C# 9.
     
    Last edited: May 15, 2024
    spiney199 likes this.
  11. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    840
    SerializableType.cs contains the SerializableType struct and supporting editor UI. The "usage example" snippet I posted earlier is just an example of what might go in a MonoBehaviour.

    Visual Studio should not be reporting errors for that syntax, it's C# 9 and is supported on Unity 6 Preview. Failing to identify LINQ is also a bad sign. Which version of the Visual Studio editor package do you have in Unity's Package Manager / do you have it installed at all? Install the latest version (2.0.22). Also, install the "Game Development with Unity" workload in Visual Studio Installer.
     
    Ryiah likes this.
  12. Spy-Master

    Spy-Master

    Joined:
    Aug 4, 2022
    Posts:
    840
    It's really not. Well, yeah, it's old, but there isn't a problem on Unity's side. That's C# 9 syntax that works on Unity since 2021.2(?). The problem is Visual Studio. To clarify, the exact code works perfectly on my Unity 6 Preview (6000.0.1f1) install, with similar code having been used throughout 2022 releases. If errors are not showing up in the Console, then 99 times out of 100 it's compiled correctly, and you just have a misconfigured IDE like Visual Studio.
     
    Ryiah likes this.
  13. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,684
    Yep. Just verified on my end that it's working. I should have just done that before posting. :p
     
    Spy-Master likes this.
  14. Muium

    Muium

    Joined:
    Feb 17, 2021
    Posts:
    7
    This fixed it, thanks ^^"
    I was using vs 2017, switching to the latest version fixed the problem and everything is working now. Thanks for the help!