Doers of Stuff.org

Indie Game Dev & Simulation Engineer | Unity + C# Wizard | Building Worlds, One Line of Code at a Time

Medic!

Having limited our Ammo and then adding a collectible to replenish our ammo, it is not much of a stretch to create another collectible to restore lost lives as well. As with our other PowerUps and Collectibles, we will create this one in the same style and leverage the existing spawning system to manage it. While our other PowerUps are limited in that they do not allow themselves to be “doubled up” e.g. you cannot collect a TripleShot PowerUp while the TripleShot is active, the Health PowerUp will allow you to collect each one immediately. However, we will still cap the player at three lives. This allows us to implement it easily and maintain the current art and logic. Plus, at this time at least, the value of being able to stockpile extra lives seems questionable.

What is nice about adding another PowerUp now is there is very little new work to do here. We have already discussed making the sprite. We have already discussed adding the game object/prefab. So, the only thing new is to add the supporting code.

As always, we need to add another branch to the switch statement in the Player.OnTriggerEnter2D() method.

    private void OnTriggerEnter2D(Collider2D other)
    {
        switch (other.tag)
        {
            ...

            case "HealthPU":
                if (playerLives < 3) { CollectHealth();  }
                break;
            default:
                break;
        }
    }

Obviously, from the above, we see we also need to create the CollectHealth() method. In this method we need to update the playerLives count and update the UI lives indicator.

    private void CollectHealth()
    {
        playerLives++;
        uiManager.CurrentLives(playerLives);
    }

    private void TakeDamage()
    {
        playerLives--;
        uiManager.CurrentLives(playerLives);            
        if (playerLives < 1) { DeathScene(); return; }
        fireEngineAnims[ChooseEngine].SetActive(true); 
    }

Now, this is awfully close to the TakeDamage() method. In fact, looking at it, we realize we need to update the fireEngineAnims also. If we gain back a life, we should disable the related animation. Think of R2D2 crawling around on the back of the ship fixing things. The problem is the ChooseEngine property.

    private int ChooseEngine      // The engine fire animations are stored in an array
    {                             // This property randomizes which one is chosen first
        get
        {
            return playerLives == 2
                 ? Random.Range(0, 2)                        // choose random engine on first hit
                 : !fireEngineAnims[0].activeSelf ? 1 : 0; ; // choose other (inactive) engine on second
        }
    }

This function checks to see if either engine fire animation is already activated or not. If it is, we return the other one. When we get back a life however, we need to do just the opposite.

Thinking about in English rather than C# we can fix this by being more explicit. In the current case, we are not just choosing an engine, we are choosing an engine that is still in “good” condition. When we add back a life, we are asking for an engine that is damaged (so we can “repair” it). That means, instead of having a ChooseEngine property, we can have ChooseGoodEngine and ChooseDamagedEngine properties. That turns our code into this.

    private int ChooseGoodEngine  
    {                             
        get
        {
            return playerLives == 2
                 ? Random.Range(0, 2)                       
                 : fireEngineAnims[0].activeSelf ? 1 : 0; ; 
        }
    }

    private int ChooseDamagedEngine  
    {                             
        get
        {
            return playerLives == 2
                 ? Random.Range(0, 2)                       
                 : !fireEngineAnims[0].activeSelf ? 1 : 0; ; 
        }
    }

    private void CollectHealth()
    {
        playerLives++;
        uiManager.CurrentLives(playerLives);
        fireEngineAnims[ChooseDamagedEngine].SetActive(false);  
    }

    private void TakeDamage()
    {
        playerLives--;
        uiManager.CurrentLives(playerLives);            
        if (playerLives < 1) { DeathScene(); return; }
        fireEngineAnims[ChooseGoodEngine].SetActive(true); 
    }

Now, I can’t say I am entirely happy with this code. Notice the only difference between the ChooseGoodEngine and ChooseDamagedEngine properties is a single character (the exclamation point). In one we check if the value is set and in the other, we check if it is NOT set. Likewise, the differences between the CollectHealth() and TakeDamage() methods is rapidly declining. Both these sets of methods seem prone to future damage due to aggressive refactoring since they are far too similar.

That being said, it seems to work for now, and while the code seems a bit dicey, the self-documenting method names make sense. So, even though I feel like we will come back to this later, let’s let it slide for now.

Leave a Reply