Search Unity

How do I know if i'm using to many if statements/ when to use a Switch statement.

Discussion in 'Scripting' started by Robster95, Aug 19, 2020.

  1. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    So i'm on the very early stages of a project i'm working on and fairly new to coding and unity (not pushed a final project but have worked on it for some time)

    I am having some concerns right now as well as confused on the best way to call for some code.

    A majority of my code is using if statements and since the project is in the early stages I havent seen any slowdown or problems due to the uses of if's(barely any nested loops). But my major concern is wondering if i'm using to many if statements as well as when is a good time to use a switch statement?

    I'm working on an ammo changing method on the gun (thinking I should switch it to the player) and using this file

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class Gun : MonoBehaviour
    5. {
    6.     public bool gunIsHeld;
    7.  
    8.     [SerializeField]
    9.     private Transform firePoint;
    10.     public GameObject normalBulletPref;
    11.     public GameObject darkBulletPref;
    12.     public GameObject lightBulletPref;
    13.     public float bulletForce = 20;
    14.  
    15.     //bullet types
    16.     [SerializeField]
    17.     private bool isNormal;
    18.     [SerializeField]
    19.     private bool isDark;
    20.     [SerializeField]
    21.     private bool isLight;
    22.  
    23.     //gun rotation;
    24.     private Rigidbody2D rb;
    25.     Vector2 mousePos;
    26.     [SerializeField]
    27.     private Transform playerPos;
    28.  
    29.     public float FireRateMax;
    30.     public float FireRateMin;
    31.  
    32.     private void Start()
    33.     {
    34.         firePoint = GetComponentInChildren<Transform>();
    35.         rb = GetComponent<Rigidbody2D>();
    36.  
    37.         FireRateMin = FireRateMax;
    38.  
    39.         isNormal = true;
    40.     }
    41.  
    42.  
    43.     public void Update()
    44.     {
    45.         //checks that the gun is being held by the player;
    46.         IsHeld();
    47.  
    48.         if(gunIsHeld)
    49.         {
    50.             if (Input.GetMouseButtonDown(1))
    51.             {
    52.                 AmmoTypeSwitch();
    53.             }
    54.         }
    55.  
    56.         if (gunIsHeld)
    57.         {
    58.             transform.position = playerPos.position;
    59.  
    60.             //start calling for the rotation of the gun before shooting
    61.             mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    62.  
    63.             //if left click start shooting
    64.             if (Input.GetMouseButton(0))
    65.             {
    66.                 Shoot();
    67.             }
    68.         }
    69.     }
    70.  
    71.     private void FixedUpdate()
    72.     {
    73.         Vector2 lookDir = mousePos - rb.position;
    74.         float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg;
    75.         rb.rotation = angle;
    76.     }
    77.  
    78.     public void Shoot()
    79.     {
    80.         if(isNormal == true)
    81.         {
    82.             if (FireRateMin == FireRateMax)
    83.             {
    84.                 GameObject bullet = Instantiate(normalBulletPref, firePoint.position, firePoint.rotation);
    85.                 Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
    86.                 rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
    87.  
    88.                 FireRateMin -= Time.deltaTime * 1;
    89.             }
    90.  
    91.             else if (FireRateMin != FireRateMax)
    92.             {
    93.                 FireRateMin -= Time.deltaTime * 1;
    94.  
    95.                 if (FireRateMin <= 0)
    96.                 {
    97.                     FireRateMin = FireRateMax;
    98.                 }
    99.             }
    100.         }
    101.  
    102.         else if(isDark == true)
    103.         {
    104.             if (FireRateMin == FireRateMax)
    105.             {
    106.                 GameObject bullet = Instantiate(darkBulletPref, firePoint.position, firePoint.rotation);
    107.                 Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
    108.                 rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
    109.  
    110.                 FireRateMin -= Time.deltaTime * 1;
    111.             }
    112.  
    113.             else if (FireRateMin != FireRateMax)
    114.             {
    115.                 FireRateMin -= Time.deltaTime * 1;
    116.  
    117.                 if (FireRateMin <= 0)
    118.                 {
    119.                     FireRateMin = FireRateMax;
    120.                 }
    121.             }
    122.         }
    123.  
    124.         else if(isLight == true)
    125.         {
    126.             if (FireRateMin == FireRateMax)
    127.             {
    128.                 GameObject bullet = Instantiate(lightBulletPref, firePoint.position, firePoint.rotation);
    129.                 Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
    130.                 rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
    131.  
    132.                 FireRateMin -= Time.deltaTime * 1;
    133.             }
    134.  
    135.             else if (FireRateMin != FireRateMax)
    136.             {
    137.                 FireRateMin -= Time.deltaTime * 1;
    138.  
    139.                 if (FireRateMin <= 0)
    140.                 {
    141.                     FireRateMin = FireRateMax;
    142.                 }
    143.             }
    144.         }
    145.     }
    146.  
    147.     void AmmoTypeSwitch()
    148.     {
    149.         if (isNormal == true)
    150.         {
    151.             isNormal = false;
    152.             isDark = true;
    153.             isLight = false;
    154.  
    155.             Debug.Log("Using Dark Ammo");
    156.         }
    157.  
    158.         else if (isDark == true)
    159.         {
    160.             isNormal = false;
    161.             isDark = false;
    162.             isLight = true;
    163.  
    164.             Debug.Log("Using light Ammo");
    165.         }
    166.  
    167.         else if (isLight == true)
    168.         {
    169.             isNormal = true;
    170.             isDark = false;
    171.             isLight = false;
    172.  
    173.             Debug.Log("Using normal Ammo");
    174.         }
    175.     }
    176.  
    177.  
    178.     void IsHeld()
    179.     {
    180.         if (gameObject.transform.parent == null)
    181.         {
    182.             gunIsHeld = false;
    183.  
    184.             rb.isKinematic = true;
    185.         }
    186.        
    187.         else if(gameObject.transform.parent.CompareTag("Player"))
    188.         {
    189.             gunIsHeld = true;
    190.  
    191.             rb.isKinematic = false;
    192.         }
    193.     }
    194. }
    195.  
    I'm not sure if if statements will be the best to use in the AmmoTypeSwitch() method or if I should change it to a switch.

    The code works but as a fairly new programmer I will still like to learn the best practices so I can grow into a better coder.

    P.S. Lastly just wanted to know if it would be better to have the current ammo type on the player and have the gun call from the player to check.


    Thank you!
     
  2. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    As a fairly new programmer, your first concern should be to finish things and get shot down, everything else comes after. You don't know what you don't know, and practice (that mean failing and making mistake) will teach you better.

    Generally "if" is unlikely to slowdown your computer in any capacity, not on modern hardware, this isn't the game boy era anymore. Hierarchy of memory access will be a bigger bottleneck, but at your level it's probably not even on the map, you probably wouldn't be pushing high level performance code that soon.

    In fact, the process of programming mean that, as you discover the need of your project, you will need to "refactor" the code, ie to re write it to make better and leaner, so don't get block too early on issue like that, most code are actually ugly anyway, despite the cultural wars that happen in programming forums, most production code, ie on a schedule with deadline, will be "bad", it's very easy to find very high profile example from notable company. Also performance is only solved through profiling first and then addressing the actual necessary problem, you get to have the thing work first, THEN you make it performant after profiling, THEN you make it pretty for your ego OR to share with your team.

    So at this point "switch" vs "if" is mostly an aesthetics concern, and that depend on the future of your code design too, switch is great if you have a single statement to test, and that in the future you won't need to break due to exception, it's a bit more verbose too. If you are in a team just follow the code style, the point isn't to make code pretty, but to make readable to many in the same group.

    Also I guess that worry comes from the stupid piling on yandere simulator code, which cause a lot of misinformation about programming to spread. Don't listen to that, a proper programming break down has been made by Dyc3 and already disqualify the if vs switch problem.
     
  3. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Like mentioned above, the problem here isn't with using too many if-statements, and switch-statements wouldn't make much of a difference.
    Rather, there's a lot of redundancy that's bloating this script which could be improved.

    If you want to refactor some of this code, here's some things you can do:
    Code (CSharp):
    1. if(gunIsHeld)
    2. {
    3.    if(Input.GetMouseButtonDown(1))
    4.    {
    5.       AmmoTypeSwitch();
    6.    }
    7. }
    8.  
    9. if(gunIsHeld)
    10. {
    11.    transform.position = playerPos.position;
    12.  
    13.    //start calling for the rotation of the gun before shooting
    14.    mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    15.  
    16.    //if left click start shooting
    17.    if (Input.GetMouseButton(0))
    18.    {
    19.       Shoot();
    20.    }
    21. }
    No need to check
    if(gunIsHeld)
    twice here. Simply move the logic from the second if-statement to the first if-statement.

    Next:
    Code (CSharp):
    1. mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    Camera.main
    is actually a shorthand for
    GameObject.FindWithTag("Main Camera").GetComponent<Camera>();
    . That's a fairly expensive operation that you don't want to run every frame. Instead, cache the reference of
    Camera.main
    in the
    Awake
    or
    Start
    method and use that.

    Next:
    Code (CSharp):
    1. if (FireRateMin == FireRateMax)
    2. {
    3.    //etc...
    4. }
    5. else if (FireRateMin != FireRateMax)
    6. {
    7.    //etc...
    8. }
    else if (FireRateMin != FireRateMax)
    is an unnecessary extra check, since it is guaranteed to be true if the previous condition of
    if (FireRateMin == FireRateMax)
    is false. You can replace that with just an
    else
    block instead.

    Small one next:
    Code (CSharp):
    1. FireRateMin -= Time.deltaTime * 1;
    * 1
    is an unnecessary mathematical operation here. It will just return the same value as
    Time.deltaTime
    anyway. Any value multiplied by 1 will be the same value.

    Next:
    Code (CSharp):
    1. //bullet types
    2. [SerializeField]
    3. private bool isNormal;
    4. [SerializeField]
    5. private bool isDark;
    6. [SerializeField]
    7. private bool isLight;
    You're trying to structure your code so that only one of these values can be true at any given time. Instead of making separate booleans for each bullet type, create an enum:
    Code (CSharp):
    1. [SerializeField]
    2. private BulletType bulletType;
    3.  
    4. public enum BulletType { Normal, Dark, Light }
    Finally, in your
    Shoot
    method, you're repeating the same code multiple times for each condition and just changing the bullet prefab that gets instantiated. You can extract this code into its own method and call it separately instead:
    Code (CSharp):
    1. void FireWhenReady(GameObject bulletPrefab)
    2. {
    3.    if (FireRateMin == FireRateMax)
    4.    {
    5.       GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
    6.       Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
    7.       rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
    8.       FireRateMin -= Time.deltaTime;
    9.    }
    10.    else
    11.    {
    12.       FireRateMin -= Time.deltaTime;
    13.  
    14.       if (FireRateMin <= 0)
    15.       {
    16.          FireRateMin = FireRateMax;
    17.       }
    18.    }
    19. }
    Putting that all together, you'd get something like this:
    Code (CSharp):
    1. public class Gun : MonoBehaviour
    2. {
    3.    [SerializeField]
    4.    private BulletType bulletType;
    5.  
    6.    public GameObject normalBulletPref;
    7.    public GameObject darkBulletPref;
    8.    public GameObject lightBulletPref;
    9.  
    10.    private Camera mainCam;
    11.    //etc...
    12.  
    13.    void Start()
    14.    {
    15.       mainCam = Camera.main;
    16.       //etc...
    17.    }
    18.  
    19.    void Update()
    20.    {
    21.       if(gunIsHeld)
    22.       {
    23.          if(Input.GetMouseButtonDown(1))
    24.          {
    25.             AmmoTypeSwitch();
    26.          }
    27.  
    28.          transform.position = playerPos.position;
    29.  
    30.          mousePos = mainCam.ScreenToWorldPoint(Input.mousePosition);
    31.  
    32.          if (Input.GetMouseButton(0))
    33.          {
    34.             Shoot();
    35.          }
    36.       }
    37.    }
    38.  
    39.    void Shoot()
    40.    {
    41.       //if-statement would work fine here as well.
    42.       switch(bulletType)
    43.       {
    44.          case BulletType.Normal: FireWhenReady(normalBulletPref); break;
    45.          case BulletType.Dark: FireWhenReady(darkBulletPref); break;
    46.          case BulletType.Light: FireWhenReady(lightBulletPref); break;
    47.       }
    48.    }
    49.  
    50.    void FireWhenReady(GameObject bulletPrefab)
    51.    {
    52.       if (FireRateMin == FireRateMax)
    53.       {
    54.          GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
    55.          Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
    56.          rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
    57.  
    58.          FireRateMin -= Time.deltaTime;
    59.       }
    60.       else
    61.       {
    62.          FireRateMin -= Time.deltaTime;
    63.  
    64.          if (FireRateMin <= 0)
    65.          {
    66.             FireRateMin = FireRateMax;
    67.          }
    68.       }
    69.    }
    70.  
    71.    void AmmoTypeSwitch()
    72.    {
    73.       if(bulletType == BulletType.Normal)
    74.       {
    75.          bulletType = BulletType.Dark;
    76.       }
    77.       else if(bulletType == BulletType.Dark)
    78.       {
    79.          bulletType = BulletType.Light;
    80.       }
    81.       else
    82.       {
    83.          bulletType = BulletType.Normal;
    84.       }
    85.    }
    86. }
    87.  
    88. public enum BulletType { Normal, Dark, Light }
    And there's probably still a bit more that can be done here.

    Briefly touching on the whole thing with Yandere Simulator brought up, that game's code issues have been blown up in the wrong way. A lot of people like making those "else if else if else if else if" jokes, but that's only a surface-level issue that's easy for people to understand & make fun of. There are problems with the game's code, but they're much more significant than just a bunch of if-statements.
    Heck, I haven't seen anyone make any jabs about the 2000+ line long Update method. That would cause significantly more of a performance impact than some if-statements.
     
    Last edited: Aug 19, 2020
    Robster95 and neoshaman like this.
  4. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    Yeah a big reason why I was wondering about the switch vs if statement was because of the Yandere sim problem where everyone says the game was very unoptimized due to running only if statements. If you think this time is about learning and growing from it then I wont worry to much, I'm just working on what I hope to be my first game that I can publish and i'm also in an internship with a small company but feel I'm not learning as much from them as I should be due to it being a team a lesser experienced programmers fighting between who has the better code.

    I appreciate the response and i'll make sure I try to push something out as soon as I can so I can learn from it!
     
    neoshaman likes this.
  5. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    Ok thank you for helping clear up the Yandere sim problem and what made me question between if statements and switch statements! as well for the code you shared and helped fix... Kind of mad I didnt see it before because after saying everything and showing me I see all of the problems but hope I get past that bad habit soon! I didnt even realize I called for the if(gunisheld) twice lol! But thank you so much for all of the help and the optimization of the code! I do have some questions though. During the firewhenready method I've always been confused with the arguments in the method as well as calling from one code to another. Will you be able to help explain to me how they are used or know a way to help learn them? (when I say code from one to another I mean the var exName = getcomponent<OtherScript>(); also, I need help with enum's but you posted that link and i'll be sure to look it up!

    Again thank you so much for all the help and the optimization!!! I'll try my hardest to learn from this!!!
     
    neoshaman likes this.
  6. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    A lot of it (the jokes) is from one hella 17 000 long (decompiled) script that hold the Whole NPC AI that is able to do investigation based on evidence, with psychological consequence on the NPC, and given the game has a lot of unique character, there is a lot of exception. That code takes less than 1ms to execute after profiling and the slowdown comes from unity's system (physics, animation, UI, shadow casting), those can be probably optimized, but I feel like it should be on the polish final phases, not a concern during designing the game.

    The code really is just a big behavior tree implemented as "if tree", so it does a lot of skipping in that update() blocks, But you can recognize naive pattern of selector and sequences, thus despite the unique character creating exception, the code is also surprisingly modular, creating character is a matter of appending new behavior and setting a few variable. You can tell that while a non coder, he has some general understanding of AI pattern, his naive behavior tree also have elements of Utility and other pattern. Also it's so readable people have been modding the game to add behavior without any need for comment, the code is truly transparent.

    Also a lot of aesthetics mistakes are artifact of the complexity of a non coder doing one of the most complex social AI for this type of game I had ever seen (only Versu is competing), and designing along the ride, a lot of variable have legacy name whose function has been extended beyond the initial intent, for example the subtree "if(flyAway)" fire when npc see a corpse, but now they don't just fly away, a lot of unique behavior has been appended, like if the corpse is the bully of the NPC, the npc turn evil and don't report the corpse, if the witness the player murdering, well they might actually help him.

    Similarly a lot of nested empty if, or obvious test that could be skip are artefact of a ongoing design where you test stuff, move and remove code to see if that help the design or not, my codes tend to have those in spade when I'm designing. If anything the biggest misstep, that isn't superficial aesthetics, is the (lack) structure of his blackboard for the AI, most bug can be trace down to that, that 17000 line of code, 6000 lines is only the code, everything else is variable! Most of them are the black board data. That mean that state management can be a bit sloppy and have mutually exclusive state be true at the same time (like being dead yet move), at the same time it's also pretty easy to tell what's broken, nothing is obscure, the naming is straight forward.

    That's about to happen when you grow such a complex and ambitious beast organically and most insight are hindsight, and the game is still not done so you probably will be wrong to choose a structure over another
    (premature optimizations and all), following the project I have seen many features dropped that would have mess this. Also the naive BT sequence could benefit from being a coroutines. While it could probably broke that script into many chunk, being all in a single place is actually a plus with code folding, the tree nature makes it very easy to navigate once fold, the naming and flow are very clear, and the behavior well ordered (despite some legacy naming due to old design direction).

    That was such an interesting thing to analyze personally. I try to read released code of games, but most are too complex or fragmented, this one was a breeze to read, very interesting. I did read it whole ...
     
    grimmjagger06, Robster95 and Vryken like this.
  7. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    thats crazy. I heard of people reading the code but the whole thing that must have taken awhile! So i feel better about my code now cause it isn't all just shoved into the update method. I believe thats what you were trying to get at. the fact that there was so much jumbled up into nested if statements calling to each other in one method!

    Thank you for your time! I'll try to do as much as I can to change the mindset I had and to make sure to stick with my game! I hope to try to publish this game!
     
  8. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Nope I was saying it was fine in the context of that specific dev timeline, team composition, skills and ambition. A lot of AI code bloat, in a typical game, just get hidden into intractable visual tools and spaghetti node, which is just the programmer pushing the responsibility to the designer.

    You would have to know what is a behavior tree and how it works to know why this was actually an optimal solution in this case. Like I said "code folding" (when you collapse block of code in the IDE) makes it very manageable, once fold, it's actually very easy to navigate and is laid out in a very sensible way. Think of the IF structure of yandere as a nested folder structure, even though you have a lot of items, you don't get to go through all of them, the search is O(log n) so that's the most efficient way to organize.

    For example let's go back to the example I gave:
    The first thing is that it's not a schedule action, so you don't check the zillion lines of code of schedule behavior (which has it's own skips to avoid executing the zillion line of code), that's one if down, when you check flyaway, you also check if the corpse is teh bully of the current npc AND check if the player is suspicious (ie has blood or hold a weapon), if not check the personality, if coward go to a safe place else go see a teacher. You skip 99% of the code right there to only set a few state on the leaf block. For example you have skip all the code pertaining to the complex psychological fallout of the corpse being the bully of the npc, that won't ever be executed or tested, and that the most complex part.

    On top of that he does the great thing of separating concern, the BT structure don't execute behavior, it merely set things up for another function down the line to execute. SO adding behavior is separated from the logic, which allow the code to allow both generic behavior and character/group specific behavior. The sophistication of the game wouldn't have be this complex. Yandere dev find a nice formula where the code is basically the design document, and is easy to edit.

    Plus since it's "low level" coding, the compiler will do further optimization to smooth things out. It works because it's a single person team, if the team was (way) bigger you would have to develop a tools and modularized the code just a bit more. The irony is that this would be more "proper" but also potentially slower in performance (but probably not enough to start caring) as you would have to have a lot of random memory access that the compiler can't optimized. So what you gain in production (by allowing multiple designer do behaviors in parallel) you probably lose in performance.

    Context matter a lot. I was trying to expose the context and how a real dev can lead you some path. Like I say hindsight is easy. NO project will ever have have perfect code or great architecture, especially from the get go, you can't always predict the need of the project ahead of time.
     
  9. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Sure thing. It's a bit of a long post though, but feel free to read if you're interested.

    Method arguments are simply some sets of values that a method requests to run. For example:
    Code (CSharp):
    1. void Start()
    2. {
    3.    PrintMessage("Hello");
    4.    PrintMessage("World");
    5. }
    6.  
    7. void PrintMessage(string message)
    8. {
    9.    //The first time this method is called, "message" = "Hello".
    10.    //The second time this method is called, "message" = "World".
    11.    Debug.Log(message);
    12. }
    This
    PrintMessage
    method requests a
    string
    value as an argument, and will print that value to the Unity console.
    In the
    Start
    method, we call
    PrintMessage
    twice, the first time giving it a value of "Hello", and the second time, a value of "World", showing how the value of the method argument ("message") is assigned when the method is called.

    If we go back to the previous post, where we call the
    FireWhenReady
    method...
    Code (CSharp):
    1. void Shoot()
    2. {
    3.    switch(bulletType)
    4.    {
    5.       case BulletType.Normal: FireWhenReady(normalBulletPref); break;
    6.       case BulletType.Dark: FireWhenReady(darkBulletPref); break;
    7.       case BulletType.Light: FireWhenReady(lightBulletPref); break;
    8.    }
    9. }
    10.  
    11. void FireWhenReady(GameObject bulletPrefab)
    12. {
    13.    if (FireRateMin == FireRateMax)
    14.    {
    15.       //if bulletType == Normal, "bulletPrefab" = "normalBulletPref".
    16.       //if bulletType == Dark, "bulletPrefab" = "darkBulletPref".
    17.       //if bulletType == Light, "bulletPrefab" = "lightBulletPref".
    18.       GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
    19.  
    20.       Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
    21.       rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
    22.       FireRateMin -= Time.deltaTime;
    23.    }
    24.    else
    25.    {
    26.       FireRateMin -= Time.deltaTime;
    27.       if (FireRateMin <= 0)
    28.       {
    29.          FireRateMin = FireRateMax;
    30.       }
    31.    }
    32. }
    ...The switch statement compares the value of
    bulletType
    and passes the corresponding prefab GameObject into the
    FireWhenReady
    method. This allows all of the logic inside
    FireWhenReady
    to be re-used with different arguments, so that you don't have to re-write all of the logic again for every different bullet prefab.

    In general, if you find that you're re-writing the same code in multiple places, it can and likely should be turned into a method.

    Methods also have what are called return types. If you're wondering what "void" is when you define a method, that is one such return type. "void" simply means "this method returns nothing", but sometimes you may want to run a method and return a result after it completes.
    A type is basically just the name of an object. "MonoBehaviour", "Vector3", "int", "bool", "Rigidbody", are all examples of types, and any custom types you create are valid as well (like your "Gun" script).

    Here's an example:
    Code (CSharp):
    1. void Start()
    2. {
    3.    //sum = 6;
    4.    int sum = AddNumbers(2, 4);
    5.  
    6.    //greaterNumber = 12.087;
    7.    float greaterNumber = GetGreaterNumber(8.125f, 12.087f);
    8.  
    9.    //Logs a message stating if this GameObject is active or not.
    10.    if(GameObjectIsActive())
    11.    {
    12.       Debug.Log("This GameObject is active.");
    13.    }
    14.    else {
    15.       Debug.Log("This GameObject is inactive.");
    16.    }
    17. }
    18.  
    19. //This method returns an int type.
    20. int AddNumbers(int num1, int num2)
    21. {
    22.    //Returns the sum of the two method arguments.
    23.    return num1 + num2;
    24. }
    25.  
    26. //This method returns a float type.
    27. float GetGreaterNumber(float num1, float num2)
    28. {
    29.    float greaterNumber;
    30.  
    31.    if(num1 > num2) {
    32.       greaterNumber = num1;
    33.    }
    34.    else {
    35.       greaterNumber = num2;
    36.    }
    37.  
    38.    //Returns the greater number of the two method arguments.
    39.    return greaterNumber;
    40. }
    41.  
    42. //This method returns a bool type.
    43. bool GameObjectIsActive()
    44. {
    45.    //Returns true if this GameObject is active in the scene, or false if not.
    46.    return gameObject.activeSelf;
    47. }

    This one's a two-fold example. It should be noted that
    GetComponent
    is specifically a Unity-only thing, but the part where you pass a type within the
    <>
    symbols is a native feature of C# called Generics, and they're a bit more of an intermediate-advanced subject.

    As previously mentioned, you pass a type within the
    <>
    symbols.
    When you pass a type into a Generic method/class/interface, it basically means, "do this using this type of object".

    So getting back to
    GetComponent<>()
    , what Unity does is search for and return a script attached to a GameObject that is the type specified within the
    <>
    symbols.
    For instance, when you write
    Rigidbody rb = GetComponent<Rigidbody>();
    , you're telling the method, "Get me a script attached to the GameObject that is a Rigidbody type", and it will return the first Rigidbody script it could find on the GameObject.

    An
    enum
    is just a set of named constant values you can choose between.
    Say you're making a calendar app, where the user can add an event onto a day of the week. Without using
    enums
    , you would probably use
    strings
    or
    ints
    instead to reference the day of the week, however that comes with a problem such as this:
    Code (CSharp):
    1. class CalendarApp
    2. {
    3.    void SomeMethod()
    4.    {
    5.       AddCalendarEvent("Monday", "Uncle's Birthday");
    6.       AddCalendarEvent("Wednesday", "Project Deadline");
    7.       AddCalendarEvent("Saturday", "Anniversary");
    8.  
    9.       //You only want to pass the named days-of-the-week, but a string could be anything.
    10.       //Imagine if the user had to type-in the day of the week in a textfield. They could do something like this:
    11.       AddCalendarEvent("gisdhufiugsdfghlauibsg", "This will run, but it won't work");
    12.    }
    13.  
    14.    void AddCalendarEvent(string dayOfWeek, string eventName)
    15.    {
    16.       //etc...
    17.    }
    18. }
    With an
    enum
    though, a defined set of named constants restricts what values can be entered:
    Code (CSharp):
    1. enum DayOfWeek
    2. {
    3.    Monday,
    4.    Tuesday,
    5.    Wednesday,
    6.    Thursday,
    7.    Friday,
    8.    Saturday,
    9.    Sunday
    10. }
    So if we fix up our calendar app to use this DayOfWeek enum as so...
    Code (CSharp):
    1. class CalendarApp
    2. {
    3.    void SomeMethod()
    4.    {
    5.       AddCalendarEvent(DayOfWeek.Monday, "Uncle's Birthday");
    6.       AddCalendarEvent(DayOfWeek.Wednesday, "Project Deadline");
    7.       AddCalendarEvent(DayOfWeek.Saturday, "Anniversary");
    8.  
    9.       //You can only use the named values inside of "DayOfWeek".
    10.       //This code will not even compile and cannot be run.
    11.       AddCalendarEvent(DayOfWeek.gisdhufiugsdfghlauibsg, "This won't even compile");
    12.    }
    13.  
    14.    void AddCalendarEvent(DayOfWeek dayOfWeek, string eventName)
    15.    {
    16.       //etc...
    17.    }
    18. }
    19.  
    20. public enum DayOfWeek
    21. {
    22.    Monday,
    23.    Tuesday,
    24.    Wednesday,
    25.    Thursday,
    26.    Friday,
    27.    Saturday,
    28.    Sunday
    29. }
    ...The day-of-week inputted by the user is guaranteed to be a valid day.
     
    Last edited: Aug 20, 2020
    Robster95 likes this.
  10. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    thank you i'll look over it soon! i'm currently going to make a health system for my character and then try to make it so the character can deal and take damage from the enemies when I make them!