Code Smells - 'Switches' in Unity3d / C# Architecture

แชร์
ฝัง
  • เผยแพร่เมื่อ 1 ต.ค. 2024
  • Check out the Course: bit.ly/3i7lLtH
    -------
    Want cleaner code? Keep an eye out for code smells in your unity3d project and c# code.. We'll talk about why switch statements can indicate architectural issues in your code, how to tell if they're actually an issue, and some alternatives to make your unity project and code architecture a bit cleaner and more maintainable.
    More Info: unity3d.college
    Join the Group: unity3d.group
    Patreon: / unity3dcollege

ความคิดเห็น • 213

  • @albingrahn5576
    @albingrahn5576 4 ปีที่แล้ว +48

    "spell" doesn't even sound like a real word anymore lol. great video though, i'm currently making a game where i plan to have a lot of spells/abilities and i would 100% have fallen into the switch statement trap if it weren't for this video, so thank you!

    • @GameDevNerd
      @GameDevNerd 3 ปีที่แล้ว +4

      How's your game project coming along? I wanted to let you in on a little enum secret that you may already know but if not it can help a lot. Suppose you have a spell that fits with two or more SpellTypes ... suppose it increases the target's armor or attack abilities but drains health or something. So what enum do you use? SpellType.Damage or SpellType.Ability? Or do you have to create a whole new one called SpellType.DamageWithAbility? That will get really messy, won't it? Luckily, .NET has the [Flags] attribute to stick on enums where you can combine values and check them like bit flags:
      [Flags] public enum SpellType {
      Default = 0x00,
      Damage = 0x01,
      Heal = 0x02,
      Ability = 0x04
      };
      You combine Flags with simple bitwise operations, in our case just like this:
      var specialType = SpellType.Damage | SpellType.Ability;
      Now it serves as both a damage and ability spell type so other code can read the Flags and see what it's for.
      In case you're wondering what is this 0x00 and 0x02 weirdness that's just hexadecimal notation for the numbers 0, 2 and 4 ... 0x0A would be 10, 0x0F would be 15 and 0x10 would be 16, hex is just simple base 16 numbers instead of base 10 (decimal) or base 2 (binary) or base 8 (octal). I use the hex notation so I immediately am reminded when looking at my code that I'm dealing with a bit flags enumerator. The reason for using the powers of 2 for each value constant is so there is no overlap in the bits. Those same numbers in binary would look like:
      00000001, 00000010 and 00000100 ...
      So if the first bit is toggled it has a Damage effect, if the second bit is toggled it has Healing effects and if the third bit is toggled it has an Ability effect! 00000111 has all three! It's a pretty handy little trick for complex enums that may be some combination of something. 😄

  • @TheThormond
    @TheThormond 5 ปีที่แล้ว +37

    Imo this abstraction is severely limiting. I would imagine in MOBA's/MMOs (or any other genres with complex mechanics) spells would have tags, not types. For example: what if you wanted to have a spell that would root an enemy and drain his life over 3 second period? That would be a root, a damaging spell and a heal (since you're draining) at the same time.
    Another set of problems comes from the fact that root also exists in some kind of hierarchy of Crowd Control effects, so you would have to group it up with into one category with Stun, Knockdown, Silence, etc (also how do those effects interact together, can a target be rooted and knocked down a the same time or is it on the same debuff slot? which one takes priority if its the same debuff slot?). Then the tag would have to be #CrowdControl, not #Root and you end up in a situation where it would be much better to avoid tags altogether and just do an Object approach.
    The way I'm usually doing it is having spells be "buckets" for effects that derive from some abstract Effect class. You would have a Spell class that has an array Effect[] and you would populate it with descendants of class Effect through polymorphism. Probably something along the lines of "Action" would be a better name for it since you could also apply it to attacks, character emotes and anything that actively launches some series of effects. Then at some point you would want to be able to launch those "procs" at certain animation thresholds (for example attack would apply damage at 0.4 animation point) so I guess that's where you want your structure with effects be called "Ability" (so you end up with Ability that inherits from Action and allows you to pick animation thresholds for each of the assigned Effects). Effects would be resolved independently by their respective effect processor (which most likely derives from an abstract EffectProcessor class).

    • @gigajoules6636
      @gigajoules6636 5 ปีที่แล้ว

      You create a new type for this combined behaviour and implement multiple health changes within method that actually implements damage in the concrete class

    • @TheThormond
      @TheThormond 5 ปีที่แล้ว +17

      @@gigajoules6636 and you will end up having to to hard-code every single ability which will cause massive overhead to prototyping. What im suggesting is that you keep Effects as seperate scriptable objects and attach them to Abilitiy scriptable objects (or whatever data structure you're using, as long as it has an editor interface accessable to the designers) and just have a system resolve the Effects as they are procced.

    • @shenlong3879
      @shenlong3879 4 ปีที่แล้ว +11

      @@TheThormond As sad as it may sound a lot of established developers work that way because they just don't know any better.
      I think that's where an Entity Component System can work wonders. Generalizing and reusing small components and combining them for different effects.
      Also I honestly hate the idea of games differentiating what I can use offensive of defensive spells on etc. I have the spell, I want to cast it on something, let me deal with the consequences and don't artificially limit me.

    • @1i1x
      @1i1x 4 ปีที่แล้ว +1

      Know any advanced tutorial channels like for MMO? I think this guy is just throwing something in there to make a video, not professional.

    • @TheThormond
      @TheThormond 4 ปีที่แล้ว +7

      @@1i1x I don't think there are. People who know state-of-the-art MMO architecture usually don't make TH-cam content, they are too busy working on their MMO projects. In addition TH-cam creators are biased towards solutions that enable fast prototyping which allow you to produce content fast and maintain viewerbase so I wouldn't count on their expertise in creating large code bases that can be extended and maintained for years, that requires a completely different mindset. Udemy/Skillshare is a better bet but I don't think you can find what you want there either.

  • @charliemalmqvist1152
    @charliemalmqvist1152 5 ปีที่แล้ว +21

    This wasn't en example on why switch statements are bad, but an example on bad and unsafe code.

    • @duramirez
      @duramirez 5 ปีที่แล้ว +10

      I agree 100% switch is not the issue here it's just bad algorithm.

  • @sagiziv927
    @sagiziv927 5 ปีที่แล้ว +46

    I understand what you are trying to accomplish, but I think that this way (reflection) is not that good for it. What if your spells needed to get some unique parameters (e.g. how much mana this spell consumes from the character) it would be impossible to pass the value in the reflection. That's why I think that implementing it with ScriptableObjects is way more comfortable because then you can assign this values in the Inspector.
    And then put them inside a dictionary like you did.
    What do you think¿

    • @legendaryitem2815
      @legendaryitem2815 5 ปีที่แล้ว

      I agree, that's a good option. Also, if you don't have any extra functionality (e.g the class just holds the spell's data) and you don't need to alter any of the information for the spell (e.g you set the parameters in the editor and don't change them at run time) then perhaps consider using a Struct to hold the spell information as it creates less overhead/garbage collection as they are passed by value rather than by reference.

    • @caliche2000
      @caliche2000 5 ปีที่แล้ว

      In order for that to happen you add an abstract method or property to the abstract class and it HAS to be implemented by each class inheriting from the spell abstract class.

    • @Sylfa
      @Sylfa 4 ปีที่แล้ว

      @@legendaryitem2815 Struct wouldn't help here, the dictionary is a reference type and would box the struct's up. In fact, you can't have an abstract struct so you would have to use an interface as a base and implement it in each struct.
      And then every time you did a lookup in the dictionary you would first unbox the struct, putting it as a copy on the stack, before you call into it's methods.
      It wouldn't even do much good if you remove the methods and put them in the SpellProcessor, first your breaking encapsulation. Secondly your recreating the switches. It's better since they are all in the same class, but still a code smell. And third your only saving a single pointer lookup.
      Since your never recreating these objects they will go into the old generation memory rather swiftly, there they will sit, not really effecting garbage collections much. You never recreate them since they are reference types, only passing the pointer along. And in the end your only doing 2 method calls per spell cast, that's 2 pointer follows, and not per frame but per player input which is much slower.

    • @Sylfa
      @Sylfa 4 ปีที่แล้ว

      "it would be impossible to pass the value in the reflection."
      Doing it this way each class describes that spell entirely, so if you have a resource usage or cooldown you would implement that either in ProcessOnCharacter (ie add caster to it and then manipulate the caster as well), or you would add an abstract property like @Carlos_Maya said.
      In that case you force each spell to record how much mana it uses, or how long it's cooldown is.
      I happen to think ScriptableObject is slightly better in this case, it allows you to drop the enum, making it so you only need to change 1 source file to add a new spell, or creating a new object for making a variation. The only time it feels awkward is if your not exposing any properties at all on your spells, making it so you only ever create a single ScriptableObject.
      This way you avoid the SpellProcessor lookup mechanic, and instead use the Unity editor like an inversion of control dependency injector which is a very nice feature on it's own, even if the database being files is a bit awkward at times.

  • @Kormelev
    @Kormelev 4 ปีที่แล้ว +7

    How about a code smells for when you check what scene is loaded in your code?

  • @georgimihalkov9678
    @georgimihalkov9678 4 ปีที่แล้ว +3

    Reflection? Wouldn't it be a performance hit? Wouldn't it be easier if we at least cached the needed methods as delegates at runtime and just invoke them when needed?

    • @georgimihalkov9678
      @georgimihalkov9678 3 ปีที่แล้ว

      @Andre SCOS absolutely! I wanted to point out that reflection is not needed in this case.

    • @georgimihalkov9678
      @georgimihalkov9678 3 ปีที่แล้ว

      @Andre SCOS reflection should be used sparingly.

    • @georgimihalkov9678
      @georgimihalkov9678 3 ปีที่แล้ว

      @Andre SCOS ofc depends on the use case, but I prefer getting the job done without reflection if possible. Reflection can be up to 600 times slower than normal code invocation (it is more optimized in .NET 5/ Core). I agree that in this example you execute it one so it won't impact the performance.

  • @DeZmanStudios
    @DeZmanStudios 3 ปีที่แล้ว +1

    In the "problematic case" there is nothing wrong with the switch statements. The problem you presented is the programmer not knowing how spell validation works. You can replace switches with anything but the programmer might just forget fo fix the validation function and the same will happen. Nothing wrong with having "actions" and "conditions" defined separately in source code, but the programmer needs to know about it. Your reimplementation of spells as classes with a validation method ("action" and "condition" defined in the same space in source code) is also completely fine an to me more preferable (less jumping aroud code) and is in the spirit of OOP.

  • @ZnelArtsGames
    @ZnelArtsGames 4 ปีที่แล้ว +3

    Interesting, is an improvement over a switch but I would argue that using reflection is a code smell by itself, for this is more appropriate the Adapter Pattern.

  • @moofymoo
    @moofymoo 4 ปีที่แล้ว +1

    tl;dr - hide branching with reflection, because switch are bad..
    I like switch, because it is simple and clear way to shows where execution flow splits in multiple branches. If code is a mess, then problem is anything else but not a switch keyword. Problem could be bloated scope, unnecessary branching, mixed responsibilities, naming, and ..unnecessary abstractions because architecture and design patterns.

  • @Fralleee
    @Fralleee 5 ปีที่แล้ว +6

    The whole initialization seems unecessary.
    Why not have the processor accept a "Spell" as parameter.
    This solution can't handle if you have multiple spells that are of the same spell type?

  • @SulixD
    @SulixD 3 ปีที่แล้ว +1

    The only reason you are saying switch is bad is because of the bad design presented here. You could very well have just one single big switch take care of everything instead of breaking it down into other methods / classes and the spell itself could take care of the isvalidtarget and so on .... I really see no reason to give swtich a bad rep because of this example.
    You complain about switch and then go ahead and implement a singleton to take care of spells.... go figure...

  • @Krakkk
    @Krakkk 5 ปีที่แล้ว +9

    Why won't you use ScriptleObjects for spells? Wouldn't it be cleaner?

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +13

      It depends on the use case. Been doing a lot of mmo stuff again lately and in games where the server is so authoritative and data is constantly being changed on the fly, automatically updating on a server, and populating out to clients, scriptable objects aren't nearly as useful. But I completely agree that in the vast majority of cases they're a great way to store the data as well.

    • @Krakkk
      @Krakkk 5 ปีที่แล้ว

      @@Unity3dCollege Thanks. I didn't think about it.

    • @ravanin
      @ravanin 5 ปีที่แล้ว +7

      @@Unity3dCollege given how many people are suggesting scriptable objects, maybe could you release an addendum video wit scriptable objects?

  • @7th_CAV_Trooper
    @7th_CAV_Trooper 2 ปีที่แล้ว +4

    I've watched a couple of Jason's videos now and this one earned my sub. Pretty solid explanation of the switch anti-pattern and how to solve it with abstraction. I see junior devs write rule-based code with switches all the time. Pro tip: action maps > conditional statements.
    at 17:25 - "we cast it as a spell" lol. nice
    U can create a static constructor instead of having an Initialize method.

  • @hellangel28
    @hellangel28 2 ปีที่แล้ว +1

    The title is very misleading, switches are NOT a code smell perse, misuse of proper structures is the issue here, switches itself are completely fine.

  • @БогданАльфавіцький-д3к
    @БогданАльфавіцький-д3к 5 ปีที่แล้ว +33

    How switch statement can be a bigger smell than damn reflection?
    Most of the time ScriptableObjects is the way to go.
    Sometimes switch can be the only option.
    But reflection? This is an absolute NO for me.

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +4

      I'm curious why? What is it about the reflection that you're concerned about?

    • @jaxowaraxa
      @jaxowaraxa 5 ปีที่แล้ว +2

      @@Unity3dCollege I think one of two:
      1 performance issues
      2 he doesn't understand reflection

    • @БогданАльфавіцький-д3к
      @БогданАльфавіцький-д3к 5 ปีที่แล้ว +13

      @@Unity3dCollege As was said before by others, it's unstable. From platform to platform it may break.
      It's not an intended way to write game logic. For testing, custom editors, tools? Yes. For actual game code? No.
      And, it's hard to debug.
      There are a lot of cases where it can be used. But in my opinion Unity has tools to avoid using it.

    • @KenderArg
      @KenderArg 5 ปีที่แล้ว +10

      @@Unity3dCollege As rule of thumb never use reflection on your own code. It is for finding out things at runtime that cannot be known at compiletime. Ideal for plugins, mods, editor tools and other uses of 3rd party assemblies.
      In this case it's being used to find out about code in the same assembly. This does not need to be done at runtime and can easily be done with a serialized field from unity, constructing a SpellType => new Spell() dictionary in an initializer or even a lazy instantiation mechanism.
      In your head replace the word "reflection" with "decompiling" or "reverse engineering" to get a better sense of when its use is appropriate.
      Practical issue: you're hiding the relationship between Spells and SpellProcessor from your IDE. All that static analysis that you were so happy about in your Rider video goes out the window where reflection is involved.
      And as others have said it makes debugging a lot harder. One of the main reasons reflection is heavily used in code obfuscators.

    • @JayAnAm
      @JayAnAm 5 ปีที่แล้ว +1

      @@Unity3dCollege The main reasons are: Hard to find bugs, debugging can be a pain and you can run into performance issues easily.

  • @madmanga64
    @madmanga64 3 ปีที่แล้ว +5

    Your channel is underrated

  • @ilhameffendi2737
    @ilhameffendi2737 5 ปีที่แล้ว +4

    this is exactly my mistake when developing a shoot 'em up game. I added a switch condition every time there's a new weapon. but it was a long time ago. the game is already on steam for 2 years now. xD

  • @andurilan
    @andurilan 5 ปีที่แล้ว +2

    They are only a code smell for people who skim code. Of course a 15 branch switch statement is badly written code, but it would only get worse with if/elseif/else... that would be 3 times the code.

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +1

      if/elseif's would definitely be way worse, no disagreement there. But I'm strongly in a mode of classes over conditionals right now and feel like most of the time the code will be easier to understand w/ more small classes and less switches/if's... (at least that's how i feel today lol, we'll see how the future is)

  • @andylinkOFFICIAL
    @andylinkOFFICIAL ปีที่แล้ว +1

    switches aren't code smells they are more performant than polymorphic approaches. and reflection is pretty slow so there is some overhead, unless you start caching / pooling.

    • @Unity3dCollege
      @Unity3dCollege  ปีที่แล้ว +1

      Anything found via reflection should definitely be cached. And switches themselves aren't always bad, but there are a lot of scenarios where they become a maintenance nightmare. Definitely want to redo this video soon though to clarify it a bit more

  • @TheSpacecraftX
    @TheSpacecraftX 4 ปีที่แล้ว +3

    Saw the title and looked nervously at the 2 Switches I used today for a 2D puzzle game.
    Determined my use case is absolutely fine though because my enum is fixed size of 3. AllignToInput, AllignToOutput, and None.

  • @guangzhixiao9638
    @guangzhixiao9638 5 ปีที่แล้ว +3

    So this guy's video argument against switches is:
    1) setup 2 switch control flows, one in IsValidTargetForSpellType() and another in UseSpellOnOtherCharacter().
    2) add a new switch case in UseSpellOnOtherCharacter() ".ChangeModel"
    3) forget to add the same switch case handling in IsValidTargetForSpellType()
    4) run the sample game and tell viewers that this is a hard to debug issue since we have added the code for ChangeModel but nothing is happening.
    I get that we are seeing this issue in hindsight so maybe this is a case of "hindsight is 20/20".
    However, this can also be avoid through the proper coding practice of adding debug print statements in control flow statements. For example:
    - adding debug print statements to failure cases like in default switch case handling in IsValidTargetForSpellType()
    - adding debug print statements under the "if (isValidTarget == false)" in UseSpellOnOtherCharacter()
    This way, if something unexpected happens, you can take a glance at Unity's message console and figure out the logic flow instead of just wondering "why did my change not occur".
    You don't have to build a strawman against switch statements just to teach reflection and claim it's code smell in general when you can use them properly.
    EDIT:
    I do agree that if you have many related functions (say casting spells in games) do their own switch statement checks it's code smell compared to creating abstract Spell class and have each derived class of Spell implement their own "isValid" and "doSpell". However, whether you use switch or long "if-else if-else" control flows, I think the key code smell is not switch statement usage but writing similar functionality (checking spell types) in many different functions. Perhaps this is just simple click-baiting and I fell for it for more views then. Ah well.

  • @DevVader
    @DevVader 4 ปีที่แล้ว +3

    Tbh I would have used a strategy pattern in conjunction with a factory pattern to achieve this. Easier to read and debug!

  • @behzadbakhti
    @behzadbakhti 5 ปีที่แล้ว +4

    Thanks for your video, I would like to learn about events , delegates, and specially callbacks in unity way, that would be great if you make some in-depth video about this stuff.

  • @mattmurphy7030
    @mattmurphy7030 ปีที่แล้ว +1

    Code smell videos are a code smell

  • @r1pfake521
    @r1pfake521 5 ปีที่แล้ว +4

    The SpellType enum is usless after the refactoring, because the spell class (type) itself can be used instead.
    And you can just use a static constructor for initialize instead of the additional bool.

    • @cuzsleepisthecousinofdeath
      @cuzsleepisthecousinofdeath 5 ปีที่แล้ว +1

      Well, we can make a Dictionary, keep the enum, and get rid of the various inherited classes.
      Because why classes, if we only need one method to be executed.

    • @cuzsleepisthecousinofdeath
      @cuzsleepisthecousinofdeath 5 ปีที่แล้ว

      (Or maybe i'm not exatcly right, since i have a bias against very contrived examples, that's why i wasn't following the vid second to second)

  • @andremilanimartin3338
    @andremilanimartin3338 4 ปีที่แล้ว +2

    didnt quite get why switch would be bad, but learned a whole new bunch of editor short cuts and programming methods, so thanks anyway

  • @khaa5zz81
    @khaa5zz81 3 ปีที่แล้ว +1

    after watching this. i just realized how terrible as a programmer i am.

    • @robertmcdonell831
      @robertmcdonell831 3 ปีที่แล้ว

      You too? 🤣 I though I was the only one

  • @duramirez
    @duramirez 5 ปีที่แล้ว +7

    I would never code like that, who does that? it's not switch fault, it's your logic and algorithm that is the issue.
    Sometimes you try so much to separate stuff that just should not be splitted up.
    What i would do is put the validation inside the methods created in the target object, respecting the responsibility principle that dictates that the Class should know about itself, so each method in this class ModifyHealth(), etc, etc, should be able to tell if this particular Instance is a valid target... putting validation about a Class outside the class is bad practice (SpellTargetChecker)

    • @StephenWebb1980
      @StephenWebb1980 4 ปีที่แล้ว

      So you'd rather have an object validate itself? Nah. It's a bad practice for an object to tell the rest of a system that IT, a part or product of the system, is valid. The system or another abstract part of the system sets the rules for checking if the system's objects are valid. This way you are programming, in one place, a system's validation of its parts and products otherwise you're breaking the DRY principle.

    • @duramirez
      @duramirez 4 ปีที่แล้ว

      @@StephenWebb1980 What does that have to do with switches? that's architecture design, don't change the subject. But since you want to criticize on my architecture decisions, ok fine, do whatever you want, this is how i and pretty much the rest of the world do it, so keep up with your DRY. No bad practices in any of our architectures, they are just different architecture, so stop with the "nah" it's up to you how you want the system to behave, nothing is set on stone, it's not just because i chose a different approach that makes it invalid, both approaches are vetted around the world, just pick your poison.

  • @MalrickEQ2
    @MalrickEQ2 3 ปีที่แล้ว +1

    Why would you avoid switches? They are one of the most performant things in coding.

    • @petrusion2827
      @petrusion2827 3 ปีที่แล้ว

      They're great, but sometimes you can do even better, especially with something as low level as enums and bools.
      The switch at 8:36 could've even been done with no branching:
      return
      (spellType == SpellType.Heal && caster.IsPlayer == target.IsPlayer) ||
      ((spellType == SpellType.Damage || spellType == SpellType.Root) && caster.IsPlayer != target.IsPlayer) ||
      (spellType == SpellType.ChangeModel && caster == target);

    • @isawadelapradera6490
      @isawadelapradera6490 3 ปีที่แล้ว

      Thing is not that switches are bad. They are possitively great specially if used adecuately.
      Thing is if you find yourself with a conditional tree with so many different cases, you _probably_ made a gross structuring mistake earlier on.

  • @jadefae
    @jadefae 2 ปีที่แล้ว

    Ah shit... I thought switches were supposed to be GOOD. Thanks for the reality check lol

  • @kutanarcanakgul5533
    @kutanarcanakgul5533 5 ปีที่แล้ว +3

    If we have so many animations on our character, it can be turning a mess for sometimes. How are we organize our animations?

  • @BehindTheFXTuts
    @BehindTheFXTuts 3 ปีที่แล้ว

    Can anyone tell me how i can get the "helper" in the script that says "Set By Unity" and "Scripting Component" etc...?

  • @StephenWebb1980
    @StephenWebb1980 5 ปีที่แล้ว +2

    switch is not a code smell for writing editors with guilayout.toolbars where the returned int is an index representing a subeditor...I use it all the time and it's so much cleaner than a bunch of if statements

    • @gigajoules6636
      @gigajoules6636 5 ปีที่แล้ว

      Likewise in C switches are often a best possible method for doing something.
      Doesn't seem to translate to higher level languages in the same way though.

    • @isawadelapradera6490
      @isawadelapradera6490 3 ปีที่แล้ว

      Code smells are not so because of the looks of the code on the screen...
      I just hope noone heeds this message as advice.

  • @RasTaIARI
    @RasTaIARI 3 ปีที่แล้ว

    Why even keep the enum instead of working directly with the System.Type Objects of the class directly? The Mapping from enum to subtype and therefore the whole SpellProcessor Class doesn't seem necessary to me ;)

  • @brendonlantz5972
    @brendonlantz5972 2 ปีที่แล้ว

    It's a good point, but the core issue is not the switch statement - it's using the switch statement for a growing list of cases. Switch statements used with a small enumeration that is not going to grow large like (CameraMode - first person, third person) is not a real issue. The key is good judgement in how you use switch statements and abstraction.

  • @mattCap42
    @mattCap42 ปีที่แล้ว

    Thanks for this. I definitely seem to be leaning too hard on switches. I have a loot manager and spell manager that are perfect use cases for this solution! I just finished scripting a boss fight where the boss stops fighting and summons adds at 25% health intervals. I have a method called at each of the three stage transitions with a switch that checks the fight stage and sends the stage appropriate array of adds to a spawn manager. This is a very narrow case and wouldn't be referenced anywhere else in the code. does this pass the smell test? or do you think it best I find another way to do it?

  • @ravanin
    @ravanin 5 ปีที่แล้ว +2

    Can you please take a more in depht look at statics in a video??

  • @cavalfou
    @cavalfou ปีที่แล้ว

    spellType Enum get rapidly confused with the Class Type of the spells, but your point is well proven.

  • @carlosvega4795
    @carlosvega4795 3 ปีที่แล้ว +1

    I'm trying to make a mechanic where the player can craft their own spells (depending on the knowledge the character has gathered and the character stats themselves, thus some spells might be suicidal/impossible/expensive for some characters but way too easy/spammable for more gifted ones), let's call it a spell-forge, the same way you craft items in other RPGs, difference being you can not loot spell knowledge, unless the player is perceptive and smart they can not reverse-engineer spells used by AI wizards. Anyways, point is: I coded all that using Scriptable Objects... I didn't know anythng about abstract stuff... But be sure I'll be using that now thanks!!

  • @alucardmikeg3
    @alucardmikeg3 4 ปีที่แล้ว

    Why do you use abstract classes even though you implements nothing inside it? Why not an interface?

  • @RyuDouro
    @RyuDouro 5 ปีที่แล้ว +5

    Why Switches can be a mess but these aren't? Say in a big scale. I fail to understand how switches can be a mess over anything.

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +1

      The biggest difference is the # of places you have to touch/modify to make a change or addition to the game. With switches it usually starts off being 1/2 places but in a bigger project you can quickly find it being a dozen or more spots that you have to add new cases to every time you add something or modify whenever you change functionality.
      When they're split into separate classes and encapsulated away, it leads to the functionality being in 1 place making it a lot safer and easier to modify/understand.

    • @RyuDouro
      @RyuDouro 5 ปีที่แล้ว +2

      @@Unity3dCollege in this specific example say I have a switch what spell the unit should do. I have entities with a variable int. Every turn each entity runs a code and have a switch for their action and reads that int. On the cases they call a fuction with the name of the spell. As I keep adding spells I only have to have the units with new ints, the switch case for those ints and they call the new fuction of the new spell.
      For your approach the only thing I save is putting a new case on the switch. Because by adding the spell to the unit, when running the game if the turn/AI picks that spell it automatically calls it (skipping having to put the case in the switch). Correct?

    • @Raymoclaus
      @Raymoclaus 5 ปีที่แล้ว +1

      @@RyuDouro I think for your case, you're in a situation where you haven't yet found a need for more switch statements. The video is targeted at projects that have found more than one place to use a switch statement for a type. You could still find yourself in need of expanding your spell system later (for example: verifying the target of your spells, like the video suggests), but changing your code now doesn't seem necessary until that happens.

    • @RyuDouro
      @RyuDouro 5 ปีที่แล้ว +1

      @@Raymoclaus I see but I'm trying to see if this could be useful for one of my projects. Right now the spell (function) checks if the entity still exists before applying the damage to it. This is because I have multiple spell types that have different multiple targets, aoe and also friendly target or multiple allies. Plus those targets must also be stored in another list because I need to keep track of who kills what, total damage and healing and also condition and healing over time even if the owner of the spell is already destroyed.

  • @The28studio
    @The28studio 5 ปีที่แล้ว +5

    This is great .
    Thanks Jason.

    • @leokamikkaze20
      @leokamikkaze20 5 ปีที่แล้ว

      Jason is great.
      Thanks this.

    • @Dxpress_
      @Dxpress_ 5 ปีที่แล้ว +2

      Jason is this.
      Great thanks.

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +1

      You're welcome! :) thx!

  • @nateHERE
    @nateHERE ปีที่แล้ว

    i cant see anything on the white code editor background xD

  • @pimpace
    @pimpace 5 ปีที่แล้ว +1

    Hey!
    You say all the time when you're debugging that can't hit F10 due to it's attached to your video-recorder. Then use the brake-point command bar buttons Mate! :) (both VS and Rider have them, even they have shown in this video) You can able to use your mouse click, right? :)

  • @MedicMainDave
    @MedicMainDave 4 ปีที่แล้ว

    @YandereDev

  • @mattsponholz8350
    @mattsponholz8350 5 ปีที่แล้ว +2

    Brilliant! You created this at a freaking great time for me! I was just about to mull over some ideas for rewriting my AudioManager base packages; which, unfortunately, we’re pretty Emum/switch heavy when implementing... thank you!

  • @JaviepalFordring1
    @JaviepalFordring1 4 ปีที่แล้ว

    How is this design pattern called?

  • @sconosciutosconosciuto2196
    @sconosciutosconosciuto2196 5 ปีที่แล้ว +1

    Good video

  • @smoraisbh
    @smoraisbh 2 ปีที่แล้ว

    Nice video. Congratulations.

  • @PhodexGames
    @PhodexGames 4 ปีที่แล้ว +1

    I never really used switches anyway because they clutter your code with breaks and I also think they are not very readable. I mean you can basically always get away with one or two liner if else statements. Much nicer to read less code. I wonder where it actually would make sense to make good use of switches.

    • @MaximilienNoal
      @MaximilienNoal 3 ปีที่แล้ว +1

      Maybe in c#9 with pattern matching. Less code than if. Otherwise, switch was always useless in c#

  • @handleneeds3charactersormore
    @handleneeds3charactersormore ปีที่แล้ว

    why would switches be so disgustingly broken in unity? or is it a C# thing?

  • @TheJoshy007
    @TheJoshy007 4 ปีที่แล้ว

    Why is abstract needed after public in your spell class?

  • @caliche2000
    @caliche2000 5 ปีที่แล้ว

    Your code is like a computer scientist's wet dream.

  • @BrunoWillianAndrade
    @BrunoWillianAndrade 5 ปีที่แล้ว +1

    >>>CAST as Spell!!!

  • @jasonpenick7498
    @jasonpenick7498 3 ปีที่แล้ว

    Normally your videos are good. In this case you have created a poorly contrived situation in order to back up your misconceived idea that switch is bad.

    • @isawadelapradera6490
      @isawadelapradera6490 3 ปีที่แล้ว

      That's the whole point and truth: Switches ARE good, *THEY ARE*
      But if you think you need a switch, you _probably_ did put yourself in a contrived situation that requires taking steps back and refactoring from there.

  • @OtakuDYT
    @OtakuDYT 5 ปีที่แล้ว +1

    I hardly see you use scriptable objects for things, are they an issue? I tend to use them a lot for all my data entries and "spells" too since I can make great/lesser heal etc with roughly the same pattern you've listed here all in one class with a "heal" value.

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +2

      Not at all, just been working on mmos (the kind w/ 10000's of spells) a lot lately and scriptableobjects don't work well in that context so I wanted to keep it even more generic :) I'm definitely gonna do some on SO's though since there seems to be a lot of interest :)

  • @Kenbomp
    @Kenbomp ปีที่แล้ว

    Expression body property

  • @Rexvideowow
    @Rexvideowow 10 หลายเดือนก่อน

    Trying to fit this code into my project here in 2023 and it's giving me this error:
    You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all
    on this line:
    Spell spell = Activator.CreateInstance(spellType) as Spell;
    Does this only work in static classes? That's the only difference I can see between your code and mine.

    • @Unity3dCollege
      @Unity3dCollege  10 หลายเดือนก่อน +1

      It won't work with a monobehavoir because unity makes the constructor inaccessible. Should work fine in 2023 with normal classes though

    • @Rexvideowow
      @Rexvideowow 10 หลายเดือนก่อน

      @@Unity3dCollege Thanks. I guess I'll see if I can avoid making it a Mono, while still figuring out a way to make an Update() tick on it, which I do need. I'm sure there is a video about that on here. Monobehaviour has gotten in my way a few times in the past. Annoying. Thanks for the answer though.

  • @azgan123
    @azgan123 4 ปีที่แล้ว

    Why I have no idea whats going on half of the video? :(

  • @snikodive5077
    @snikodive5077 3 ปีที่แล้ว +1

    This video is one of the most interesting i have ever seen. You have found a very typical example (as you said code smells) and provide us professional solutions. It is a lot better than videos that want to explain design patterns. Keep going guy !

  • @TheIndieGameDev
    @TheIndieGameDev 5 ปีที่แล้ว +2

    This was very helpful thank you. I'm curious how you would tackle interactions between a player character and an interactable object, it's an area I always end up using switches and later regretting it. My solution in the past was to have an Interactable base class that each interactable inherits from, rather than an InteractableType enum, but then you get into the interactions themselves and you go another layer deeper into the abyss

    • @Unity3dCollege
      @Unity3dCollege  5 ปีที่แล้ว +2

      I'd definitely go with the interactable base or an IInteractable interface. I was thinking about this earlier today and I think "classes over conditionals" succinctly describes my preference currently. Make more small classes and far fewer branching conditionals, it makes things so much easier to follow once you know the base architecture of the project.

    • @TheIndieGameDev
      @TheIndieGameDev 5 ปีที่แล้ว

      @@Unity3dCollege yeah I completely agree, it makes for better testability too, and can push you more towards a functional programming or composition way of structuring your logic. Speaking of which, do you have any plans for a DOTS course or series? It's been so difficult to study because it keeps changing, seems like it's getting closer to its final form now though

  • @LeandroDotta
    @LeandroDotta 4 ปีที่แล้ว

    Hi Jason! Really nice video!
    Do you know if the code in the Initialize method of the SpellProcessor works fine in Android using IL2CPP? I'm asking that, because I experienced weird behaviors using "Activator.CreateInstance" (with IL2CPP at Android) before.
    Thanks :)

  • @bahtiyarozdere9303
    @bahtiyarozdere9303 5 ปีที่แล้ว +1

    What about the "IsValidTarget()" function? I think it was not called at all after refactoring.

    • @ashishdandge5587
      @ashishdandge5587 5 ปีที่แล้ว

      May be make a method in SpellProcessor to check a spells's validity before using the SpellProcessor's use spell method

  • @godzabomni4148
    @godzabomni4148 4 ปีที่แล้ว

    Are your rich from comp sci?

  • @matt78324
    @matt78324 5 ปีที่แล้ว +2

    Great video

  • @GameDevNerd
    @GameDevNerd 3 ปีที่แล้ว

    Jason you should do a game architecture video about designing something like a spell and weapon system where you talk about the way classes will be structured amd GameObject hierarchies created rather than the specific graphics and special effects and things like that ... just the architecture and structure part. Will be very helpful to many of us who know how to do a lot of specific things but get confused about engineering large systems in complex games.

  • @Ziplock9000
    @Ziplock9000 5 ปีที่แล้ว

    I'm using a version of this as a slash command processor (Like the one in EQ2) which does some string parsing first and then some parameter number and type validation. But I use Func instead of reflection.. but suppose each of the switch statements is replaced with a dictionary that points to a func or action that can have a variable number of parameters? How would you organise the definition of the Dictionary for those parameters? The way I've done it is to make the parameters a List, which works, but I'm wondering if there's something better?

  • @owencoopersfx
    @owencoopersfx 3 ปีที่แล้ว

    I have a simple personal project where I’m using several switches and have run into this exact problem... like having to change things in a bunch of places when I’m trying to add new functionality. I’ll need to rewatch this a few times but this is great to know about to improve the code. Thanks!

  • @mypaxa003
    @mypaxa003 3 ปีที่แล้ว

    Omg. Why I have not found this video before. I just implemented similar system digging tones of information at the internet. Damn...

  • @DenverMadsen
    @DenverMadsen 4 ปีที่แล้ว

    Do you prefer this approach of reflection over the Gang of Four's Visitor Pattern? en.wikipedia.org/wiki/Visitor_pattern

  • @DannyBoy443
    @DannyBoy443 5 ปีที่แล้ว

    I've been trying to code (n Python firs) since 2014, and I have no idea what the fuck he's talking about lol. Or very little.

  • @litgamesdev
    @litgamesdev 3 ปีที่แล้ว

    Ah I see, the problem is not switches themselves but implementing them instead of concrete implementations of a derived type. I have a power up class in my current project I could definitely apply this to.

  • @XZYSquare
    @XZYSquare 4 ปีที่แล้ว

    You could use a list of spell objects, which can do the checking for whether it can be run on the enemy and can do the things each object would do. Like add health or remove health. Instead of having a "changehealth" method you could just do that stuff in an auto property. moving things out of an autoproperty into another method just to be used the same way, won't help your code. it'll still run the same.

  • @justinwhite2725
    @justinwhite2725 5 ปีที่แล้ว +1

    More of this please! I’m self taught programmer so I don’t know any of the best practices.
    @1:54 omg! See, this is the kind of thing I need. I know this concept but I’ve been writing a getter without a set every time. This shorthand is fantastic!

    • @gigajoules6636
      @gigajoules6636 5 ปีที่แล้ว

      I would recommend Christopher okravi's code walk series.

    • @naviseven5697
      @naviseven5697 4 ปีที่แล้ว

      Get the Design Patterns: Elements of Reusable Object-Oriented Software book. It's a must for every programmer.

  • @LoOnarForge
    @LoOnarForge 3 ปีที่แล้ว

    Hi Jason, thanks for the great lesson, as always. Quick question: those gray comments like: "Set by Unity" or "2 usages" by the functions name, is that some sort of addon for the Unity or for the VS perhaps? You are not using Visual Studio right? Thanks for the info, looks helpful.

  • @joaoferreira_yt
    @joaoferreira_yt 4 ปีที่แล้ว

    I'm starting now on Unity and I migrated from Mobile Development. I'm glad that I actually did something like that except for the "Processor" class, that I already implemented. Cool tips, thanks!

  • @ConchStreetStories
    @ConchStreetStories 5 ปีที่แล้ว

    Do you have made any c# tutorial, i mean if you have masy any playlist

  • @maddehaan
    @maddehaan 4 ปีที่แล้ว

    This is great if you are 'convenient' in the code that is used for the dictionary the way Jason does it. I have used switch statements, but made sure (up to now, so I guess I was lucky) they would use a unique enum type (so single responsibility), like the gamemanager with a gamestate.

  • @KhrisKruel
    @KhrisKruel 5 ปีที่แล้ว +1

    Thank you, this is exactly what I've been looking for. There is almost no understandable videos on reflection. Can you also do this with interfaces or is this unique to classes?

  • @shenlong3879
    @shenlong3879 4 ปีที่แล้ว

    As other have already pointed out those aren't problem with switch statements, they are really problems with bad code design.

  • @dougwarner59
    @dougwarner59 2 ปีที่แล้ว

    I never thought about using named parameters on a method having a single parameter; It does make it easier to see what is going on though.

  • @thomasanderson1416
    @thomasanderson1416 4 ปีที่แล้ว

    Spelllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
    It's weird when you look into it for long.

  • @senser1o76
    @senser1o76 4 ปีที่แล้ว

    I saw book behind you - Tupac Shakur =)

  • @MQNGameDev
    @MQNGameDev 4 ปีที่แล้ว

    Great Video. Thanks for touching on a practical use case for reflection. Your the best.

  • @JayAnAm
    @JayAnAm 5 ปีที่แล้ว

    Your reflection spell has a smell;-)

  • @RiorXD
    @RiorXD 4 ปีที่แล้ว

    That spell switch issue is why i try not to pass values around when they dont need to be passed. ...

  • @parthpandya008
    @parthpandya008 5 ปีที่แล้ว +1

    Very good video for switch statement alternative.
    Love to see more regarding reflection.

  • @Dasky14
    @Dasky14 5 ปีที่แล้ว

    Video idea, about a problem I've had lately:
    Pairing saving data with ScriptableObjects. For example, I have made my game's enemies as ScriptableObjects, so that they can be made into assets easily by the designer and/or artist easily.
    My problem comes when I need to keep track of things like current health/mana of said character, and then I need to make some separate CombatCharacter class and... Well, It just feels like I'm repeating myself, instead of simply being able to store the current health of a character in the same place as other stats.
    So, in short, I'd like to see a more elegant way of pairing temporary data with ScriptableObjects.

  • @aurelienlerochbermuda6600
    @aurelienlerochbermuda6600 5 ปีที่แล้ว +1

    yeah the factory pattern video even use spell(ability) like here, thanks for the new video

  • @pawwilon
    @pawwilon 4 ปีที่แล้ว +4

    7:50 in this case you should "default: throw..." since you don't want to ship a game with unhandled spell types.
    You should be careful with reflection due to it's nature to find links between different point of code more difficult since you skip referencing types directly and instead work in System.Types and genetic objects. Reflection is cool, but it can hide functionality just like switches do with it's automagical nature. Good use of it still here imo.

  • @PerfectlyNormalBeast
    @PerfectlyNormalBeast 4 ปีที่แล้ว

    That static dictionary makes me nervous. I'm pretty sure dictionary isn't threadsafe
    I'm assuming all events from the unity engine are on the main thread?

    • @PerfectlyNormalBeast
      @PerfectlyNormalBeast 4 ปีที่แล้ว

      If the spells aren't threadsafe, you can make the dictionary "thread safe" and do your reflection initialization by wrapping it in a ThreadLocal
      ThreadLocal itself is threadsafe and guarantees one instance per thread (used just like Lazy, where lazy is one instance per process)
      For example:
      private static ThreadLocal _dict = new ThreadLocal(() => InitDict());
      private static Dictionary InitDict()
      {
      //TODO: load it up before returning
      return new Dictionary();
      }
      Then to use it:
      string result = _dict.Value[3];

  • @Sv9zlsT
    @Sv9zlsT 5 ปีที่แล้ว +1

    Jason meets Rider :) and starts use "var" :) it's so misunderstunding when read others code, always tell my teammate about it...
    and rider sometimes doesn't work with breakpoints in static content, please remember about this underneath

    • @DevVader
      @DevVader 4 ปีที่แล้ว +1

      What's the problem with using "var" in C#. Don't understand why people are concerned about that. "var" is just a way to safe some time. Type safety still exists with "var".

    • @tryfinally
      @tryfinally 4 ปีที่แล้ว +1

      @@DevVader var is less readable when doing code review in mspaint.

  • @RossYlitalo
    @RossYlitalo 4 ปีที่แล้ว

    Great Stuff! Thanks Jason!

  • @darryljf7215
    @darryljf7215 5 ปีที่แล้ว

    I do wish there was a GitHub repo.

  • @appolos5116
    @appolos5116 5 ปีที่แล้ว

    You can user scriptable objects

  • @bazzboyy976
    @bazzboyy976 5 ปีที่แล้ว

    Hey Unity3D College, great video. I have never thought about or seen this problem solved this way, I thought from this problem, you would always have to resort to passing in spell objects and throw enums away. Do you use this solution for the mmo stuff you have been working on as of late? Are there any issues as you start to have many spells with more complexity? I assume as you begin to have multiple heal abilities that differentiate from each other in some way, your spelltype enumerations would begin to take on more specific names ('holy light, regenerate, flash heal, etc)?..

  • @eduoki8498
    @eduoki8498 4 ปีที่แล้ว

    += comment

  • @abwuds7208
    @abwuds7208 5 ปีที่แล้ว

    Alright, when you're typing "case" you should just hit Ctrl + Caps + Space to trigger the smart completion! It works everywhere (when assigning something to a variable, passing a parameter, writing a switch/case...) Thanks for the video!

  • @smartknowledge9209
    @smartknowledge9209 5 ปีที่แล้ว

    Thank you very much. Would you mind to do a video tutorial about level select system that when player come back next time he will start from the level he left last time(eg:- voodoo iOS game Roller splat!) without player need to go to level select screen. Just start from the level.

    • @ericsilver6362
      @ericsilver6362 5 ปีที่แล้ว

      Just save the fucking level to a text file.

  • @NiclasGleesborg0
    @NiclasGleesborg0 5 ปีที่แล้ว

    Will definitely take your suggestions into account.