Search Unity

Unity is not .NET

Discussion in 'General Discussion' started by ippdev, Aug 11, 2018.

  1. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Premise : Unity uses a C# scripting environment. It translates this to C++. Ergo alot of what folks think is happening if not familiar with Unity is not exactly what happens under the hood.

    Backstory : I have read in many threads about this and that method causing garbage collection, allocations and other issues which cause performance hiccups. I am stuck with 3 hard core C# .NET guys and they still can't get their head around AddComponent and Instantiate versus new...445 warnings in console. I have like 300 components using the Unity UI. One of these guys decided he is gonna write a .NET GUI setup for windoze business forms with his own little events and custom buttons API and his three widgets on stage and they blew all to hell and back and won't work because I changed the canvas space to ScreenSpace-Camera. Not very robust for a supposedly superior system. All my stuff still works. He had parent objects at scale 99 and child objects at 0.0012. I set up a Unity template for him. I just checked and got it working in five minutes by just dropping proper UI elements in. He won't use it because it is not a part of his vaunted superior knowledge based .NET driven system.

    Story : I am preparing to lay this all out to the stakeholders as I am expected to write VR/AR and have cursors change, write voice command for the UI and other systems I would prefer to rely on the Unity API for and not have to write through another API on top of Unity to get to Unity's API but a whole new subsystem has to be written for this reprobate..once he rejiggers stuff for the new canvas space when i am asked to alter it for phone UI or tablet his crap is gonna blow up all over again and the C# guys will be like..did you have to?..ummm..yes.. I need ammo.

    I have a number of issues I have asked as far as procedure is concerned such as if you know the Count of a List do not use foreach as it causes allocations and sometimes there may be 5k objects looped thru many times in a row. When this burps the VR I will be looked at but I am not allowed to touch this code..affirmative action and all that crap.. I don't want to sound like a bitch doing this and want to lay a solid case to the stakeholders. I have been using Unity daily since 2010 and servicing top end clients and handed over alot of code to established studios who never had an issue with my Unity code, performance of said code nor work ethic. The prime monetary stakeholder trusts me 1000% and told me to make this a Unity app that Unity devs could easily sit down and begin working in. It is 8 years Unity experience versus a total of maybe three years at most amongst them. The fellow with the vaunted superior system was trying to place a flat image as a background and had no clue it was a skybox..just to give you some params for experience or knowledge level.

    Request : So, having established a framework of reasoning above I want to see a list of things that will work in .NET but do not work the same in Unity due to marshalling to C++, not work at all or are inherently bad practice due to garbage collection. Anecdotes on solving this with other studio mates or employees you had to work with with similar notions may be helpful as well.
     
  2. ShilohGames

    ShilohGames

    Joined:
    Mar 24, 2014
    Posts:
    3,021
    One example that jumps to mind is not using the Update function in every class attached to every game object when there are thousands of game objects. Here is a link about that topic:
    https://blogs.unity3d.com/2015/12/23/1k-update-calls/

    And of course, any time there are actually thousands of game objects, it would be smart to use ECS or something similar instead of game objects.
     
  3. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    I only use Update on progress bars, navigation and wherever I have to use it the gameObject is set to inactive except for when I need the actual functionality. I keep framerates at 450 - 700 until we load an architectural model..then we drop down to 20 - 150 fps. The BS I am dealing with will not even allow me to change the hiccupping foreach loops into for loops using the count of the List. I can see them spike in the Profiler. Things like null values and Compare don't work correctly with some of the data and I am sure it has something to do with marshalling to C++ or something. Thing is I use Unity, don't get hung up on the technical compsci terms but certainly have run into every scenario where something should work in theory but the quirks of Unity won't allow. Like string formatting. I would copy directly from .NET examples at the ms dev site and they would not work in Unity. Over the years i figured my own ways around such quirks.

    The version we are using had issues with prefabs and UI.. I ended up using templates in the Hierarchy to avoid the sudden glitches in the prefabs. The employee ( I am a contracted Unity consultant brought in to convert the application from C# into Unity) who makes my life miserable refuses to stop using them even though his issues arise from such, as I broke the prefab and got his crap working again..but then my fix was made broken again and his crap will be broke at the investors meeting ... My switching the UI to ScreenSpace - Camera will be blamed and not inadequate knowledge of Unity or recalcitrance to admit that perhaps he should be doing what i asked politely when he came on board.
     
  4. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,519
    Models are garbage. I do this at work, the engineers have zero sense of optimization.
     
  5. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Linq is the first thing that springs to mind. It can still be used, but only when you're willing to generate garbage.

    Also, from memory .NET's standard event passing paradigm involves using a custom object that describes the event. Again, this can work, but you don't want to be creating a new object each time - at the very least you want to be pooling them.

    I think the really important distinction to be made is that while you're writing code in C#, the majority of what the computer is doing isn't in the scripting runtime. I would point out that the scripting runtime sits on top of a game engine, and understanding what's going on in the game engine part of the system is really important. This guy might know C# really well, but that's only part of the picture.

    Your description of the situation brings to mind the saying "to a man with a hammer everything looks like a nail".
     
    one_one, Ryiah and Kiwasi like this.
  6. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,051
    Ugh. That is an ugly one. It makes prefabs in the ui almost completely unusable. I would try to argue a case for upgrading unity, it’s only a point version or two to escape that bug.
     
  7. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Tell me about it. I have imported couches with 450,000 polygons. That is why it is super important to have the rest of the application smoking hot with performance.
     
  8. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Oh.. I tried that but ya see.. in .NET you never upgrade during development apparently. What I need is a few killer shots that higher ups who don't code can understand or i ain't getting any cooperation. The bigshots don't get compsci jargon,..their eyes go glassy.
     
  9. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853

    I am aware of the LINQ thing and it has had to be used in a few components and I shut that stuff off as soon as. I have reiterated similar to what you have stated over and over..That Unity is a frame dependent component based architecture that uses C# as a scripting language for a C++ engine. Much can get lost in translation. I learned C# through the use of Unity and performed on dozens of contracts and my code has always passed muster first round no questions. They dragged and dropped, hit play and it worked performantly per design specs. This job, though it pays well, is the first time i have had my hands consistently tied in regards to cogent and correct advice being summarily ignored, causing problems, me solving them and preferring [feelings..nothing more than feelings] to go back to the problem method rather than take sober advice.. It is new to me in a workplace and I am not sure how to deal with it. I am inclined to just walk away and await the phone call down the road when the affirmative action gambit blows up in their face..
     
  10. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Maybe draw a picture? There's C# land, and there's scene land, and there's engine land, and decisions need to take all three into account otherwise they'll mess stuff up in the others.

    Personally, I got really choosy about picking my fights when in a similar situation. I would always put my advice in, but I'd only hold my ground on stuff that really mattered, because if you argue about all of the things people stop listening.
     
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I feel your pain. I've been in similar circumstances before.

    Plenty of advice around the garbage collector that doesn't make sense in a regular .NET environment. You probably know all of this already. Pooling everything you can. Reusing memory rather then producing new allocations. Manually calling GC.Collect at a performance appropriate time after a big chunk of deallocations.

    This. So much this. If I had to choose between knowing the internals of .NET and knowing the internals of Unity, I would go with Unity every time.

    Unity is kind of the same. Except that you need to take about four or five shots first to figure out which of the development environments has the least breaking bugs and workarounds for your particular use case.
     
    ippdev likes this.
  12. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853

    We had some blowups upgrading with TMPro and had to manually reset every text and input field to get the text back just prior to the upgrade that trashed prefabs. Hence the extreme trepidation to upgrade again. I worked around the prefab issue by making templates and leaving them inactive in the hierarchy. I would Instantiate them and it worked flawlessly and I could move on. I told this guy and he refuses to budge off the UI prefab..even after I remade it. He just reverted back. Pride and falls.
     
  13. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    866
    Upgrade every full version toward the end of that versions point releases. Not upgrading will bite you bad if you skip versions.
     
  14. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Unity uses Mono by default, not C++ (il2cpp), just a note
     
  15. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    True for some platforms. I have seen you chime in with a number of revelations of what happens under the hood when you use certain variable types. The main point is that compiled Unity is not a C# application and should not be treated as a .NET core CLR environment. I learned C# in Unity {also had years of UnityScript and some Boo development scripting as well] so I do not have the deep knowledge of working in a pure .NET environment so I can make a strong case against writing things like a windows OOP business form in a frame dependent component based architecture versus using an API built especially for said architecture and keeping an eye on performance even if it means foregoing standard paradigms in use in most other development. Frankly he can do whatever he bloody well pleases but as soon as I have to write a system that takes me a few minutes like making the cursor type change when over an interactable UI object and he refuses to use the Unity UI nor write an interactable bool that can be gotten in the same way Unity does, I am simply writing all kinds of conditionals for a hacky framework that is useless and wasting company money coddling this guy. Same thing for refusing to not use a prefab when it is well documented that the version we are on trashes UI prefabs out of the blue. It goes against my professional and capitalistic sensibilities.
     
  16. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    I think the point is that whether you use IL2CPP or Mono or not, data must be marshalled to C++ since that is what the actual engine runs in. The engine will always run as C++
     
    Kiwasi and ippdev like this.
  17. ShilohGames

    ShilohGames

    Joined:
    Mar 24, 2014
    Posts:
    3,021
    The issue with foreach is that the Mono implementation creates an Enumerator object as a class on the heap instead of as a struct. The applies to Lists but not arrays. Note, I don't know if this applies to IL2CPP.
     
    ippdev likes this.
  18. ShilohGames

    ShilohGames

    Joined:
    Mar 24, 2014
    Posts:
    3,021
    As far as null checking goes, there is a slight bit of performance gained from slightly less straightforward code.

    For example, a lot of code has an if statement checking to see if a game object is equal or not equal to null. For example, this would be a typical line:
    if(gameObject!=null)

    A faster solution is to use the following:
    if(!System.Object.ReferenceEquals(gameObject, null ))

    The reason for this performance difference is the call between the native-managed bridge. I don't know if this applies to IL2CPP.
     
    ippdev likes this.
  19. ShilohGames

    ShilohGames

    Joined:
    Mar 24, 2014
    Posts:
    3,021
    Another thing of interest is how to allocate variables in a class. In most business .NET software, variables get declared inside functions and cleaned up automatically as they go out of scope. With a game, there is often some performance to be gained by declaring some variables as class level privates and re-using them throughout functions. It goes against modern scoping concerns, but it addresses memory and performance issues that are usually more important in games.
     
    Kiwasi and ippdev like this.
  20. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    This is stuff I know intuitively. Nice to see it outlined technically ...good ammo.
     
    QFSW likes this.
  21. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    There are good and bad .NET developers :) at my dayjob it's not super relevant to care about allocation but it's not like its rocket science to adept to the fact it's important in Unity :) (Though I do think allocation is OK if you know what you are doing, for example I have no problem using Linq from user actions that I know can't execute more often than the user can press a button or similar).

    The biggest difference lies in .NET Vs Mono CLR but more so the compiler and the core API, it's alot of differences between Mono 2 and .NET, though as far as I know most of those quirks are fixed if you run Unity in 4.5.

    Also I don't see a problem in using fancy designs etc if you know what you are doing, problem is many devs dont know what they are doing. A correctly implemented MVVM library ontop of Unity UI shouldnt create noticable perfmance loss if done correctly for example. In fact our game is written 100 procent as a enterprise application like I would my dayjob and we even have gotten praise for how well it run compared to our adversary. Core domains usualy aren't the problem, the rendering is, more so in VR that we target.
     
    frosted likes this.
  22. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Keep in mind that those two lines of code do not actually do the same thing. If you destroy the GO, the top line will work as you expect, but the bottom line will not since it still references the container object. Unless you actively assign null to the variable, that won't work in a lot of cases.
     
    Kiwasi likes this.
  23. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    AFAIK this was the case but was fixed somewhere in the 5.x cycle. I may very well be wrong however
     
  24. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,145
    Unity 5.6. I wasn't able to remember where it in the release notes but here is an article with benchmarks for each collection.

    https://jacksondunstan.com/articles/3805
     
  25. Murgilod

    Murgilod

    Joined:
    Nov 12, 2013
    Posts:
    10,139
    It was fixed. I noticed the GC allocs disappear on one of my projects when I tested updating.
     
  26. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    Thanks, good to know I didn't dream it happening :p
     
  27. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    (PS: Wow this ended up being a bit of a long rant)

    I lose a bit of myself every time I manually inline a bunch of vector math lines (including constructors / operators) making the code 5 times as long. But then it shows a 30% speed boost on a task that's running a lot, and it's worth it. (was tested using .net 3.5, I assume the updated runtime is a bit better at it).

    Optimizing for your codes' performance does not really seem to be a thing outside of the game dev world. There's people happy with their site hosting responding to a request within 100 milliseconds. 100 milliseconds. I'm not a web dev so maybe it does do all kinds of fancy things in the background, but the average sites' response time should not be 100 milliseconds (not taking into account internet ping).

    I get that there's a tradeoff between a devs' salary and the hosting costs involved (easier to eat the hosting cost scaling up then hiring more (/ more competent) devs) - its the same argument about using c++ vs c# vs python.
    I feel like every group of devs that manages something that runs a lot should have someone who just says "hey, this function here, if we use data structure <x> it'll work 3 times as fast" and possibly "hey if we drop this edge case requirement we can run the code 3 times as fast while maintaining 95% of the functionality".

    For a lot of games optimization is key. PC/android games maybe even more than console games. On a console you know your target. If you can maintain 60 fps, you can maintain 60 fps - you're done. Sure you can optimize a bit more so you can fit in a bit more vertices / code, but roughly if you hit your fps goal you're done.
    For PC/android games you'll always have people who try to run your game on a toaster from 2006, hopefully on minimum settings. Having your game run better on these toasters will help in word-of-mouth spreading of the game and potential market size. If it doesn't run on the toasters, you may be missing half or more of the possible market often. And everybody playing your game benefits from improved framerates and/or battery life and/or noise and/or electricity cost.

    Offtopic 1: Anyone tried/used mono.simd.dll? Did it work properly cross platform with a usable speed improvement? I feel like the wrapping from Vector3/4 to vector3f/4f will be quite annoying.

    Offtopic 2: I really like the direction c# seems to be taking recently with Span<T> and some other things that'll help with performance improvements.

    Offtopic 3: I would really like an option in .net to say "hey, this program is long running, can you recompile the code but take 5 times as long optimizing?". Java hotspot VM I guess.

    Offtopic 4: I would really like more compiler enforced attributes like [Pure], [NoGC] and [ThreadSafe], possibly even optional ownership semantics somewhat like rust.
     
    QFSW and ippdev like this.
  28. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This might be faster, but its generally a bad idea. Unity uses a fake null object and overloads ==, which means that using reference equals does not return the same thing as a regular null check. A GameObject can pass a reference equals check, and still throw a null reference error on the very next line.
     
    ippdev and zombiegorilla like this.
  29. ShilohGames

    ShilohGames

    Joined:
    Mar 24, 2014
    Posts:
    3,021
    I only mentioned System.Object.ReferenceEquals because it was possibly relevant to what the OP was asking for. I don't advocate using System.Object.ReferenceEquals in most projects. As you mentioned, it is not exactly the same thing as a typical null check. In addition to that, it hurts code readability.
     
    Kiwasi likes this.
  30. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    If null check hurts your performance then you are doing something terrible wrong :D
     
  31. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Anybody can chime in with why we use AddComponent and Instantiate versus new under the hood?
     
  32. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    AddComponent and Instantiate versus new, all 3 allocate I dont see a difference there? You shouldnt be afraid of allocate, as long as its valid and sane allocation.
     
  33. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    So...you come on a job site. There are over 350 components written using standard Unity API style and their UI. Would you consider yourself smart to begin writing stuff your way or would you look at what was there and try to adhere to practices and systems in place a year before your arrival. Including naming conventions. Or would you consider it your duty to 'enlighten' everybody so that the framework could be ripped apart and updated so you held the keys to it and all had to enter though your API? Ya know..because it is so superior and powerful..and f*** Unity techs and their lack of superior skillz. Ok.. I know...baited and loaded question..:) I don't think anybody coming on should have to learn an MVVP library written over top of Unity's toolkit. It's like putting a saddle on a saddle on a horse. Just saddle that horse up and ride. You may sit taller with two saddles but they ain't stable going over rough terrain quickly. Plus all those extra stirrups, straps and saddlebags flapping around for no reason.
     
    Kiwasi likes this.
  34. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Depends on the quality of existing code, I'm no stranger to berate a new team for bad existing code. Its my job as a system architect, though it must be done in a constructive manner. Unity is just a API like any other API, you will want to abstract it and form your own API ontop of it that fits your use case, abstraction is always good as long as its sane (Some devs just abstract for the abstraction).

    Talking about AddComponent, I know a place that I use it alot normally we call this code on scene load so doesnt matter. But we have a tutorial that runs this code after scene load and I have noticed a little spike so I just benched it.

    Here is the code, we have 26 colliders so n = 26

    Code (CSharp):
    1.        private static readonly  Dictionary<Type, Action<Transform, Collider>> colliderCloners = new Dictionary<Type, Action<Transform, Collider>>
    2.         {
    3.             {
    4.                 typeof(BoxCollider), (transform, collider) =>
    5.             {
    6.                 var box = collider as BoxCollider;
    7.                 var target = transform.gameObject.AddComponent<BoxCollider>();
    8.  
    9.                 target.center = box.center;
    10.                 target.size = box.size;
    11.                 target.isTrigger = box.isTrigger;
    12.             }
    13.             },
    14.             {
    15.                 typeof(CapsuleCollider), (transform, collider) =>
    16.             {
    17.                 var capsule = collider as CapsuleCollider;
    18.                 var target = transform.gameObject.AddComponent<CapsuleCollider>();
    19.  
    20.                 target.center = capsule.center;
    21.                 target.direction = capsule.direction;
    22.                 target.height = capsule.height;
    23.                 target.radius = capsule.radius;
    24.                 target.isTrigger = capsule.isTrigger;
    25.             }
    26. }
    27. };
    28.  
    29.  
    30.         private void CreateCompoundColliders()
    31.         {
    32.             var colliders = Bomb.GetComponentsInChildren<Collider>();
    33.             foreach (var col in colliders)
    34.             {
    35.                 var child = new GameObject();
    36.                 child.transform.SetParent(col.transform);
    37.                 child.transform.localPosition = Vector3.zero;
    38.                 child.transform.localRotation = Quaternion.identity;
    39.                 child.layer = col.gameObject.layer;
    40.                 child.name = col.GetType().Name;
    41.  
    42.                 colliderCloners[col.GetType()](child.transform, col);
    43.                 child.AddComponent<ObiCollider>().CollisionMaterial = Material;
    44.                 Destroy(col);
    45.             }
    46.         }
    We call AddComponent 52 times

    It turns out its a third party tool that creates the spike, like always :D

    upload_2018-8-12_12-57-30.png
     
  35. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Instantiate/AddComponent creates a C# object and the corresponding C++ object together. It registers the newly created GameObject/Component with the various systems Unity uses. (Incidentally calling new on a GameObject does the same thing too, don't be afraid to use new GameObject("SomeName") when the situation calls for it).

    New is used for creating regular vanilla C# objects. It goes ahead and calls the constructor.

    The thing is you can't use new to create a MonoBehaviour or to add Components to a GameObject. Unity will throw an error. Even if you do create your entire structure without GameObjects, you still need to use GameObjects and Components to interact with Unity's rendering system (and physics ect too). Which means in most cases, you are better off just using components in the first place, rather then putting an extra layer in between you and the engine.
     
    Ryiah, ippdev and angrypenguin like this.
  36. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Edit: Beaten! :)

    Under the hood Unity reserves the MonoBehaviour constructor for its own purposes, and does stuff that isn't fully publicly documented hooking up internal engine stuff. There are other ways that probably could have been handled, but that's what Unity picked, so constructors are just out of bounds to us. Because of that they instead give us Awake() and Start(), but they really aren't the same thing for two notable reasons:
    1. You can't give them parameters.About the closest we can get is a static CreateBlah(...) method which calls Instantiate or AddComponent and then does whatever else we would want to do.
    2. Awake and Start are called at different times to a constructor. I believe that Awake is immediately after construction, so that one's pretty similar, but Start is called as a part of the next iteration of the engine loop. Understanding that engine loop and what it means for your objects and their lifecycles and when you can reliably access what things is pretty important, and fairly specific to a game engine (or similar real-time systems).
    This is of course only relevant for MonoBehaviour and friends. You can make plain C# classes and use their constructors and that's all groovy. You can use those classes from your MonoBehaviours, and you can have those classes reference MonoBehaviours to interact with the scene.
     
    Ryiah and ippdev like this.
  37. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    This bit depends really heavily on what you're doing. Treating the Scene as just a "view" of your data can be really handy. I've not bothered with that for games myself, but have seen others do so with great results. I have done that for non-game applications, and it has quite a few benefits. As @AndersMalmgren says, what's important is that you don't abstract just for the sake of abstraction.
     
  38. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    I would not use var and just explicitly type the component. I would not use foreach and use for loops. Other than the Dictionary stuff I write my code similarly.

    I despise going though code and seeing myVal = 7f or myText = "hello world" and then looking in the hierarchy to find what this generic thing is referencing and everything has the default name. It smells to me like copying sample code and shows no regard for other team members working with your stuff.
     
  39. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    No var removes bloat code and bloat code is bad for readability, same thing why we got syntax suger like expression bodied members etc
     
  40. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Your abstraction can inherit from monobehavuour (this is not a 100 procent abstraction since the users of your API can still access the Unity core functions) or better yet implement interfaces. GetComponent works on any interface as long as its implemented on a member that's a Component
     
  41. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    When you've got highly specialised types that end up with verbose names, sure. But when we're dealing with simply types I'm with @ippdev all the way - I want my code to be explicit.
     
    Ryiah, QFSW and ippdev like this.
  42. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    I tend to use it when creating inline value types like for some reason, so I do int foo = 5; instead of var foo = 5; it's very clear it's a int so I don't know why I do that, old habits die hard I guess. :)
     
  43. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Explain how using the component type versus var is bloating things beyond the readability. I prefer the component name or the class, property etc. for my sensibilities in distinguishing what the line of code is referencing.I always name the parent of a composite with a name that describes what it does and matches the component so if i wish to find what i am looking for in the scripts or the objects in the Hierarchy they are synched. Often I name the child objects as well so it explains what they do. Always CamelCased with no spaces. If I get kidnapped by aliens I am positive a second year Unity dev can sit down on day one and start working with my set up. It is consistent, properly named and referenced, adheres to Unity coding examples style, commented where the code is not explicitly stating what is occurring and organized where most of any work needed can be done in the Inspector without bothering the code guys to rewrite or add code.
     
  44. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    For the same reason we don't use Hungarian notation anymore its just noise. If you cant immediately understand what type it is you problsby need to refactor, method abstraction to make the current method shorter, or even maybe shorten the entire class by moving stuff out of it to maintain high cohesion.
     
  45. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    C# engineers are initially going to be the wrong people for be programming games in general. They're trained in practises that work actively against how Unity uses C#.

    So they need to understand that game development is about making up new rules (breaking them) because the rules aren't designed for games.

    In the world of game development, it's all about optimisations because it has to run within a specific time limit in order to be interactive and enjoyable.

    This means breaking the rules.

    The earliest game developers (since the birth of the very first game) up to today, had to be about breaking rules because the hardware wasn't (and still isn't really) designed for games. So to make games you need to play outside the established set of rules. That means using things that aren't designed for something, and not using things that are designed for something (in a language and hardware context).

    This typically means that most optimisations are breaking the "rules".

    Of course to any game dev using Unity, we're not really aware we're breaking rules, we're just flexible enough to think well outside the box, because that's a normal every day mode of thinking for the humble game developer.
     
    Kiwasi and Ryiah like this.
  46. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    I would say thats more because most enterprise software developers does not optimize even in their day jobs. I have seen such bad code through the years, its insane.

    You can still write efficient code and still follow the "rules", you might need to break them here and there, but in general its possible. Old principle like DRY, SOLID, etc, work well in game design, you just need to apply them correctly. For example when following the SOLID principle maybe its better to use a very simple and fast IoC system than a fully fledged DI.

    Or when doing pub / sub, at my dayjob this is fine, (actual code from my current project)

    Code (CSharp):
    1.     public class EventAggregator : IEventAggregator
    2.     {
    3.         private readonly WeakReferenceList<object> subscribers = new WeakReferenceList<object>();
    4.  
    5.         public void Subscribe(object subsriber)
    6.         {
    7.             subscribers.Add(subsriber);
    8.         }
    9.  
    10.         public void Publish<T>(T message) where T : class
    11.         {
    12.             subscribers
    13.                 .OfType<IHandle<T>>()
    14.                 .ForEach(s => s.Handle(message));
    15.         }
    16.     }
    This code is very simple to use, you just inject IEventAggregator and call Subscribe(this) from your constructor, no matter how many IHandle you implement it will just work. The overhead of using OfType and Linq doesnt matter. I would never use this code from game domain (Maybe from a screen space UI were frame rate isnt crucial). Instead our game domain equivalent looks like this

    Code (CSharp):
    1.  
    2.     public interface IEventHandler<in T> where T : struct
    3.     {
    4.         void Handle(T evt);
    5.     }
    6.  
    7. public class EventBus
    8.     {    
    9.         private class ValueContainer<T>
    10.         {
    11.             public T Value { get; set; }
    12.         }
    13.  
    14.      
    15.         private readonly Dictionary<Type, List<object>> subscribers;
    16.  
    17.         private readonly Dictionary<Type, object> cache;
    18.  
    19.         public EventBus()
    20.         {
    21.             cache = new Dictionary<Type, object>();
    22.             subscribers = new Dictionary<Type, List<object>>();
    23.         }  
    24.  
    25.         public void Subscribe<T>(IEventHandler<T> handler, bool replayLast = false) where T : struct
    26.         {
    27.             var type = typeof(T);
    28.  
    29.             if (!subscribers.ContainsKey(type))
    30.                 subscribers.Add(type, new List<object>());
    31.  
    32.             subscribers[type].Add(handler);
    33.  
    34.             if(replayLast && cache.ContainsKey(type))
    35.             {
    36.                 var value = cache[type] as ValueContainer<T>; //ValueContainer no value type boxing is done
    37.                 handler.Handle(value.Value);
    38.             }
    39.         }
    40.  
    41.         public void Unsubscribe<T>(IEventHandler<T> handler) where T : struct
    42.         {
    43.             var type = typeof(T);
    44.             if (!subscribers.ContainsKey(type)) return;
    45.  
    46.             subscribers[type].Remove(handler);            
    47.         }
    48.  
    49.         public void Publish<T>(T evt) where T : struct
    50.         {
    51.             var type = typeof(T);
    52.  
    53.             if(!cache.ContainsKey(type))
    54.                 cache[type] = new ValueContainer<T>();
    55.  
    56.             (cache[type] as ValueContainer<T>).Value = evt;
    57.  
    58.             if (!subscribers.ContainsKey(type))
    59.             {
    60.                 return;
    61.             }
    62.  
    63.             var handlers = subscribers[type];
    64.  
    65.             for(int i = 0; i < handlers.Count; i++)
    66.             {
    67.                 ((IEventHandler<T>)handlers[i]).Handle(evt);
    68.             }
    69.         }
    70.  
    71.         public void PublishDelayed<T>(T evt) where T : struct
    72.         {
    73.             UnityThreadDispatcher.Run(() => Publish(evt));
    74.         }
    75.     }
    Alot of quirks like ensuring user uses the API with structs etc so that zero allocation is ensured. It also has some stuff going on to avoid boxing when caching value types. Only downside here is, we cant use weakreference list we must have more control over allocation and dispose. Plus since we cant use Linq and fancy runtime generic code on Publish time we need to use much simpler code. The biggest difference for user is that if you implment one IHandle it will work as the other implementation. But if you have two IHandle you need to supply the generic arguments when calling subscribe. A little less readability sure, but does it break the rules? Not really, both honor the pub / sub principles with decoupled domains.
     
    Last edited: Aug 12, 2018
    hippocoder likes this.
  47. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    AFAIK Awake is also on the next update loop. It's why you can instantiate something and then change a member before the Awake runs. All the Awakes just run before all the Starts. I may be wrong but I think it's still delegated to the next update loop
     
  48. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    This is a particularly bad example to defend your case since it makes no distinction if foo is inherently integral or fractional
     
    angrypenguin likes this.
  49. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I don't use var at all, for what it's worth.
     
    Antypodish and angrypenguin like this.
  50. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    I don't either, although there are cases that wouldn't bother me when others use (such as over complex verbose names)
     
    angrypenguin likes this.