Confused about my Switch statement

I’m trying to understand switch statements and conditionals with them.

Here is the script currently.

Unit thisUnit; <- my reference to the unit stats it has.
int captureStartingHealth;
int captureStartingMana

void Start()
{
    captureStartingHealth = thisUnit.currentHP;
    // In start it's the same as thisUnit.maxHP;
    captureStartingMana = thisUnit.currentMP;
    // In start it's the same as thisUnit.maxMP;
}

void AssignmentSwitchStatement()
{
    switch (captureStartingHealth)
   {
case int captureStartingHP when captureStartingHealth < (float)captureStartingHealth:
        behaviour = Behaviour.SUPPORT;
        roll = Random.Range(0, 101);
        IncrementSupportRollChances();
        break;
default:
        roll = Random.Range(0, 101);
        DefaultRollChances();
        break;
   }
}

What is the correct syntax for constructing a way to reference when my captureStartingHealth variable is less than 40 for example. Basically I want to be able to swap out behaviour with specific code whenever this character reaches a threshold determined by my case/conditional.

Why can’t I use my captureStartingHealth integer like this?

switch (captureStartingHealth)
        {
            case captureStartingHealth when captureStartingHealth < (float)thisUnit.maxHP:
             //execute code
             break;
        }

"
Instead it requires me to make a new integer

case int a when a < conditional

but how do I know when this new int has reached the conditional as true? Like I don’t understand how this int “a” works for the switch since it’s a local integer that isn’t assigned anything right? So for me it’s like saying…

case int a (0) when a (0) < (float)thisUnit.maxHP: (which will never be 0)

That is the part that baffling me with my switch statement currently.
Edited for clarity

case int captureStartingHP when captureStartingHealth < (float)captureStartingHealth:

That doesn’t really make sense here, you’re making a comparison of the same variable here.

It would also be helpful if you would report the error message.

But in the simple case a single if else block would be better here.

you can try (untested):

switch (captureStartingHealth)
        {
            case int captureStartingHP  when captureStartingHP < thisUnit.maxHP:
             //execute code
             break;
        }

Edit: I don’t know exactly how Mono behaves, but when I test the code with https://sharplab.io, the compiler replaces the switch with an if else block.

1 Like

Switch statements used to be quite simple constructs. However with the latest C# revisions we get a lot of new stuff. In particular switch statements and switch expressions now support all sorts of pattern matching magic. In your case it just makes no sense to use a pattern matching switch since this only makes sense when the value / variable you switch on is a polymorphic or compound type. The pattern matching switch is about switching depending on the actual type of the value / variable and directly converting / casting it to that type or to have access to individual properties within the additional guard. C# got a lot more complex with those additions and provides a lot more attack surface to mess things up.

To me it’s not really clear what you actually want to test here. While the new pattern matching stuff makes sense in some cases as it could simplify / shorten the code, I would highly recommend to stay away from code you don’t fully understand ^^. If you want to know more about pattern matching in C#, here’s a great video summary of what was added in which version of C#.

1 Like

Thanks guys, sorry about my confusion. Are switch statements in general something difficult to tackle for beginner-intermediate coders?

Usually not because in the past switch statement were limited to only constant expressions and in the beginning only supported integral types (integers, enums). Later it also supported string constants as cases. All the pattern matching stuff is relatively new. The question is why you actually want to use a switch in the first place ^^. Keep in mind that those are all just tools in your tool box. The classical switch block provided optimisations when you had a lot of switch cases because the switch was essentially translated into either a jump table or a dictionary look up. The pattern matching which involves checking variables are just “neater” ways of writing else-if chains. As Nick mentioned in the video, this pattern matching syntax is a more functional programming approach. Functional programming is a very different approach to a problem as you program more with declarative relations. This makes sense if you have complex relations or complicated edge cases.

In many cases plain imperative programming is much easier to follow and to understand. While it’s always good to learn something new, I highly recommend to either stay away from code you don’t fully understand what it does, or if for some reason you really want to use it, take the time to study it in detail. As a beginner it may be difficult to wrap your head around complex mechanics if you still struggle with fundamentals. Of course we can’t say anything about your level of understanding and therefore it’s not possible for us to give a proper suggestion what you should or shouldn’t do or learn.

I recommend sticking to things you already know and once you’re comfortable using those tools and statement you can try improve / refactor some of your older code.

C# used to be a quite simply and simplistic language. Though over the last years it has grown significantly in complexity. In addition to that when it comes to Unity you always have to watch out which version of C# (or which features) are actually supported by the Unity version you’re using. Unity usually lags behind the development of the C# language itself.

1 Like

I wanted to branch out from relying too heavily on the if / else and I did very little research on switch statements prior to today’s attempt at using it. I wanted to make a very basic greater or equal, less than or equal structure where it would switch depending on the threshold set of the enemy’s life points.

So in theory I just wanted to have the enemy behave more conservatively if it had less health, try to heal or take defense approach events. If it had more life than say the player, then be more reckless. It’s just a basic implementation for making the enemies feel a bit more robust and alive.

but because it’s my first attempt at a switch… ever prior to this post I had researched on stack overflow with different methods of writing out code for switches. I discovered it was possible to do the check aforementioned above… during that implementation I noticed I wasn’t able to just directly hook up the integer I wanted to compare with the unit’s life.

That stack overflow had the example as

int a = 50;

switch (a)
{
   case int b when b >= 60:
   //execute something,
   break;

   case int b when b >= 40:
   //execute something,
   break;

   case int b when b >= 20:
   //execute something,
   break;
}

But you guys are completely right, it seems to be out of my realm for now. I will definitely look into researching more about switch statements. Also I’m using one of the latest Unity version’s, updated my project recently.

The main advantage a switch statement provides over an if statement, is reducing cyclomatic complexity. It really isn’t something to worry about early on, only when you are trying to squeeze every ounce of efficiency possible. It might not even matter since its highly likely the compiler converts if statements to switch statements if it sees that it can.

for a beginner learning each mechanism of scripting one at a time, usage of a switch statement should be reserved for things with distinct state, which is usually integers, strings, or enums. It can be looked at as several == comparisons conceptually.

If you want something to happen on a more-than/less-than basis, that usually throws out switch statements as an option.

1 Like

@Bunny83 after watching some of that video I messed around again with my switch code after a nights sleep cause I’m naive and stubborn and kept thinking about it last night (my time).

After watching the video where he explained some things I decided to implement the one where he was demonstrating the boolean ‘is int’ and applied that to the unit’s health integer.

So I removed the captureStartingHealth and directly fed in the syntax as ‘switch (thisUnit.currentHP is int currentHP)’ okay. Now it works like a boolean due to the ‘is’ if I’m understanding that correctly.

So now I have this working exactly how I wanted it to work and it seems extensible as I’ve tried up to 6 different cases and each turn false or true depending on the amount of health they have.

Made the switch small for readability, but it’s enough to at least show off I was able to eventually get it working the way I intended.

void AssignmentSwitchStatement()
    {
        switch (thisUnit.currentHP is int currentHP)
        {
            case true when currentHP < (float)thisUnit.maxHP / 1 * 0.25f:
Debug.Log("Reached the current last threshold at " + (float)thisUnit.maxHP / 1 * 0.25f);
                behaviour = Behaviour.SUPPORT;
                roll = Random.Range(45, 101);
                IncrementSupportRollChances();
                break;

            case true when currentHP < (float)thisUnit.maxHP / 1 * 0.5f:
Debug.Log("Reached a new threshold at " + (float)thisUnit.maxHP / 1 * 0.5f);
                behaviour = Behaviour.SUPPORT;
                roll = Random.Range(25, 101);
                IncrementSupportRollChances();
                break;

            case true when currentHP < (float)thisUnit.maxHP / 1 * 0.75f:
Debug.Log("Reached it's first threshold at " + (float)thisUnit.maxHP/ 1 * 0.75f);
                behaviour = Behaviour.AGGRESSIVE;
                roll = Random.Range(-15, 101);
                IncrementAggressiveRollChances();
                break;
            default:
                Debug.Log(thisUnit.unitName + " has default behaviour!");
                behaviour = Behaviour.PASSIVE;
                roll = Random.Range(0, 101);
                DefaultRollChances();
                break;
         }
    }

And for calling this method I have it hooked up in an update where it checks for various things before this gets called. So… thanks again for that video and for everybody elses confusion at my code to help push me to find a solution instead of putting it in the back burner.

You have no way of knowing this, but switch statements aren’t an advanced concept. What I mean is, lists are an advanced concept – if you have variables like a1, a2, a3 … a20, then a single list of length 20 is hugely better. Or functions – if you have similar code in 5 places then a function makes it so much better. But switches are just an alternate way to write some simple IF’s. If feels as if IF’s are A or B, whereas a switch is multi-way – A, B, C, D, or E. But IF’s are multiway with the cascading trick:

if(a<10) { do stuff }
else if(a<20) { do other stuff}
else if(b==12) { do other, other stuff }
...
else { do none of the above stuff }

People get excited about switches since the syntax is so different and there are so many special rules. You figure language designers would never work that hard for a feature that doesn’t make programming any easier; but no – it’s easy to add switches than to deal with people who want to know why you left them out.

Another way to think of it, useful things to know are: lists and indexes, functions & parameters, passing functions, writing your own encapsulated classes, using inheritance in built-ins, reading an API … . Figuring out switches won’t let you do anything IF’s aren’t perfectly fine for, they aren’t terribly useful practice for anything, and they keep you from learning actually useful concepts.

2 Likes

As Owen said this is unnecessarily complicated and I would call that a complete misuse of the switch statement. You get the same behaviour that way:

    void AssignmentSwitchStatement()
    {
        int currentHP = thisUnit.currentHP;
        if (currentHP < thisUnit.maxHP * 0.25f)
        {
            Debug.Log("Reached the current last threshold at " + thisUnit.maxHP * 0.25f);
            behaviour = Behaviour.SUPPORT;
            roll = Random.Range(45, 101);
            IncrementSupportRollChances();
        }
        else if (currentHP < thisUnit.maxHP * 0.5f)
        {
            Debug.Log("Reached a new threshold at " + thisUnit.maxHP * 0.5f);
            behaviour = Behaviour.SUPPORT;
            roll = Random.Range(25, 101);
            IncrementSupportRollChances();
        }
        else if (currentHP < thisUnit.maxHP * 0.75f)
        {
            Debug.Log("Reached it's first threshold at " + thisUnit.maxHP * 0.75f);
            behaviour = Behaviour.AGGRESSIVE;
            roll = Random.Range(-15, 101);
            IncrementAggressiveRollChances();
        }
        else
        {
            Debug.Log(thisUnit.unitName + " has default behaviour!");
            behaviour = Behaviour.PASSIVE;
            roll = Random.Range(0, 101);
            DefaultRollChances();
        }
    }

Though this actually has less noise and is even shorter and simpler. Note that in a “normal” switch the order of the switch cases do not matter. However when you use “when” guards this is no longer true. For example if currentHP is 0, all 3 cases actually match since currentHP is smaller than the specified values. However only the first switch case would be executed. When you swap your cases around, the behaviour would break, just like in the normal else-if chain. So using a switch here does not provide any advantage. It just adds noise and makes the code harder to read.

Also note I simplified your calculations. Dividing by 1 is pointless. Multiplying an int by a float automatically results in a float. Just one argument has to be a float.

You could actually pull out the max hp from the conditions like this

float percent = (float)thisUnit.currentHP / thisUnit.maxHP;

Now you can use if statements like this:

if (percent < 0.25f)
// [ ... ]
else if (percent < 0.5f)
// [ ... ]
else if (percent < 0.75f)
// [ ... ]

Not only will this be faster since we only have to do one calculation when calculating the percentage, but the conditions become even simpler to read and understand.

1 Like

Whoa, thanks for the clarification from the both of you.

I guess I was just excited to get a switch working and not actually think about the reasoning why I wanted to use one in the first place. You both have made me understand a lot better with my current code situation.

Amazing how useful this forum is whenever I come for questions. Thank you for the break downs on the errors of my code and the switch usage =)