Optional Variables - Unity Tips [2020.1+]

Поділитися
Вставка
  • Опубліковано 13 бер 2021
  • Just a tiny video showcasing a really simple Unity pattern. I find it really useful and thought that maybe you would too.
    Thanks for watching!
    Sources:
    gist.github.com/aarthificial/...
    Support me on Patreon:
    / aarthificial
    #unity #unitytips #gamedev

КОМЕНТАРІ • 102

  • @clonkex
    @clonkex 3 роки тому +125

    This is the kind of thing that makes me love programming. You're not only programming a game, you're programming the tools to program the game.

  • @aarthificial
    @aarthificial  3 роки тому +48

    @EDIT I've updated the gist to correctly support prefab overrides.
    I have a really exciting topic for my next devlog! Sadly, it will take some time to make so, in the meantime, I wanted to create something less ambitious.
    Thanks for watching!

  • @Sephta
    @Sephta 3 роки тому +80

    These are real workable "tips and tricks" that I, and im sure plenty of other people, would love to see more of. If you're able to, please create more content like this!

  • @geekjoystick
    @geekjoystick 3 роки тому +34

    Really nice to have small tutorials like that, and more generally giving an idea of how things work in your game. Personally it really helps me to better structure my own ideas and projects. Keep up the good work :)

  • @ariehuybregts9875
    @ariehuybregts9875 3 роки тому +8

    This is such a useful function/struct that exists in a couple of scripting/markup langs I use that I've been wishing was in unity

  • @octsonandivorgias7007
    @octsonandivorgias7007 3 роки тому +14

    Short, Simple, and Straightforward. This video deserves more views!

    • @WingedOriTV
      @WingedOriTV 3 роки тому +1

      This Dude deserves more subscribers and views!

  • @early2000skid
    @early2000skid Рік тому +5

    Very nifty struct :)
    I'm curious as to how you created an empty PropertyDrawer at 1:31 because not having to write the boiler plate for that would save so much time. Not sure whether that's an inbuilt Unity thing or a unique feature of your IDE. Looks I've got some research to do, great vid Aarth :)

  • @Hoowwwww
    @Hoowwwww 3 роки тому +8

    i don't use unity, but i love watching your videos, quality content!

  • @tiredko-hi-
    @tiredko-hi- 3 роки тому +10

    I really like this solution. Easy to use, understand, and works perfect. Nice tip!

  • @_kett2164
    @_kett2164 3 роки тому +10

    that is brilliant! I just finished a project in unity and this definitely would have come in handy
    I am hyped for the next devlog!

  • @RandomProduct
    @RandomProduct 3 роки тому +5

    Just discovered this channel. These are incredibly useful, and you've got a smooth voice. I could see your stuff blowing up, my dude.

    • @RandomProduct
      @RandomProduct 3 роки тому +2

      Also, after watching the pooling video, I gotta say your animations are so helpful in understanding these topics. Very well done!

    • @aarthificial
      @aarthificial  3 роки тому +2

      Thanks!

  • @floatingblaze8405
    @floatingblaze8405 3 роки тому +7

    I don't use Unity, but I find this really informative nonethelss, great video!

  • @hazemsamy9058
    @hazemsamy9058 3 роки тому +6

    That's a great tip!
    will definitely come in handy with my current project. These types of videos are so informative and straight to the point, would be amazing if you gave us more of these useful snippets. Also, can't wait for the upcoming devlog, you have a great game in the making. Best of luck and keep up the great work.

  • @chris.davidoff
    @chris.davidoff 3 роки тому +2

    Found this from Unity's blog, and I'm stoked! I want to try benchmarking this to see the difference

  • @bilel114
    @bilel114 3 роки тому +4

    Thank you! More people should know this stuff, I love that you're sharing it.

  • @LutennantLongshot
    @LutennantLongshot 3 роки тому +4

    Wow these are super cool. Love the mini tutorial on random stuff as well.

  • @zop5725
    @zop5725 3 роки тому +6

    Oh dude, I have been looking a week ago, and found nothing good. Awesome Video!

  • @rasulseidagul
    @rasulseidagul 3 роки тому +4

    Good job 👍,
    it is actually so much easier now to work with generics thanks to unity team. Previosly such things required a lot of boiler plate code.
    However, Imho, they could have done it waaay erlier(and yet they still didn't implement full polemarfism)

  • @teamjoy1968
    @teamjoy1968 3 роки тому +4

    Nice and short :>

  • @verified_tinker1818
    @verified_tinker1818 3 роки тому +8

    Expected something like Rust's Options, but this is even cooler!

  • @jbaidley
    @jbaidley 3 роки тому +1

    Great tutorial. Clear, short, and to the point.

  • @rvv27
    @rvv27 3 роки тому +2

    Don't use Null check in Update loop. That is one of cool performance trick that I learned. This is a very nice tip 👍👍

  • @qwfp
    @qwfp 3 роки тому +2

    Really nice concise video

  • @partially2k
    @partially2k 3 роки тому +4

    Thx jacob - will be yoinking this ahaha

  • @DecrareOld
    @DecrareOld 3 роки тому +4

    Interesting

  • @NeoN-bd3so
    @NeoN-bd3so 3 роки тому +3

    Amazing, love your vids! Which Code Editor are you using?

  • @nagybalint1474
    @nagybalint1474 3 роки тому +1

    very useful thanks so much for creating this video :D

  • @Thomason1005
    @Thomason1005 3 роки тому

    neat tip, love it

  • @dpeter99
    @dpeter99 3 роки тому +2

    I'm sooo happy that I found your channel, these dev logs are great! Brought back my inspiration to actually make something maybe, but first back to my overcomplicated tooling
    You could also add some operator overwrites to the Optional class. For example auto convert to the value.
    Btw why did you use the old imgui based editor UI rather than the more modern UIElemens stuff?

    • @aarthificial
      @aarthificial  3 роки тому

      I'm glad to hear that!
      As far as I know, the default inspector still runs on IMGUI so you can't use UIElements inside of it yet. Although I may be out of the loop on that one

    • @dpeter99
      @dpeter99 3 роки тому

      @@aarthificial I think you should be able to do it. The editor uses a messed up way where the whole thing is UIElemens but the automatic fields in it are basically individual imgui containers. At least that is the case for the latest releases 2021.1 I think

  • @abdullahshafqat772
    @abdullahshafqat772 Рік тому

    This is OP dude

  • @peppidesu
    @peppidesu 3 роки тому +1

    Pro tip: If you add two user-defined implicit conversion operators between T and Optional, you can just write Optional foo = 0.3f;
    Or just use a nullable type and use the majestic null coalescing operator for the job, like this:
    private Transform? target;
    private void Update() {
    DoSomethingWith(target ?? default);
    // or
    target?.position = new Vector3(a,b,c);
    // or
    target ??= default
    }

    • @aarthificial
      @aarthificial  3 роки тому

      Thanks for the suggestion!
      Have you tested nullable reference types with Unity? I thought they where C# 8+

    • @andermium
      @andermium 2 роки тому

      For anyone coming across this, I don't think the null-coalescing operators work, because they bypass Unity's equality override. It's an unfortunate consequence and they wrote a blog post on how they would probably try a different solution if they could start from scratch

  • @Tantandev
    @Tantandev 3 роки тому +3

    Cool trick!
    Was this inspired by the Rust programming language?
    Option is a built in type there.

    • @aarthificial
      @aarthificial  3 роки тому +4

      Thanks!
      Now as I think of it, it could've been subconsciously inspired by Rust. I haven't used it in quite some time but I was always inspired by the way it handles nulls (by just not having them lmao)

  • @ZiplawDev
    @ZiplawDev 3 роки тому +2

    love it :)

  • @itsME-dc4vm
    @itsME-dc4vm 3 роки тому +3

    Nice ;D

  • @KingGurke98
    @KingGurke98 3 роки тому

    I... don't really understand what you're talking about. But I'm sure I'll run into that problem you solved here, so this video gets saved ^^

  • @AngelofD000M
    @AngelofD000M 2 роки тому

    You actually can make floats & int's null! All you do is:
    int? _integerValue;
    float? _floatValue;
    The question mark makes it null until a number is put in!

    • @aarthificial
      @aarthificial  2 роки тому +1

      Nullable value types:
      1. Don't work for references
      2. The Unity inspector doesn't support them

  • @His-Games
    @His-Games 2 роки тому

    and you can do an implicit operators to automatically convert other values into one as well!

  • @DaydreamStudios_Official
    @DaydreamStudios_Official 2 роки тому

    Editor Scripts Are Great Too :D

  • @paulblart7378
    @paulblart7378 11 місяців тому +1

    I’ve never seen anyone or anything say that a comparison to null is expensive. I’m pretty certain that “null” just refers to a special value meaning the variable refers to nothing. Is a null check not just a simple comparison of two values? I would really appreciate a deeper explanation.

    • @aarthificial
      @aarthificial  11 місяців тому +1

      Unity is actually a C++ engine and the objects we operate on in C# are just handles for the underlying C++ objects.
      The base Object class in C# overrides its Equals method so that if we compare it to null, it checks if that underlying C++ object still exists. This is done because the C++ object might have already been destroyed even though the garbage collector hasn't yet cleaned up its C# representation. So checking for null would return false even though the object is gone.
      This check requires an external call from the manage side (C#) to the unmanaged side (C++) which comes with a cost.
      In Unity, it's a rule of thumb to avoid these external calls in the hotpath.

    • @paulblart7378
      @paulblart7378 11 місяців тому +1

      @@aarthificial Thank you! Interesting how the majority of youtube channels don't mention this and perform many null checks in Update(), myself included. I've never noticed any performance issues from it, so it surely can't be horribly expensive, but thanks for the clarification.

  •  3 роки тому +1

    I don't get it. What happens if you use an optional Transform target, but target is empty in the editor and enabled is true? You'd have to check for null anyways...

    • @aarthificial
      @aarthificial  3 роки тому +1

      It depends on your approach
      For example, I never check for null if a field is required for the script to operate. The error thrown is clear enough to inform the designer what field is missing.
      In this situation enabling the checkbox is like making the field required
      In other words it's more about showing the intent that the field should be empty, rather than protecting the code from all possible values

  • @bertrandguay-paquet3120
    @bertrandguay-paquet3120 3 роки тому +1

    Hey cool trick! Be careful with using this at scale though because you're trading a single value with a value + a bool which increases memory usage. This will also generate a lot of generic classes that can bloat memory usage and code size.

    • @aarthificial
      @aarthificial  3 роки тому

      Thanks!
      That's an interesting point. Do you happen to know how much memory a struct definition takes up? I've never faced a situation where the number of types in an application would be a bottleneck

    • @bertrandguay-paquet3120
      @bertrandguay-paquet3120 3 роки тому +1

      @@aarthificial It varies with Unity versions, whether C# reflection is used with the generic class/struct and other factors so I can't give an actual number. I'm not saying don't use generics at all because they're very useful, but keep in mind that using generics has a runtime cost.

  • @sonictrent
    @sonictrent 3 роки тому +1

    When applying the property drawer anything else that uses the namespace of editor gives me errors, and without it the property won't show up in the inspector. I'd really like to be able to use this to improve workflow, I hope it's an easy fix.

    • @aarthificial
      @aarthificial  3 роки тому

      Sorry if it's obvious but did you put the OptionalPropertyDrawer file in "Assets/Editor" ?
      If that doesn't work you can try changing the name of the namespace or removing it entirely since it's not required

    • @sonictrent
      @sonictrent 3 роки тому +1

      @@aarthificial I have tried removing the namespace and having it in the right folder, I'm really confused why it doesn't work... I'll attempt it with a new project and see what happens, thank you for the reply by the way!

  • @chris.davidoff
    @chris.davidoff 3 роки тому +1

    So I was thinking about this more.. Most null checks in an update loop are to safe guard against the object in question being destroyed..
    Can you think of a way that 'enabled' could be set to false upon its GameObject being destroyed? (assuming `T` is a GameObject). I wish Unity provided an OnDestroy event instead of a message
    also, I tried replying with the link to the unity blog but it keeps getting removed

    • @aarthificial
      @aarthificial  3 роки тому

      Oki, I was able to find the blog. Had no idea I was mentioned there
      Anyway, this pattern is more suited for editor-time configuration where things don't really change at runtime. That's why the struct is read-only. You can see at 2:22 that I mostly use it for value types and/or SOs.
      When it comes to GameObjects with unpredictable lifespans, probably something different should be used.
      Also, totally agree, an OnDestroy event would be great!

    • @chris.davidoff
      @chris.davidoff 3 роки тому +1

      @@aarthificial I made an attempt this. Unfortunately, it relies on the user calling Initialize in an awake method, but otherwise it works! Let me know what you think
      github.com/RockyGitHub/OptionalT

    • @aarthificial
      @aarthificial  3 роки тому

      Interesting idea!
      I think you could implement ISerializationCallbackReceiver and use the OnAfterDeserialize() method instead of calling initialize manually.
      Also, before adding the OnDestroyInvokeEvent component, I'd check if it already exists cuz there may be a situation where two structs reference the same object

    • @chris.davidoff
      @chris.davidoff 3 роки тому +1

      @@aarthificial It appears that you can't access a "gameobject" in that callback, dang! That would have been sweet

  • @cil7ea
    @cil7ea 2 роки тому

    I'm super new to C# and unity as I come from a different programming background.
    I know that C# 8 already has a nullable type... doesn't this mostly redefined that functionality?
    I understand that System.Nullable is not serializable, but isn't there a way to fix that?
    Why is comparing to null slower than comparing to "enabled"?
    Is there a way to force the language to use the same syntactic sugar of float? instead of Optional like maybe some operator overloading?

    • @aarthificial
      @aarthificial  2 роки тому

      1) "C# 8 already has a nullable type... doesn't this mostly redefined that functionality?"
      Kinda. In C# 8 only value types can be nullable. Here the main idea is to use this with both references and values.
      2) "System.Nullable is not serializable, but isn't there a way to fix that?"
      To the best of my knowledge, there is not. Maybe if we could apply field-targeted attributes to it. But nothing I'd know of.
      3) "Why is comparing to null slower than comparing to "enabled"?"
      Unity is a C++ engine. Any C# object deriving from "Object" is just a reference to the underlying C++ object. Null comparison is overloaded to check if this native object still exists. This requires a transition from managed code (C#) to C++ which is said to be much slower than normal comparison. I don't know exactly why it is but I trust Unity on that one.
      4) "Is there a way to force the language to use the same syntactic sugar of float?"
      I don't think so, especially since you're talking about a declaration, not an operator. But I'm more than glad to be corrected

    • @cil7ea
      @cil7ea 2 роки тому

      @@aarthificial Wow, thanks for taking the time to give such a detailed answer.
      I was asking all this since in Swift (i'm coming from an iOS background) the "native" Optional type is an enum similar to how you implemented it but in Swift both value and reference types can be nullable.
      I wasn't quite aware of this C++ layer that might slow things down. I did read somewhere that sometime ago unity switched from mono to il2cpp but I haven't really found a detailed explanation of what that means. Most tutorials online just focus on the making of the game. I would love to learn about the intricacies of how unity actually works under the hood.
      Anyway, I love your videos! Super inspiring work. You make me wanna make things and learn stuff. That's an incredible impact that you have on people! You just made me become a proud patron :D
      Would you know by any chance any quality resources that might tackle some more advanced subjects about unity/C#/shaders (that don't cover how a for loop works)?
      Also, where would you think it's the best place to ask unity related questions? Unity forum, stackoverflow, some (your) discord group?

    • @aarthificial
      @aarthificial  2 роки тому +1

      Thanks so much for the support!
      Unfortunately, I don't have any particular resources to recommend. Unite talks are definitely worth watching though, especially the ones by Ian Dundore.
      I think Unity forum would be the best place.

    • @realtimberstalker
      @realtimberstalker 2 роки тому

      @@aarthificial There might be a way to add some syntactic sugar. I didnt see you add it in the video, but its been over a year so you may have added it already, you can define custom implicit conversions. That way, when passing it to a function, you wouldn't have do .value. It would also let you store a value into a Optional variable without needing to call a constructor.

  • @FranciscoTChavez
    @FranciscoTChavez 3 роки тому +1

    This is really cool, but I have to wonder which one adds more runtime overhead:
    - Checking for null
    - Creating two property wrappers that causes two additional (and separate) stack allocation and de-allocations. After all, property wrappers are just functions, and every function call will do a stack allocation, followed by a jump to the function's code, followed by a stack-deallocation, followed by jumping back to code in the calling function. There's a reason why the performance experts at Unity suggest using fields instead of properties in your release code.
    So, if anyone were to take the time to do a performance analysis on this bit of code, I would be interested in reading the results. Besides, in my limited experience in doing performance analysis, I found that calling a function as a method parameter is a bit of a performance killer in the .NET Framework compiler when set to generating release code. I know that Unity rolls out their own compiler, so their complier(s) might be ok with passing a method call as a parameter to another method call.

    • @aarthificial
      @aarthificial  3 роки тому +1

      Sorry if it's obvious but it's not about just checking for null. Unity objects override the equality operator and perform a transition from managed code to C++ (to check if the wrapped object still exists) which is why it's a best practice to not use them in the hot path

    • @FranciscoTChavez
      @FranciscoTChavez 3 роки тому

      @@aarthificial I'm not saying that there isn't additional overhead to checking for null in Unity. I just didn't think I had to say that because you already mentioned it in your video. Besides, it's not that unusual to override equals equals operator in C#. This is an elegant pattern, but I would like to see some metrics that actually show that it's faster. I would like to see the metrics for the Mono builds and the metrics for the IL2CPP builds.
      I've seen some weird things with C# over the years that are counter intuitive to what I learned when I studied Computer Science. Last week I learned about the website called leetcode. They were kind enough to provide Runtime and Memory use metrics, so I decided to play around with the code in my solution. When I removed my temp variables and just used reused the input parameters, memory use went up by about 0.7 MB. And, the difference was not linear.
      With 2 temp variables, I used 28.1MB. With 1 temp variable, I used 28.7 MB.
      With 0 temp variables, I used 28.8 MB.
      It doesn't make sense, but adding variables to the stack, decreased my total memory usage.
      Sometimes, the thing you expect to execute faster, based on study and knowledge of how code executes, ends up being slower. I don't know if doing a null check is faster or slower, but I would like to know. But, in order to know, I would need to see some metrics to back it up.

    • @aarthificial
      @aarthificial  3 роки тому

      @@FranciscoTChavez I suspected that much. I just didn't like how biased your comment was. Without, for example, mentioning the stack allocations that would be caused by the overloaded operator. So I wanted to clarify for anybody reading it

  • @lilSapphireFox
    @lilSapphireFox 3 роки тому

    Did they add the functionality to display a class with an generic class in 2020? bc i cant use it like this in 2019.4 or am i doing sth wrong?

    • @aarthificial
      @aarthificial  3 роки тому

      Yes, unfortunately, generics require 2020.1+

  • @decidev
    @decidev 3 роки тому +1

    Have you thought about making a discord server? It would definitely benefit your channel and allow people to talk to you without having to use UA-cam comments

    • @aarthificial
      @aarthificial  3 роки тому

      See, my crippling anxiety doesn't like the idea of people talking to me directly. I wanna try to overcome my fear at some point, just not today

    • @decidev
      @decidev 3 роки тому +1

      @@aarthificial That's completely understandable. I'm always to scared to talk when I join a discord server so someone else has to initiate the conversation lol. But yeah I understand

  • @CaseyHofland
    @CaseyHofland Рік тому

    I see a lot of lofty comments but I would like to point out as a dev myself that these quality of life structs do unnecessarily complicate code. In the distance example, you could (and in my opinion should) have simply said that a value of “0” means “unlimited”, which is pretty standard practice.
    The reason the struct is a bit frowned upon in this example is because you cannot as easily use your float in extensions and methods, because it’s a field of a struct. you can’t modify the field, you have to modify the struct now. These are all considerations you need to make when using the Optional struct: it has its uses of course but they cannot be too overhyped lest we regret our choices later.
    In these cases a “DisableLimit() => distanceLimit = 0f;” which some code to check for 0 is better practice in my opinion.
    Man here I go, I love your videos and the first comment I decide to write is nitpicking your code, I’m sorry I don’t choose to be this way :’)

    • @CaseyHofland
      @CaseyHofland Рік тому

      You also “hide” your float inside a wrapper, making other developers anxious as to “what is going on inside that wrapper?!” Simple is often best, if for its clear communication alone.

    • @aarthificial
      @aarthificial  Рік тому

      It's meant for configuring things via the inspector. Editing a serializable MonoBehaviour field in the code is a really questionable practice.
      Still, value types were just an additional benefit, besides the main point of the video.

  • @crash1998100
    @crash1998100 3 роки тому

    Have you actually benchmarked if it is faster?

  • @last8exile
    @last8exile 3 роки тому

    Unity 2020.1+ needed to serialization of generics

  • @hetigamedev1818
    @hetigamedev1818 3 роки тому

    Shouldn't you constrain T to something Unity can serialize?

  • @watercat1248
    @watercat1248 3 роки тому

    after wach this video im sure the need to learn how to use the editor scripts

  • @alexjawny8705
    @alexjawny8705 2 роки тому

  • @marijnstapert9036
    @marijnstapert9036 3 роки тому +2

    C# doesnt have an optional type? Just curious java has one an JS has undefined

    • @aarthificial
      @aarthificial  3 роки тому +1

      Since C# 8 we have nullable references (denoted via "?") which serve a similar purpose but I'm not sure how they would cooperate with Unity.
      In terms of a traditional optional container then unfortunately no, there's no default implementation in C#

    •  3 роки тому +1

      @@aarthificial Spolier: the won't cooperate. If you check against nullable references you'll get null unity objects that evaluate as non-null.
      twitter.com/VehiclePhysics/status/1348641386573271040

    • @aarthificial
      @aarthificial  3 роки тому

      @Edy García Oh, yeah, that's for sure. I was thinking more about the nullable aware context and how Unity's serializer would interpret something like:
      [SerializeField] private MonoBehaviour? mb;
      That is, if they're even gonna support that

  • @ninosandlarz783
    @ninosandlarz783 Рік тому

    For those who tried to use optional variables in lists and encountered problems with indentation, you should reset the indentation to 0 as below:
    EditorGUI.EndDisabledGroup();
    EditorGUI.indentLevel = 0; //add this line
    position.x += position.width + 24;