Help with if statement

Hi, so I am making a tower defense game and I am trying to make it so if you have enough points for level 2 but less than enough points for level 3, upgrade to level 2. And if you have enough points to upgrade to level 3 and more points than enough points needed to upgrade to level 2, upgrade to level 3. For example, you need 20 points to upgrade a tower to level 2 and 40 to upgrade to level 3. You only have 30, so you can only upgrade to level 2. But if you had 40 or more than 40 you could skip level 2 and upgrade right to level 3. I thought this code would do that but with this code it wont upgrade at all. So how would I do that? Thanks!

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Waypoint : MonoBehaviour
{
    [SerializeField] Tower towerPrefab;
    [SerializeField] Tower tower;
    [SerializeField] Transform towers;
    [SerializeField] PathFinder pathFinder;
    [SerializeField] TextController textController;
    public bool hasTower;
    public bool towerIsLevel1;
    public bool towerIsLevel2;
    public bool towerIsLevel3;
    [SerializeField] EnemyTrigger enemyTrigger;
    [SerializeField] TowerLevel2 towerLevel2Prefab;
    [SerializeField] TowerLevel3 towerLevel3Prefab;
    public Tower spawnedTower;
    Tower towerSpawned;

    // public ok here as it is a data class 
    public bool isExplored = false; // ok as is a data class
    public Waypoint exploredFrom;
    public bool isPlaceable = true;
    public bool isPath;


    Vector2Int gridPos;

    const int gridSize = 20;
    public int GetGridSize()
    {
        return gridSize;
    }

    public Vector2Int GetGridPos()
    {
        return new Vector2Int(
              Mathf.RoundToInt(transform.position.z / gridSize),
              Mathf.RoundToInt(transform.position.x / gridSize)
        );
    }

    void OnMouseOver()
    {
        if (Input.GetMouseButtonDown(0))
        {
            CheckForUpgrade();
            if (pathFinder.numberOfTowers != pathFinder.towerLimit && isPlaceable == true)
            {
                SpawnTower();
                var updatedTowerText = textController.towersLeft = textController.towersLeft - 1;
                textController.towerText.text = updatedTowerText.ToString();
            }
            else if (pathFinder.numberOfTowers == pathFinder.towerLimit && hasTower == false)
            {
                print("Tower Limit Reached!");
            }
        }
    }

    public void SpawnTower()
    {
        float towerX = transform.position.x;
        float towerZ = transform.position.z;
        Vector3 towerPos = new Vector3(towerX, 20f, towerZ);
        spawnedTower = Instantiate(towerPrefab, towerPos, Quaternion.identity);
        hasTower = true;
        spawnedTower.transform.parent = towers;
        isPlaceable = false;
        pathFinder.numberOfTowers = pathFinder.numberOfTowers + 1;
        towerIsLevel1 = true;
        towerIsLevel2 = false;
        towerIsLevel3 = false;
    }
    void CheckForUpgrade()
    {
        if (isPlaceable == false && textController.points >= tower.scoreNeededForLvl2 && isPath == false && textController.points < tower.scoreNeededForLvl3
            && towerIsLevel3 == false && towerIsLevel1 == true && towerIsLevel2 == false)
        {
            print("Upgraded To Level 2");
            textController.points -= tower.scoreNeededForLvl2;
            towerIsLevel1 = false;
            towerIsLevel2 = true;
            towerIsLevel3 = false;
            spawnedTower.damagePerHit = towerLevel2Prefab.damagePerHit;
        }
        if (isPlaceable == false && textController.points >= tower.scoreNeededForLvl3 && isPath == false && textController.points > tower.scoreNeededForLvl2
            && towerIsLevel2 == true && towerIsLevel3 == false && towerIsLevel1 == false)
        {
            print("Upgraded To Level 3");
            textController.points -= tower.scoreNeededForLvl3;
            towerIsLevel2 = false;
            towerIsLevel3 = true;
            spawnedTower.damagePerHit = towerLevel3Prefab.damagePerHit;
        }
    }
}

Well, there’s your question, and then there’s advice on the code presented. Unfortunately, I can’t quite separate these two.


From here I can’t tell the state of all these bools when the test (if) fires in CheckForUpgrade(). If isPlaceable were true or or isPath were true, the rest of the test isn’t going to be checked, and it is difficult to debug this with so many tests combined in a single if test. Sometimes if tests just have to check several things, but when you see those things being tested repeatedly, that’s a hint there’s a problem in design.


An example thought would be to reject early for the two repeated tests, like this:

void CheckForUpgrade()
{
 if ( isPlaceable == true || isPath == true ) return;

....
}

If either isPlaceable or isPath are true, the method returns without further work. That is exactly what is written from the reverse perspective in the two “if” tests, but now this is an early rejection, and these tests can be removed from the next two “if” statements, you know they must both be false or the execution point couldn’t reach them.


Next, you have three bools indicating the level. This is really better ‘encoded’ by an integer. Consider a variable, currentLevel, as in:

public int currentLevel = 1;

When that’s a 2, the player is on level 2. When that’s a 3, the player is on level 3. Now you can expand to more levels without adding lots of tests, but even if you stay with 3 levels, the test is much simpler. Instead of checking a pattern of 3 bools (and do we even know they’re correctly aligned?), you’re checking the level for a value. Three tests become 1 test.

With these small changes, the exact same work as you’ve posted would now read like this:

 void CheckForUpgrade()
 {
  if ( isPlaceable == true || isPath == true ) return;

  if (    textController.points >= tower.scoreNeededForLvl2 
       && textController.points < tower.scoreNeededForLvl3
       && currentLevel == 1 )
     {
         print("Upgraded To Level 2");
         textController.points -= tower.scoreNeededForLvl2;

         currentLevel = 2;

         spawnedTower.damagePerHit = towerLevel2Prefab.damagePerHit;
     }

  if (   textController.points >= tower.scoreNeededForLvl3 
      && currentLevel == 2 )
     {
         print("Upgraded To Level 3");
         textController.points -= tower.scoreNeededForLvl3;

         currentLevel = 3;

         spawnedTower.damagePerHit = towerLevel3Prefab.damagePerHit;
     }
 }

Notice that in the second test, I removed the check against level2’s score. Presumably if the score is greater than level3, it has to be greater than level2.


I can’t be sure why the score is decremented in these cases, but perhaps you’re implementing the concept that these points “purchase” or are “spent” to change the level. Think about that carefully for a moment, though. Let’s say it’s 20 points to get level 2, and 40 points to get level3. Let’s say the currentLevel == 1, and the player has 10 points. They won’t upgrade, which seems correct. They, the earn more and have 20 points.


At that point, the first test should fire. After that currentLevel == 2, and their points drops to zero. Ok, they’re upgraded.


If SpanTower is ever called, it will reset the player to level 1. Is that expected?


Now, let’s say SpanTower isn’t a problem. The player must reach at least 40 points in order to upgrade at all, and then only to level3. That seems ok.


At this point I should ask, what operating system are you using, and which debugger? Are you using Visual Studio? If you are on Visual Studio, you can set a breakpoint in the CheckForUpgrade function and watch it work, check values and know what it is (or is not) doing that you expect.


Hi, thank you so much for the great and detailed answer. Thanks to you, I was now able to find my problem easily. Here is my new code: void CheckForUpgrade()
{
if(isPlaceable == true || isPath == true)
{
return;
}

    if (textController.points >= tower.scoreNeededForLvl2 && currentTowerLevel == 1 && textController.points < tower.scoreNeededForLvl3)
    {
        print("Upgraded To Level 2");
        textController.points -= tower.scoreNeededForLvl2;
        currentTowerLevel = 2;
        spawnedTower.damagePerHit = towerLevel2Prefab.damagePerHit;
    }
    if (textController.points >= tower.scoreNeededForLvl3 && currentTowerLevel == 2 || currentTowerLevel == 1)
    {
        print("Upgraded To Level 3");
        textController.points -= tower.scoreNeededForLvl3;
        currentTowerLevel = 3;
        spawnedTower.damagePerHit = towerLevel3Prefab.damagePerHit;
    }
}