Search Unity

Official Workflow Case Study - Show us your pain!

Discussion in 'Editor & General Support' started by willgoldstone, Mar 1, 2019.

  1. kbm

    kbm

    Joined:
    Aug 7, 2014
    Posts:
    84
    For me, by far the biggest pain point is, that everything is soo slow and downright painful to work with.
    I don't see that simple fact discussed enough in this thread, it's always super specific things, and they are all super valid, but: For me, none of this matters when the whole Editor Experience feels like wading through a swamp.

    I have just finished work on a 3+ year project, working daily with Unity for 8+ hours.
    I have become quite numb to it, but literally every single action, every click on every editor tab, every play-mode change, it's all just...laggy & unsatisfying.

    I was interested in Godot and started up their editor recently, it's a completely different experience. It's super responsive, got great "feel", and in general feels so much more mature than the Unity editor. How can this be? I mean, it's obvious that they're doing something better than Unity, but how can it be that a company that's so big can have an Editor experience that's so lacking, in 2021? Try it out for yourself, you will be amazed at the difference in responsiveness - it's like a breath of fresh air.

    And then there are the excruciatingly long compile & assembly reload times, for even the simplest scenes.
    It's just crazy that 2 days ago I installed the lastest version of Unity, loaded the 3D sample project (this alone takes several minutes), and even in this basically empty project, stuff is ultra slow. Compile/reload times take >3 seconds with basically no scripts.

    On my current project it takes me more than 20 seconds to go from a single line of code change -> actually playtesting the thing. I often find myself literally stopping work and browsing Reddit or stuff because my focus just slips. It is literally sucking away my fun of making games, and it's making me sad.

    And yes, I have done all the things & tricks I am supposed to do: Disable domain reload, uninstall all unnecessary packages, done the works. It's just not enough.

    There is all this big talk about runtime performance, and of course this is important, but what about Editor performance?
    This is where we, as devs, spend so much of our time, and it's not good. What is the solution to this?

    I am really disappointed in the current state of Unity, and I feel like we as a community are deserving a better engine.
    I really hope that all the things in the works will come to fruition, DOTS most of all, but I don't know how much longer I can wait for all that.
     
    Last edited: Feb 12, 2021
  2. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,526
    I would love it if the editor UI ran at 60 fps, just like our games do ( if everything goes well )

    One thing I like a lot in Godot is that pressing play creates a new window and its in its own process. if the play-mode crashes the editor is still running.
     
  3. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    @kbm, I can feel your pain as if it were mine. I ditched Unity about a year ago after worked on a project for a couple of years. I couldn't bear the pain anymore. I cried out loud many times but to no avail. What matters the most is to cross the finish line first and I thought I would cross the finish line faster switching to Unreal. Yeah, I was damn right, I'm really happy that I did. I vowed not to piss toward Unity unless they make the Editor performant. Making twice as fast will not make much difference. It needs to speed up at least 5x in order to feel ok and I'm serious.

    The doggy slow Editor is a well-known issue and Unity is pretty much sitting still until the new .NetCore embraces them. They said they are looking at it but I don't believe it until I see it on the roadmap. Even if it's on the roadmap, expect few years of delay. We all know Unity's Soon can mean months to years.

    Instead, they are putting all their efforts into DOTS and there are no end insights. I predicted that DOTS will take at least 2 or 3 years until it matures about a year ago and I admit I'm wrong. It will take another 2 or 3 years from now on. And will it fix the doggy slow Editor? hell no. Do you see why I ditched Unity? UE5 will feature the dynamic scripting language and I'm really excited and it will be a game-changer. UE Editor is very powerful and fast but C++ has slow compile-time and BP can become a spaghetti very fast. The new scripting language will remedy and Unity programmers will feel at home. Imagine instant compilation at runtime on the fly without even restarting the game. There are tons of other benefits but everyone has different opinions and I don't want to argue which is the better engine. If you are making a clicker game, Unity is still a good choice.

    Anyway, Unity will need to migrate to .NetCore right now instead of after few years if they have the right mindset. With 1/10th efforts they put into DOTS, we probably have some working version now, and it will help all Unity users instead of the very few DOTS users.

    "Performance by Default" as they say, but what we really need is "Performant Editor by Default" Put DOTS on the separate branch so that it doesn't drag us all down. And likewise, put .NET Editor on a separate branch so that existing users can keep using the current Editor while adventurous users can try DOTS or .NET.

    Unity, now a public company valued $10+ B, will they care about users? Unfortunately, we are a second citizen after the investors and remember, Unity doesn't profit by developers being successful but benefit from having as many new users who are needing to buy Assets for the bare-bone Editor. I don't think they care much until the bottom falls out.

    Unity has a very grim future unless they act now but watching them over a decade, Unity will be Unity; Stubborn, Lazy, and Incompetent. I'm being negative but I'm not being sarcastic. Hope it rings some bells to some.

    Cheers!
     
    IOU_RAY, PeterB, sand_lantern and 3 others like this.
  4. davvjs

    davvjs

    Joined:
    Nov 13, 2016
    Posts:
    11
    I agree with all of you guys! I made this thread a while ago but seems like it just got lost among all the help/support posts. Would love to hear your opinions on it (so it gets a bump up).

    I'm not sure how we can stress this appropriately so it reaches management. But IMO editor sluggishness is the major concern the unity team should prioritize above everything atm. It affects productivity of every single game studio using their product.

    https://forum.unity.com/threads/all...ke-a-lightweight-editor.1029856/#post-6668545
     
    IOU_RAY, PeterB, sand_lantern and 2 others like this.
  5. JonathanCel

    JonathanCel

    Joined:
    Feb 17, 2021
    Posts:
    22
    Right now one of my bugbears is when working one someone else's project...

    A button animates from off screen into the visible area.
    What animates that button's position?

    Somewhere in the hierarchy of 200k objects, there's one script with a link to this button's GameObject.

    But how do I find it?

    I wrote something that iterates through everything in the scene but
    A: it crashes on OSX and I don't generally have the time to debug that.
    B: it's so slow and prone to following the wrong path to your object

    E.g. if NGUI has cached a bunch of transforms, they'll definitely spam your results.
    Circular references are no fun either.

    Try googling this btw.
    I haven't yet found the special sauce for "how find which script linked to gameobject"

    It's that special kinda "worse than useless" where it suggests loads of answers it *thinks* you're after and actually obfuscates any potential useful ones.



    I can't really understand why nobody else is having this issue?
    - E.g. do most people work only on their own projects?
    - Does everyone else just suffer in silence? (definitely very frustrating!)

    Thanks.
     

    Attached Files:

  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    So to do that, you'd open the scene file, find the fileID of the object in question (and it's transform), and see what references that.

    You could also write the script you're talking about, using SerializedObject/Property to iterate all serialized references. I've got a script that does that to update all references to an object in a prefab to a different object.

    But, yeah, it's obvious to anyone that's done anything in Unity that one of the most missing features is "what's referencing this thing". That's a complaint we've had for half a decade.
     
    awesomedata likes this.
  7. JonathanCel

    JonathanCel

    Joined:
    Feb 17, 2021
    Posts:
    22
    Cheers Baste; glad to know it's not just me!
    I guess there's no reason we can't have a poke through the current scene's file programmatically.
    I'll have a look at that!
     
  8. JonathanCel

    JonathanCel

    Joined:
    Feb 17, 2021
    Posts:
    22
    Hi, there are a few slowdowns that are getting quite annoying as part of my workflow:

    • When switching between Android and iOS versions of the app (shared/symlinked assets folder), every time you switch, you must go into the preferences and regenerate the project files. This could be automated or easier to reach.
    • The preferences window doesn't seem to rememeber its size either, so more faff.
    • Visual Studio is just slow here and prone to errors as it rebuilds. + Breaks every few months.
    • VS Code has some serious OmniSharp issues and dies on about 80% of our projects for me. (I have to regen the project files every hour or two)
    • Visual Studio for Mac is... they tried really hard, but you know? Even the splash window stops respondng to clicks.
    • Rider doesn't fix issues for years at a time (I've had 10+ tickets open, including infinite trial exploits) - not an option.
    All of this faffing about adds up, and I'm worn out with the constant reinstalling IDEs to fix intellisense, then setting them all up again.
    Can we just have the project files regenerated as a matter of course?
    Or an easier button to reach?

    Right now for example VS has just decided that Unity.* doesn't exist. It was fine 5 mins ago!
    Then again, last night I didn't even close Unity and ... same issue.

    Big love to VSCode, that would be my daily driver in a hearbeat if Omnisharp would behave!
    But I still haven't settled on a Unity IDE in almost 8 years.
    Doesn't help that the support tools are really derpy and rushed too.
    It's an awful state to be in after this long, isn't it?

    When building from Unity 19.4 LTS to Android, it's too hands-on - especially on mac.
    E.g. I'd quite like to just build and run, but I need newer Gradle.
    • So I export to an android studio project.. sit & wait
    • And every time, I have to use the mouse on a mac to find the right folder...
    • Then wait a bit while the window jams up with the pinwheel
    • And build settings window draws over everything else on the other monitor for a few seconds, unresponsive
    • So at least 2 monitors are out of service for a couple of seconds
    • Then I have to open the project...
    • And wait a sec for it to process...
    • Then I have to switch the debug/release build variant every time...
    • (separate project folders for separate release candidates)
    • And upgrade the proj gradle to v6.8ish every time to avoid issues with the new XML tags...
    • And every subequent export means hitting the sync button, waiting... upgrade gradle again, waiting...
    • (I know there are options to use local gradle, but that doesn't seem to work and the setting is per project).
    • And then every time you hit play, it re-uploads the app... more waiting
    • Lol did you remember to enter the keystore password!
    • And then logcat's button moves between launches... and I missed the launch of the app!

    There's limited information about the VS build system + trying to build without exporting is hacky and requires finding a specific version. And that's if you find the sweet spot.
    • <= gradle 5.1 complains about parallel execution
    • >= gradle 5.1.1 complains about deprecated features

    The gaps are too small to do anything between, but it all adds up and you lose 5 mins easy.
    This being time I could've spent on a secondary task, but I'm sat here minding this automatable process.


    Then there's dealing with android plugins - that's a huge mess at the moment.
    Every few versions something breaks.
    • .androidlib wasn't properly handled for a while
    • stuff like facebook audience network doesn't resolve properly
    • Google's resolver still isn't very stable either.
    • plus resolver + Firebase spams constantly! Every update, every change, every compile...
    • switching from standalone Resolver to packagemanager Resolver is very crash prone too
    • (requiring the code to be valid in its before *and* after state to actually work)
    • and every so often Google's teams will release plugins based on mismatched dependencies
    • (and honestly, it happened - they'llask you, the user to contact the other team for a matching release!)
    • and then you go to build and the dexed version of multidex will conflict with the undexed version (same version!)
    • and you must either alter your manifest to remove new unwanted permissions
    • (or unzip the plugins and edit their manifests (or users won't get automatic updates)
    • then faff about with aapt to ensure your permissions are correct
    • and when you want to replace one plugin, it's generally best to remove them *ALL* and start again
    • (firebase analtyics, crashlytics, etc, admob, admob mediation, admob mediation networks, billing, etc)
    • and when you're importing them you have to remember not to accidentally import the resolver, or it'll import mismatched versions of itsself and cause all sorts of conflicts.
    • and if you do... start over.
    • oh, and if you left VSCode open, the package manager will flake out with errors.
    • And your AndroidManifest.xml is likely broken at this point
    • So you manually remove one copy of multidex to fix the final build
    • Then you have to fill in all the details (ad ids, google play services stuff, etc)
    • And as soon as you hit save on your GPGS settings, it tries to resolve again (usually fails, but always breaks your config)
    • So delete resolved libraries.. force resolve, deelete multidex one more time.
    • And you guys are like "oh no, we don't support multidex - use proguard"
    • But sometimes you don't have a choice :(
    • And then of course you must deal with new versions of plugins deciding to do callbacks off the main thread
    • Or new versions sending e.g. OnAdvertLoaded to *all* GameObjects instead of the target one, etc.
    • And when you do go to build, you're missing licenses from the SDKs that the hub installed.
    • So you have to install those via the SDK manager and copy the license file over anyway...
    And I haven't measured, but it feels like all of the extra compile events slows things down massively every time you change a single character in a script.

    Despite Apple's walled garden, iOS isn't as bad as you'd expect...
    • Got to constantly remove ARM64 from the requirements if ARMV7 is present
    • Still got to constantly remove the Metal requirement when Auto Graphics API is used.
    • As of 19.4 you must patch AppController.mm but I patched that at source.
    It's far more streamlined in general


    Suffice to say bits like this make working on Android pretty miserable with Unity nowadays.
    A fully 'native' solution seems long overdue, and would change everything.


    And some miscellaneous stuff:

    • Now the console panel has a minimum width due to all the non-optional extra buttons, or you lose the Log/Warn/Icon logos on the right, so I lose some space/time.

    • And as I've mentioned, you can't really find references from a script to an object in the editor. Working on someone else's project? Go through *everything* looking for references. There's zero flow.

    • And every debugger but Visual Studio seems to randomly crash and take Unity with it, so I find myself spamming logs instead. (Windows/Mac/VSCode/Visual Studio For Mac/Rider - i have just given up caring about the debugger for the most part.)

    • And why's there no way to peek at private script fields in the editor inspector? (Third party solutions sadly aren't up to snuff and add considerable bulk/theme issues)

    • When setting up script execution orders, why can't I drag a component from the Inspector window into the list?Otherwise I have huge chunks of (often obfuscated) namespaces to filter through, tens of times in the single, tall context menu.

    • Why doesn't the script execution order window scroll up when I'm dragging an item near the borders? Or highlight the last one I moved?

    • Why is the build settings window not a dockable panel yet?
    (which one belongs to which instance of unity?)
    You know what would be really nice? Push a combo and a second set of utility panels scroll down from the top.
    Package manager, Build Settings, Project settings, Services, Occlusion Culling.
    But since e.g. Package manager induces a ton of lag during compilation, etc...they're not drawn in the background until the windows are actually drawn.

    • Why does the package manager not come with a warning that leaving it open makes things unacceptably slow! New users won't necessarily realise that Unity isn't just slow.

    • Why is *some* data lost when unity crashes and others not. It's a pain trying to work out how far back to go, and it looks like you're just burning time on your timesheet. When you save, is it serialized or not? What makes it to disc immediately?

    • Why do I have deal with conflicting key combos so frequently? And why is it that after applying settings, I often have to go back and do it again.

    • Since ~Unity 5 or so, the console will randomly decide it's not scrolling automatically to the final line.

    • When there's an error, and I double click on it in the console panel, why not take me to the nearest available source *in my project* vs giving me the library metadata or decompilation. We can still click the hyperlinks to the libraries, but that's not what you're after 99% of the time.

    • Why is gradle?

    • Why when I open the sprite/texture selection window is the textual part not focused?

    • Why does the component selection window not show the path to the compoenent? "GUI Root/Main Panel/Toolbar/Left/OptionsButton" There might be a few "options" buttons, I'm quicker risking going through the hierarchy and hoping I don't misclick.

    • Why is it when I click on the Build Settings window (mac) it happily takes focus immediately. But for the thing behind it, then it's 2 clicks? It's forever in the way. Incredibly frustrating.

    • Why when a build starts does no amount of clicking and dragging the window behind it fix things?. If you're debugging some build issues that's part of a screen out of commision for the duration.

    • I'm in VSCode's main window - focused over Unity... I right click on a file in Unity's project browser, go down to delete. Popup window appears asking if I want to delete, behind VSCode. VSCode still has focus... Why's unity letting me do this? Why so many focus issues?


    None of this is gamebreaking, but it feels like a lot of the smaller points have been left behind in pursuit of bigger goals... and so I'm left wasting time repeating myself on lots of pointless little tasks and wrestling with the editor.
    I guess what I'm saying is work flow has become work staccato.
     
    PutridEx and kbm like this.
  9. SpacePrez

    SpacePrez

    Joined:
    Aug 19, 2019
    Posts:
    7
    Some very basic requests

    - Selecting a folder shouldn't cause Inspector to show the folder. There is NOTHING inspector can do or show for folders. Selecting a folder shouldn't replace the currently selected GameObject. This makes setting references a massive pain. You have to constantly lock and unlock the current inspector object to prevent it from switching so you can grab a reference to a prefab in another folder.

    Storing things in separate folders is good practice, this shouldn't be so painful.

    - There needs to be a very simple way to apply the current play state of an object to its saved world state. You should be able to click any component and say "commit current values" to apply them to the base scene, same as how you can apply override changes to a prefab.

    This happens INCREDIBLY often: Hit play, move object, take picture of the transform co-ordinates, hit stop, object reverts position, manually use the picture to replicate the moved values of the transform.

    There is no reason the Editor shouldn't make this a one-click solution and doing so would save countless hours of devtime.

    There is so much pain in the Unity editor for very common tasks. UI needs lots of work. Game engine is great. Please improve usability.
     
  10. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    You can right-click the transform in the inspector and select "copy component", and later "paste component". That works between play mode and edit mode, and for any component type.
     
  11. Can you elaborate (with screenshot maybe?) what you're talking about? Because my Unity doesn't do such a thing. I click on a game object or asset, the inspector shows its data. I click on a folder in the project window, the inspector stays the same. It does not show nothingness about the folder.
     
  12. JonathanCel

    JonathanCel

    Joined:
    Feb 17, 2021
    Posts:
    22
    If you're in dual column mode then selecting something in the 'directories panel' doesn't affect your selection but selecting something in the 'files' panel does (whether that's a file or a folder).

    In single column mode, selecting anything changes your active selection.
    Very painful for setting things up.

    It's quite often worth making a little editor extension window to pin things to.

    E.g. to remember the last 3-4 things you've selected, otherwise it's quite easy to get frustrated with this stuff.
    Especially if you're e.g. dragging a bunch of things from different sources onto one specific manager but:
    - got to go through a large hierarchy looking for them
    - got to verify which parent object they're on
    - got to use find and select through some object.

    Having a "back" or "previous selection" button like in your browser definitely helps.
     
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    We did use the ability to select folders to create custom editors for specific folders, which created a grid in the inspector for all the objects in the folder.

    So that's a use-case where it's useful to be able to select folders.

    But it required some pretty nasty hacks, so it's probably not supposed to be supported. And selecting the things by mistake is always annoying as hell.
     
    awesomedata likes this.
  14. Ah, I see. I never use that.

    BTW, folder selection is quite important in the "explorer" part of the project window, because you only can select folders for Addressables through selecting the folder there.
     
  15. DearUnityPleaseAddSerializableDictionaries

    DearUnityPleaseAddSerializableDictionaries

    Joined:
    Sep 12, 2014
    Posts:
    135
    I have been patiently waiting for years to have dictionary in the inspector. The order of values don't matter, so maybe it can be fake serializable?

    The chances I want dictionary in inspector is so common, it happens in every game I make and in several cases of every game. I need this dictionary in inspector feature for at least several scripts or game objects, for every game.

    Even have to put this on my signature.
     
    Last edited: Apr 2, 2021
  16. sand_lantern

    sand_lantern

    Joined:
    Sep 15, 2017
    Posts:
    210
    I found a script somewhere a while ago someone made to fake it, it sometimes doesn't properly mark objects as dirty or some dumb things like that. I'm not sure how efficient it is, but this is what I use:

    Code (CSharp):
    1. using System;
    2. using System.Linq;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.Diagnostics;
    6. using UnityEngine;
    7. #if UNITY_EDITOR
    8. using UnityEditor;
    9. #endif
    10. using UnityObject = UnityEngine.Object;
    11. using Debug = UnityEngine.Debug;
    12.  
    13. namespace Tools
    14. {
    15.     [Serializable, DebuggerDisplay ("Count = {Count}")]
    16.     public class SerializableDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
    17.         [SerializeField, HideInInspector] int[] _Buckets;
    18.         [SerializeField, HideInInspector] int[] _HashCodes;
    19.         [SerializeField, HideInInspector] int[] _Next;
    20.         [SerializeField, HideInInspector] int _Count;
    21.         [SerializeField, HideInInspector] int _Version;
    22.         [SerializeField, HideInInspector] int _FreeList;
    23.         [SerializeField, HideInInspector] int _FreeCount;
    24.         [SerializeField, HideInInspector] TKey[] _Keys;
    25.         [SerializeField, HideInInspector] TValue[] _Values;
    26.  
    27.         readonly IEqualityComparer<TKey> _Comparer;
    28.  
    29.         // Mainly for debugging purposes - to get the key-value pairs display
    30.         public Dictionary<TKey, TValue> AsDictionary {
    31.             get { return new Dictionary<TKey, TValue> (this); }
    32.         }
    33.  
    34.         public int Count {
    35.             get { return _Count - _FreeCount; }
    36.         }
    37.  
    38.         //public TValue this[TKey key, TValue defaultValue] {
    39.         //    get {
    40.         //        int index = FindIndex (key);
    41.         //        if (index >= 0)
    42.         //            return _Values[index];
    43.         //        return defaultValue;
    44.         //    }
    45.         //}
    46.  
    47.         public TValue this[TKey key] {
    48.             get {
    49.                 int index = FindIndex (key);
    50.                 if (index >= 0)
    51.                     return _Values[index];
    52.                 throw new KeyNotFoundException (key.ToString ());
    53.             }
    54.  
    55.             set { Insert (key, value, false); }
    56.         }
    57.  
    58.         public SerializableDictionary()
    59.             : this (0, null) {
    60.         }
    61.  
    62.         public SerializableDictionary(int capacity)
    63.             : this (capacity, null) {
    64.         }
    65.  
    66.         public SerializableDictionary(IEqualityComparer<TKey> comparer)
    67.             : this (0, comparer) {
    68.         }
    69.  
    70.         public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) {
    71.             if (capacity < 0)
    72.                 throw new ArgumentOutOfRangeException ("capacity");
    73.  
    74.             Initialize (capacity);
    75.  
    76.             _Comparer = (comparer ?? EqualityComparer<TKey>.Default);
    77.         }
    78.  
    79.         public SerializableDictionary(IDictionary<TKey, TValue> dictionary)
    80.             : this (dictionary, null) {
    81.         }
    82.  
    83.         public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
    84.             : this ((dictionary != null) ? dictionary.Count : 0, comparer) {
    85.             if (dictionary == null)
    86.                 throw new ArgumentNullException ("dictionary");
    87.  
    88.             foreach (KeyValuePair<TKey, TValue> current in dictionary)
    89.                 Add (current.Key, current.Value);
    90.         }
    91.  
    92.         public bool ContainsValue(TValue value) {
    93.             if (value == null) {
    94.                 for (int i = 0; i < _Count; i++) {
    95.                     if (_HashCodes[i] >= 0 && _Values[i] == null)
    96.                         return true;
    97.                 }
    98.             } else {
    99.                 var defaultComparer = EqualityComparer<TValue>.Default;
    100.                 for (int i = 0; i < _Count; i++) {
    101.                     if (_HashCodes[i] >= 0 && defaultComparer.Equals (_Values[i], value))
    102.                         return true;
    103.                 }
    104.             }
    105.             return false;
    106.         }
    107.  
    108.         public bool ContainsKey(TKey key) {
    109.             return FindIndex (key) >= 0;
    110.         }
    111.  
    112.         public void Clear() {
    113.             if (_Count <= 0)
    114.                 return;
    115.  
    116.             for (int i = 0; i < _Buckets.Length; i++)
    117.                 _Buckets[i] = -1;
    118.  
    119.             Array.Clear (_Keys, 0, _Count);
    120.             Array.Clear (_Values, 0, _Count);
    121.             Array.Clear (_HashCodes, 0, _Count);
    122.             Array.Clear (_Next, 0, _Count);
    123.  
    124.             _FreeList = -1;
    125.             _Count = 0;
    126.             _FreeCount = 0;
    127.             _Version++;
    128.         }
    129.  
    130.         public void Add(TKey key, TValue value) {
    131.             Insert (key, value, true);
    132.         }
    133.  
    134.         private void Resize(int newSize, bool forceNewHashCodes) {
    135.             int[] bucketsCopy = new int[newSize];
    136.             for (int i = 0; i < bucketsCopy.Length; i++)
    137.                 bucketsCopy[i] = -1;
    138.  
    139.             var keysCopy = new TKey[newSize];
    140.             var valuesCopy = new TValue[newSize];
    141.             var hashCodesCopy = new int[newSize];
    142.             var nextCopy = new int[newSize];
    143.  
    144.             Array.Copy (_Values, 0, valuesCopy, 0, _Count);
    145.             Array.Copy (_Keys, 0, keysCopy, 0, _Count);
    146.             Array.Copy (_HashCodes, 0, hashCodesCopy, 0, _Count);
    147.             Array.Copy (_Next, 0, nextCopy, 0, _Count);
    148.  
    149.             if (forceNewHashCodes) {
    150.                 for (int i = 0; i < _Count; i++) {
    151.                     if (hashCodesCopy[i] != -1)
    152.                         hashCodesCopy[i] = (_Comparer.GetHashCode (keysCopy[i]) & 2147483647);
    153.                 }
    154.             }
    155.  
    156.             for (int i = 0; i < _Count; i++) {
    157.                 int index = hashCodesCopy[i] % newSize;
    158.                 nextCopy[i] = bucketsCopy[index];
    159.                 bucketsCopy[index] = i;
    160.             }
    161.  
    162.             _Buckets = bucketsCopy;
    163.             _Keys = keysCopy;
    164.             _Values = valuesCopy;
    165.             _HashCodes = hashCodesCopy;
    166.             _Next = nextCopy;
    167.         }
    168.  
    169.         private void Resize() {
    170.             Resize (PrimeHelper.ExpandPrime (_Count), false);
    171.         }
    172.  
    173.         public bool Remove(TKey key) {
    174.             if (key == null)
    175.                 throw new ArgumentNullException ("key");
    176.  
    177.             int hash = _Comparer.GetHashCode (key) & 2147483647;
    178.             int index = hash % _Buckets.Length;
    179.             int num = -1;
    180.             for (int i = _Buckets[index]; i >= 0; i = _Next[i]) {
    181.                 if (_HashCodes[i] == hash && _Comparer.Equals (_Keys[i], key)) {
    182.                     if (num < 0)
    183.                         _Buckets[index] = _Next[i];
    184.                     else
    185.                         _Next[num] = _Next[i];
    186.  
    187.                     _HashCodes[i] = -1;
    188.                     _Next[i] = _FreeList;
    189.                     _Keys[i] = default (TKey);
    190.                     _Values[i] = default (TValue);
    191.                     _FreeList = i;
    192.                     _FreeCount++;
    193.                     _Version++;
    194.                     return true;
    195.                 }
    196.                 num = i;
    197.             }
    198.             return false;
    199.         }
    200.  
    201.         private void Insert(TKey key, TValue value, bool add) {
    202.             if (key == null)
    203.                 throw new ArgumentNullException ("key");
    204.  
    205.             if (_Buckets == null)
    206.                 Initialize (0);
    207.  
    208.             int hash = _Comparer.GetHashCode (key) & 2147483647;
    209.             int index = hash % _Buckets.Length;
    210.             int num1 = 0;
    211.             for (int i = _Buckets[index]; i >= 0; i = _Next[i]) {
    212.                 if (_HashCodes[i] == hash && _Comparer.Equals (_Keys[i], key)) {
    213.                     if (add)
    214.                         throw new ArgumentException ("Key already exists: " + key);
    215.  
    216.                     _Values[i] = value;
    217.                     _Version++;
    218.                     return;
    219.                 }
    220.                 num1++;
    221.             }
    222.             int num2;
    223.             if (_FreeCount > 0) {
    224.                 num2 = _FreeList;
    225.                 _FreeList = _Next[num2];
    226.                 _FreeCount--;
    227.             } else {
    228.                 if (_Count == _Keys.Length) {
    229.                     Resize ();
    230.                     index = hash % _Buckets.Length;
    231.                 }
    232.                 num2 = _Count;
    233.                 _Count++;
    234.             }
    235.             _HashCodes[num2] = hash;
    236.             _Next[num2] = _Buckets[index];
    237.             _Keys[num2] = key;
    238.             _Values[num2] = value;
    239.             _Buckets[index] = num2;
    240.             _Version++;
    241.  
    242.             //if (num3 > 100 && HashHelpers.IsWellKnownEqualityComparer(comparer))
    243.             //{
    244.             //    comparer = (IEqualityComparer<TKey>)HashHelpers.GetRandomizedEqualityComparer(comparer);
    245.             //    Resize(entries.Length, true);
    246.             //}
    247.         }
    248.  
    249.         private void Initialize(int capacity) {
    250.             int prime = PrimeHelper.GetPrime (capacity);
    251.  
    252.             _Buckets = new int[prime];
    253.             for (int i = 0; i < _Buckets.Length; i++)
    254.                 _Buckets[i] = -1;
    255.  
    256.             _Keys = new TKey[prime];
    257.             _Values = new TValue[prime];
    258.             _HashCodes = new int[prime];
    259.             _Next = new int[prime];
    260.  
    261.             _FreeList = -1;
    262.         }
    263.  
    264.         private int FindIndex(TKey key) {
    265.             if (key == null)
    266.                 throw new ArgumentNullException ("key");
    267.  
    268.             if (_Buckets != null) {
    269.                 int hash = _Comparer.GetHashCode (key) & 2147483647;
    270.                 for (int i = _Buckets[hash % _Buckets.Length]; i >= 0; i = _Next[i]) {
    271.                     if (_HashCodes[i] == hash && _Comparer.Equals (_Keys[i], key))
    272.                         return i;
    273.                 }
    274.             }
    275.             return -1;
    276.         }
    277.  
    278.         public bool TryGetValue(TKey key, out TValue value) {
    279.             int index = FindIndex (key);
    280.             if (index >= 0) {
    281.                 value = _Values[index];
    282.                 return true;
    283.             }
    284.             value = default (TValue);
    285.             return false;
    286.         }
    287.  
    288.         private static class PrimeHelper {
    289.             public static readonly int[] Primes = new int[]
    290.             {
    291.             3,
    292.             7,
    293.             11,
    294.             17,
    295.             23,
    296.             29,
    297.             37,
    298.             47,
    299.             59,
    300.             71,
    301.             89,
    302.             107,
    303.             131,
    304.             163,
    305.             197,
    306.             239,
    307.             293,
    308.             353,
    309.             431,
    310.             521,
    311.             631,
    312.             761,
    313.             919,
    314.             1103,
    315.             1327,
    316.             1597,
    317.             1931,
    318.             2333,
    319.             2801,
    320.             3371,
    321.             4049,
    322.             4861,
    323.             5839,
    324.             7013,
    325.             8419,
    326.             10103,
    327.             12143,
    328.             14591,
    329.             17519,
    330.             21023,
    331.             25229,
    332.             30293,
    333.             36353,
    334.             43627,
    335.             52361,
    336.             62851,
    337.             75431,
    338.             90523,
    339.             108631,
    340.             130363,
    341.             156437,
    342.             187751,
    343.             225307,
    344.             270371,
    345.             324449,
    346.             389357,
    347.             467237,
    348.             560689,
    349.             672827,
    350.             807403,
    351.             968897,
    352.             1162687,
    353.             1395263,
    354.             1674319,
    355.             2009191,
    356.             2411033,
    357.             2893249,
    358.             3471899,
    359.             4166287,
    360.             4999559,
    361.             5999471,
    362.             7199369
    363.             };
    364.  
    365.             public static bool IsPrime(int candidate) {
    366.                 if ((candidate & 1) != 0) {
    367.                     int num = (int) Math.Sqrt ((double) candidate);
    368.                     for (int i = 3; i <= num; i += 2) {
    369.                         if (candidate % i == 0) {
    370.                             return false;
    371.                         }
    372.                     }
    373.                     return true;
    374.                 }
    375.                 return candidate == 2;
    376.             }
    377.  
    378.             public static int GetPrime(int min) {
    379.                 if (min < 0)
    380.                     throw new ArgumentException ("min < 0");
    381.  
    382.                 for (int i = 0; i < PrimeHelper.Primes.Length; i++) {
    383.                     int prime = PrimeHelper.Primes[i];
    384.                     if (prime >= min)
    385.                         return prime;
    386.                 }
    387.                 for (int i = min | 1; i < 2147483647; i += 2) {
    388.                     if (PrimeHelper.IsPrime (i) && (i - 1) % 101 != 0)
    389.                         return i;
    390.                 }
    391.                 return min;
    392.             }
    393.  
    394.             public static int ExpandPrime(int oldSize) {
    395.                 int num = 2 * oldSize;
    396.                 if (num > 2146435069 && 2146435069 > oldSize) {
    397.                     return 2146435069;
    398.                 }
    399.                 return PrimeHelper.GetPrime (num);
    400.             }
    401.         }
    402.  
    403.         public ICollection<TKey> Keys {
    404.             get { return _Keys.Take (Count).ToArray (); }
    405.         }
    406.  
    407.         public ICollection<TValue> Values {
    408.             get { return _Values.Take (Count).ToArray (); }
    409.         }
    410.  
    411.         public void Add(KeyValuePair<TKey, TValue> item) {
    412.             Add (item.Key, item.Value);
    413.         }
    414.  
    415.         public bool Contains(KeyValuePair<TKey, TValue> item) {
    416.             int index = FindIndex (item.Key);
    417.             return index >= 0 &&
    418.                 EqualityComparer<TValue>.Default.Equals (_Values[index], item.Value);
    419.         }
    420.  
    421.         public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index) {
    422.             if (array == null)
    423.                 throw new ArgumentNullException ("array");
    424.  
    425.             if (index < 0 || index > array.Length)
    426.                 throw new ArgumentOutOfRangeException (string.Format ("index = {0} array.Length = {1}", index, array.Length));
    427.  
    428.             if (array.Length - index < Count)
    429.                 throw new ArgumentException (string.Format ("The number of elements in the dictionary ({0}) is greater than the available space from index to the end of the destination array {1}.", Count, array.Length));
    430.  
    431.             for (int i = 0; i < _Count; i++) {
    432.                 if (_HashCodes[i] >= 0)
    433.                     array[index++] = new KeyValuePair<TKey, TValue> (_Keys[i], _Values[i]);
    434.             }
    435.         }
    436.  
    437.         public bool IsReadOnly {
    438.             get { return false; }
    439.         }
    440.  
    441.         public bool Remove(KeyValuePair<TKey, TValue> item) {
    442.             return Remove (item.Key);
    443.         }
    444.  
    445.         public Enumerator GetEnumerator() {
    446.             return new Enumerator (this);
    447.         }
    448.  
    449.         IEnumerator IEnumerable.GetEnumerator() {
    450.             return GetEnumerator ();
    451.         }
    452.  
    453.         IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() {
    454.             return GetEnumerator ();
    455.         }
    456.  
    457.         public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> {
    458.             private readonly SerializableDictionary<TKey, TValue> _Dictionary;
    459.             private int _Version;
    460.             private int _Index;
    461.             private KeyValuePair<TKey, TValue> _Current;
    462.  
    463.             public KeyValuePair<TKey, TValue> Current {
    464.                 get { return _Current; }
    465.             }
    466.  
    467.             internal Enumerator(SerializableDictionary<TKey, TValue> dictionary) {
    468.                 _Dictionary = dictionary;
    469.                 _Version = dictionary._Version;
    470.                 _Current = default (KeyValuePair<TKey, TValue>);
    471.                 _Index = 0;
    472.             }
    473.  
    474.             public bool MoveNext() {
    475.                 if (_Version != _Dictionary._Version)
    476.                     throw new InvalidOperationException (string.Format ("Enumerator version {0} != Dictionary version {1}", _Version, _Dictionary._Version));
    477.  
    478.                 while (_Index < _Dictionary._Count) {
    479.                     if (_Dictionary._HashCodes[_Index] >= 0) {
    480.                         _Current = new KeyValuePair<TKey, TValue> (_Dictionary._Keys[_Index], _Dictionary._Values[_Index]);
    481.                         _Index++;
    482.                         return true;
    483.                     }
    484.                     _Index++;
    485.                 }
    486.  
    487.                 _Index = _Dictionary._Count + 1;
    488.                 _Current = default (KeyValuePair<TKey, TValue>);
    489.                 return false;
    490.             }
    491.  
    492.             void IEnumerator.Reset() {
    493.                 if (_Version != _Dictionary._Version)
    494.                     throw new InvalidOperationException (string.Format ("Enumerator version {0} != Dictionary version {1}", _Version, _Dictionary._Version));
    495.  
    496.                 _Index = 0;
    497.                 _Current = default (KeyValuePair<TKey, TValue>);
    498.             }
    499.  
    500.             object IEnumerator.Current {
    501.                 get { return Current; }
    502.             }
    503.  
    504.             public void Dispose() {
    505.             }
    506.         }
    507.     }
    508.    
    509. #if UNITY_EDITOR
    510.     public abstract class DictionaryDrawer<TK, TV> : PropertyDrawer {
    511.         private SerializableDictionary<TK, TV> _Dictionary;
    512.         private bool _Foldout;
    513.         private const float kButtonWidth = 18f;
    514.  
    515.         public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
    516.             CheckInitialize (property, label);
    517.             if (_Foldout)
    518.                 return (_Dictionary.Count + 1) * 17f;
    519.             return 17f;
    520.         }
    521.  
    522.         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    523.             CheckInitialize (property, label);
    524.  
    525.             position.height = 17f;
    526.  
    527.             var foldoutRect = position;
    528.             foldoutRect.width -= 2 * kButtonWidth;
    529.             EditorGUI.BeginChangeCheck ();
    530.             _Foldout = EditorGUI.Foldout (foldoutRect, _Foldout, label, true);
    531.             if (EditorGUI.EndChangeCheck ())
    532.                 EditorPrefs.SetBool (label.text, _Foldout);
    533.  
    534.             var buttonRect = position;
    535.             buttonRect.x = position.width - kButtonWidth + position.x;
    536.             buttonRect.width = kButtonWidth + 2;
    537.  
    538.             if (GUI.Button (buttonRect, new GUIContent ("+", "Add item"), EditorStyles.miniButton))
    539.             {
    540.                 AddNewItem ();
    541.                 EditorUtility.SetDirty (property.serializedObject.targetObject);
    542.             }
    543.  
    544.             buttonRect.x -= kButtonWidth;
    545.  
    546.             if (GUI.Button (buttonRect, new GUIContent ("X", "Clear dictionary"), EditorStyles.miniButtonRight))
    547.             {
    548.                 ClearDictionary ();
    549.                 EditorUtility.SetDirty (property.serializedObject.targetObject);
    550.             }
    551.  
    552.             if (!_Foldout)
    553.                 return;
    554.  
    555.             foreach (var item in _Dictionary) {
    556.                 var key = item.Key;
    557.                 var value = item.Value;
    558.  
    559.                 position.y += 17f;
    560.  
    561.                 var keyRect = position;
    562.                 keyRect.width /= 2;
    563.                 keyRect.width -= 4;
    564.                 EditorGUI.BeginChangeCheck ();
    565.                 var newKey = DoField (keyRect, typeof (TK), key);
    566.                 if (EditorGUI.EndChangeCheck ()) {
    567.                     try {
    568.                         _Dictionary.Remove (key);
    569.                         _Dictionary.Add (newKey, value);
    570.                     } catch (Exception e) {
    571.                         Debug.Log (e.Message);
    572.                     }
    573.                     EditorUtility.SetDirty (property.serializedObject.targetObject);
    574.                     break;
    575.                 }
    576.  
    577.                 var valueRect = position;
    578.                 valueRect.x = position.width / 2 + 15;
    579.                 valueRect.width = keyRect.width - kButtonWidth;
    580.                 EditorGUI.BeginChangeCheck ();
    581.                 value = DoField (valueRect, typeof (TV), value);
    582.                 if (EditorGUI.EndChangeCheck ())
    583.                 {
    584.                     _Dictionary[key] = value;
    585.                     EditorUtility.SetDirty (property.serializedObject.targetObject);
    586.                     break;
    587.                 }
    588.  
    589.                 var removeRect = valueRect;
    590.                 removeRect.x = valueRect.xMax + 2;
    591.                 removeRect.width = kButtonWidth;
    592.                 if (GUI.Button (removeRect, new GUIContent ("x", "Remove item"), EditorStyles.miniButtonRight))
    593.                 {
    594.                     RemoveItem (key);
    595.                     EditorUtility.SetDirty (property.serializedObject.targetObject);
    596.                     break;
    597.                 }
    598.             }
    599.         }
    600.  
    601.         private void RemoveItem(TK key) {
    602.             _Dictionary.Remove (key);
    603.         }
    604.  
    605.         private void CheckInitialize(SerializedProperty property, GUIContent label) {
    606.             if (_Dictionary == null) {
    607.                 var target = property.serializedObject.targetObject;
    608.                 _Dictionary = fieldInfo.GetValue (target) as SerializableDictionary<TK, TV>;
    609.                 if (_Dictionary == null) {
    610.                     _Dictionary = new SerializableDictionary<TK, TV> ();
    611.                     fieldInfo.SetValue (target, _Dictionary);
    612.                 }
    613.  
    614.                 _Foldout = EditorPrefs.GetBool (label.text);
    615.             }
    616.         }
    617.  
    618.         private static readonly Dictionary<Type, Func<Rect, object, object>> _Fields =
    619.             new Dictionary<Type, Func<Rect, object, object>> ()
    620.             {
    621.             { typeof(int), (rect, value) => EditorGUI.IntField(rect, (int)value) },
    622.             { typeof(float), (rect, value) => EditorGUI.FloatField(rect, (float)value) },
    623.             { typeof(string), (rect, value) => EditorGUI.TextField(rect, (string)value) },
    624.             { typeof(bool), (rect, value) => EditorGUI.Toggle(rect, (bool)value) },
    625.             { typeof(Vector2), (rect, value) => EditorGUI.Vector2Field(rect, GUIContent.none, (Vector2)value) },
    626.             { typeof(Vector3), (rect, value) => EditorGUI.Vector3Field(rect, GUIContent.none, (Vector3)value) },
    627.             { typeof(Bounds), (rect, value) => EditorGUI.BoundsField(rect, (Bounds)value) },
    628.             { typeof(Rect), (rect, value) => EditorGUI.RectField(rect, (Rect)value) },
    629.             };
    630.  
    631.         private static T DoField<T>(Rect rect, Type type, T value) {
    632.             Func<Rect, object, object> field;
    633.             if (_Fields.TryGetValue (type, out field))
    634.                 return (T) field (rect, value);
    635.  
    636.             if (type.IsEnum)
    637.                 return (T) (object) EditorGUI.EnumPopup (rect, (Enum) (object) value);
    638.  
    639.             if (typeof (UnityObject).IsAssignableFrom (type))
    640.                 return (T) (object) EditorGUI.ObjectField (rect, (UnityObject) (object) value, type, true);
    641.  
    642.             Debug.Log ("Type is not supported: " + type);
    643.             return value;
    644.         }
    645.  
    646.         private void ClearDictionary() {
    647.             _Dictionary.Clear ();
    648.         }
    649.  
    650.         private void AddNewItem() {
    651.             TK key;
    652.             if (typeof (TK) == typeof (string))
    653.                 key = (TK) (object) "";
    654.             else
    655.                 key = default;
    656.  
    657.             var value = default (TV);
    658.             try {
    659.                 _Dictionary.Add (key, value);
    660.             } catch (Exception e) {
    661.                 Debug.Log (e.Message);
    662.             }
    663.         }
    664.     }
    665. #endif
    666. }
    667.  
    And then I create a custom script for each one I need:

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3.  
    4. namespace Tools {
    5.     [CustomPropertyDrawer (typeof (AttackConfigGroup.AttackConfigDictionary))]
    6.     internal class AttackConfigDictionaryDrawer : DictionaryDrawer<Behavior.ConfigMode, AttackConfigGroup> { }
    7. }
    8.  
    and then you can have a serializable dictionary.

    I agree though, it's a total pain to use this and it is an ugly work around for something that is so ubiquitously needed that almost everyone would want to do it.

    Hope it helps though.
     
  17. Ne0mega

    Ne0mega

    Joined:
    Feb 18, 2018
    Posts:
    755
    Inspector tab needs a "show in project window" button for assets, a "show in hierarchy" button for scene items and a "show in explorer" button for all.

    EDIT: The world is a GREAT PLACE because Unity has a "ping" that lets you see where your item came from.
     
    Last edited: Mar 15, 2021
  18. Ne0mega

    Ne0mega

    Joined:
    Feb 18, 2018
    Posts:
    755
    Adding " (1), (2), (3)..." to copied object names is HORRIBLY CRUEL. It ensures we either have to highlight and erase them (be sure to get both of those pesky little thin parenthesis, and don't start your drag on the wrong side of one!), or press backspace/delete FOUR times to remove it.

    It would be A KINDER, GENTLER WORLD if it were "_1, _2, _3...". That way it only takes a double click to highlight, then delete, or a ctrl+backspace.

    EDIT: The world is a KINDER, GENTLER PLACE because in 2020 somebody let us change this, as ryanslikesocool has noted below,
     
    Last edited: Mar 15, 2021
    awesomedata likes this.
  19. ryanslikesocool

    ryanslikesocool

    Joined:
    Jul 31, 2016
    Posts:
    49
    This became an option sometime in the 2020 release cycle under Project Settings/Editor, below Enter Play Mode Settings.
     
    Ne0mega likes this.
  20. jonathans42

    jonathans42

    Unity Technologies

    Joined:
    Jan 25, 2018
    Posts:
    514
    Hi,

    This is already the case with the Ping option:

    upload_2021-3-15_8-10-26.png
     
    Ne0mega likes this.
  21. Neonage

    Neonage

    Joined:
    May 22, 2020
    Posts:
    287
    Btw, who is responsible for this "Lock" button at the very top? I've been clicking there for "Normal" my entire life!!
     
  22. jonathans42

    jonathans42

    Unity Technologies

    Joined:
    Jan 25, 2018
    Posts:
    514
    Hi, it has been there for as long as I can remember. We could move it down.
     
    awesomedata likes this.
  23. Neonage

    Neonage

    Joined:
    May 22, 2020
    Posts:
    287
    Huh, I didn't ever noticed it.
    Unity 2019.4:
    upload_2021-3-15_19-25-1.png

    Anyway, what's the purpose of it in the context-menu when we already have a much nicer button next to the pie-menu?
     
  24. jonathans42

    jonathans42

    Unity Technologies

    Joined:
    Jan 25, 2018
    Posts:
    514
    Hmm good point. I will ask the question with product design and we'll see what to do about this.

    Thanks,
     
    awesomedata likes this.
  25. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Yeah, I hate that the lock is on the top now with a passion! We already have a button for locking, and since we want to actually use Unity and not go insane, we've added a shortcut button to lock it, so all it did was put a thing we already had easy access to in the way of something we need to click.
     
    jonathans42 likes this.
  26. Slashbot64

    Slashbot64

    Joined:
    Jun 15, 2020
    Posts:
    322
  27. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,526
    Can we finally use Pen Pressure for tablets in the editor? And not just for mac but cross-platform, this should not be so hard, other engines have this for a while now.
     
  28. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Can unity build in a 'back' and 'forward' button on the inspector. I know there are assets that add this but a simple back and forward to cycle through selections in history would be really useful and speed up or remove a lot of searching through the scene and project windows.
     
    AcidArrow, NotaNaN, gasppol and 4 others like this.
  29. FiveXGames

    FiveXGames

    Joined:
    Apr 27, 2016
    Posts:
    43
    Button + Mouse Clics (shortcut) like Visual Studio
     
  30. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    Agreed. Selection management is a constant source of friction and needless clicks in Unity. I need to constantly juggle multiple locked project and inspector tabs to get anything done.
     
  31. pbritton

    pbritton

    Joined:
    Nov 14, 2016
    Posts:
    159
    I fully agree with the editor being painfully slow. Too many times I wonder if folks at Unity are using their editor or even what they are doing. Couple the insane sluggishness of the editor with the constant loading and loading and hold on loading. Sometimes I don't do anything and it is loading. I am teaching this at my university and it has been nothing short of a painful experience. I have seen nothing from Unity that addresses this issue.

    Now about packages. It is another broken promise. The initial idea was to make packages independent of Unity versions but now it is locked to the version negating one of the core elements of packages. I am just going to finish my current project and find an alternative. It has been years of waiting and little to no improvement to the overall experience. Prior to Unity moving to the yearly versions, iteration was fast and responsive, making it a joy to use. Now, every script change takes at least 12 seconds and most changes locks the editor. Nothing is pushed to the background. Unity at the moment is just a painful experience that I have to unfortunately share with my students.
     
    awesomedata and Slashbot64 like this.
  32. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,349
    The 40 projects limit in Unity Hub, rendering it useless for any practical daily usage, as i do all work manually in the end to keep my projects listed properly and know their position in the disk.

    Clearly the Hub is useless in some cases, unless this strange random limit is lifted.

    Also the Hub will just replace a project at random when the 40 limit has been reached, without any warning or indication, resulting to hours of work to identify which project was removed.

    Unity response is that is a legacy compatibility thing, but why would that be ? Then maybe we should be still using Unity 5.4 core for legacy purposes as well, things must move forward at some point, I dont see why the Hub should remain limited like that forever.
     
    Nexer8, awesomedata and mgear like this.
  33. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    In addition to forward/back selection buttons the inspector also needs a highlight current selection button. I often want to quickly jump to the object that's selected in the inspector after navigating elsewhere so having a 'highlight' button on the inspector would enable you to quickly find whatever it is you have selected in the inspector.
     
  34. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    I recently learned that you can do that by right-clicking the inspector header and selecting "ping"
     
    Antony-Blackett likes this.
  35. Neonage

    Neonage

    Joined:
    May 22, 2020
    Posts:
    287
    Or better - you can press "F" to frame selection while hovering on hierarchy
     
  36. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Oh, that’s fairly well hidden.
     
    awesomedata likes this.
  37. SoftwareGeezers

    SoftwareGeezers

    Joined:
    Jun 22, 2013
    Posts:
    902
    Not sure of the value of this thread any more. The initial post was a somewhat scientific approach to measuring usability. Now it's just a mess of different opinions without any structure making it hard to really identify what's what or address real workflow issues. Seems like there are opinions from veterans and new users alike, making it hard to tell what problems lie in Unity and what lie in inexperience (and what Unity can do to make things more intuitive for new users). Then you have ideas here people may be sharing, like slow Editor performance, not being made aware the discussion is ongoing where they may be able to add to the discussion.

    Is the original Show Us Your Pain programme running? Any updates to official, structured research and new Workflow investigations?

    Some of the stuff here should be discussed in its own conversation. eg. Performance is heavily linked to the computer running Unity. Some of the criticisms here, I don't experience. Therefore it's not a workflow issue but something else, either bugs or Best Practices or issues with some versions or maybe people with buggy installs that, perhaps, can resolve their issues with a clean install. Whatever, listing such performance issues here neither helps with real workflow discussion nor helps those with performance issues find solutions.
     
    Nexer8 likes this.
  38. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Note sure if @willgoldstone or anyone else is monitoring it or not. Will would know!
     
  39. Deleted User

    Deleted User

    Guest

  40. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    249
    If I'm not using Bolt do I need to remove it via package manager every new project? I first noticed this today when there was some sort of DontDestroyOnLoad with a name like "variables" when I tried Playmode.
    Seriously? does this get added to every project? Why?
     
  41. Deozaan

    Deozaan

    Joined:
    Oct 27, 2010
    Posts:
    707
    See this recent blog entry:
    https://blogs.unity3d.com/2021/03/23/unity-2020-lts-and-unity-2021-1-tech-stream-are-now-available/

    In short: as of Unity 2021 Bolt is integrated into the editor and can't be removed.
     
  42. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    165
    When showing "Current Platform" it should show what that platform is. I can't count the number of times current platform was selected and it should have been Android but was in fact Windows and I didn't find out until the reimport process was finished only to have to reimport back to Android.

     
  43. hecker_de

    hecker_de

    Joined:
    Mar 28, 2021
    Posts:
    19
    I'm still new to Unity, but my immediate pain point is that I can tab forward between fields in the Inspector (for example entering position values), but not backwards using shift-tab...

    Also, the integration between UnityHub and my browser just isn't. Good thing one can run Unityhub with "unityhub:..." links from the command line.
    In the other direction, when I select a project from the "Learn" tab and then "View Tutorials" it opens an OAuth2 API link with an invalid token...
     
    useraccount1 likes this.
  44. hecker_de

    hecker_de

    Joined:
    Mar 28, 2021
    Posts:
    19
    Now, if only the "My assets" page of the asset store would reveal them similar to the "download unity" archives list...
     
  45. Brogan89

    Brogan89

    Joined:
    Jul 10, 2014
    Posts:
    244
    Hi, I've been a long time Windows and Linux user for Unity Editor but am now using MacOS. I have one pain point right now and that is the Folder/File ordering is raw alphabetical rather than respecting Folders first then alphabetical like it is in Finder. For some reason its only like this on mac.
    I came here because I can not find an option in the preferences for changing this.
    Please make it work like Windows and Linux. Thank you :)

    upload_2021-4-16_16-59-53.png
     
    nikescar, NotaNaN and ryanslikesocool like this.
  46. IOU_RAY

    IOU_RAY

    Joined:
    Jul 25, 2017
    Posts:
    127

    Speaking of reimporting.....that's a fun development process on its own!
     
  47. GinoMan2440

    GinoMan2440

    Joined:
    Feb 14, 2018
    Posts:
    9
    SerializedProperty and the documentation around it is hot garbage. It's like an opaque box that zaps you when you touch it and then while you're reeling from the burn in your finger also burns your house down to spite you, but actually does something useful if you can only figure out how to touch it the right way. You give it voice commands and it will either obey or pretend you're an idiot just because you can't see inside it. You go grab the user manual for help and it basically tells you the commands you can issue it but nothing of what they do. You can try to insert a coin into the obvious coinslot but you'll just get a banana when you ask for it back. It's hot garbage. Its documentation is hot garbage.

    But wait, maybe there's some probe you can attach to it to find out how it works? Nope! and if you try to write your own probes it fries the multimeter they're attached to.

    In all seriousness the thing is a SOLID mess, it's poorly documented, and utterly required for extending the editor. It's a complete violation of both "Easy things are easy, hard things are possible" AND "Objects should behave by default exactly as the user expects them to".

    Hmm, I need the serialized object for my custom property drawer. Let's see. Ahh, the drawer is for a list item. So surely I can use the array methods in here right? Oh no! I can't. Why not? hmmm Ok, I'll look for the property that corresponds to my list item. Oh that's not the right name because the list wrapper class doesn't define the name, it's internal object does. Ok. Let's try that. Oh look I got a list. Cool now I can work on it. Want to add an item? Oh no! there is no add method. Ok, well, there is this insert method but hang on, no, it didn't insert anything, it created a copy of the object before it and put THAT in. Ok. Well, how big is this array of underlying objects? Oh no! it's one short. Wait. Is it really one short of the underlying object? hmmmm "arraySize"... What about that screams "Last Index"? What's this? inserting doesn't actually change the length of the array? Who came up with that idea!? The cheese melts in the microwave, the objects melt in SerializedObject. Oh look, the Object is bleeding now.

    Unity's underlying programming interface is beginning to feel to me as a programmer like Sibelius does to Tantacrul (
    )
     
    awesomedata and FernandoMK like this.
  48. GinoMan2440

    GinoMan2440

    Joined:
    Feb 14, 2018
    Posts:
    9
    Serialized Property is to the programmer what Sibelius is to Tantacrul. I wrote a whole post about why but despite using no profanity it appears that it was flagged as "inappropriate". If you don't get the reference to Tantacrul:



    DM me if you want to read it.
     
    FernandoMK likes this.
  49. TheVirtualMunk

    TheVirtualMunk

    Joined:
    Sep 6, 2019
    Posts:
    150
    Would like to add here as I don't know where else to give this suggestion;

    Use case;
    1. Working on a project for a specific platform using a specific Unity version.
    2. Been a while since updated unity - some core features of our application have been improved by a new unity feature/bug fix.
    3. But I'm afraid its not worth my time to upgrade to the latest Unity version and debugging potential new broken stuff.
    4. I could go through each individual release page newer than my current version at https://unity3d.com/unity/whats-new/201x.x.x to get an overview of what I might expect, but wow, that might take a long time...
    Solution:
    Give us a tool on the unity website to diff our current version against all newer features up until a target. This way we can quickly do a search; for instance "WebGL" to get all the expected changes our project might benefit/suffer from if upgrading.
     
  50. KarlKarl2000

    KarlKarl2000

    Joined:
    Jan 25, 2016
    Posts:
    606