1
\$\begingroup\$

I am currently following a tutorial on how to make a 2D platformer in Unity.

The tutorial explains how to make an enemy wave spawner, but it doesn't explain how to make enemies stronger after I have completed a certain amount of waves.

So, to explain: I have an Enemy class, which has an EnemyStats class in it:

public class Enemy : MonoBehaviour
{
    [System.Serializable]
    public class EnemyStats
    {
        public int damage = 20;
        public int maxHealth = 100;

        public int CurrentHealth
        {
            get { return _currentHealth; }
            set { _currentHealth = Mathf.Clamp(value, 0, maxHealth); }
        }

        private int _currentHealth;

        public void Init()
        {
            CurrentHealth = maxHealth;
        }
    }

    public EnemyStats stats = new EnemyStats();

    private StatusIndicator statusIndicator;

    void Awake()
    {
        stats.Init();
        statusIndicator = transform.Find("StatusIndicator").GetComponent<StatusIndicator>();

        if (statusIndicator != null)
        {
            statusIndicator.SetHealth(stats.CurrentHealth, stats.maxHealth);
        }
    }
}

And in the WaveSpawner class I have the method to spawn the enemy:

private void SpawnEnemy(Enemy enemy)
{
    Transform sp = spawnPoints[UnityEngine.Random.Range(0, spawnPoints.Length)];

    if (CurrentWave % 5 == 0)
    {
        enemy.stats.maxHealth += 20;
    }

    Instantiate(enemy.transform, sp.position, sp.rotation);
}

In the WaveSpawner you can set a certain number of waves (let's say 5), and after you complete all of the waves I've set them to repeat. So after I beat the 5 waves, I want the 5 waves to repeat and the enemies to be stronger (for example to have their max hp increased by 20).

I do that with the line: enemy.stats.maxHealth += 20;

But the problem here is that after this line is executed and I Instantiate the enemy prefab, the prefab changes its value in the inspector. So let's say that the initial value of the prefab was 120, after I beat the 5 enemy waves, the value of the prefab is set to 140. Then, after I reset the game, the initial value of the prefab is 140 and the max health of the first enemies is 140 and not 120.

I have tried to not change the maxHealth variable of the EnemyStats class, but instead to have another variable tempMaxHealth and do the changes on it. This doesn't resolve my problem, as other problems arise and I don't know what to do anymore.

I hope that you have an answer for me, I would be grateful if that's the case.

\$\endgroup\$
1
\$\begingroup\$

Remember, the variable called enemy here is your prefab.

Sometimes I'll name these variables something like enemyPrefab so I can't lose track of what's a source asset I shouldn't modify at runtime, versus what's a dynamic instance.

When we write it this way it's obvious:

if (CurrentWave % 5 == 0) {
    enemyPrefab.stats.maxHealth += 20;
}

We just changed the prefab with this line, so it's natural that we see it change in the inspector.

Instead we should do something like this - we capture the instance of the enemy prefab that we just spawned and modify that:

Enemy enemyInstance = Instantiate(enemyPrefab, sp.position, sp.rotation);

int numHealthBoosts = CurrentWave/5;
enemyInstance.stats.maxHealth += numHealthBoosts * 20;

Any code that works with the maxHealth should be moved from Awake/OnEnable (which run before Instantiate returns) and into Start or a custom initializer method so it runs after our spawning routine finishes and sees the updated value of the stats.

\$\endgroup\$
  • \$\begingroup\$ The only thing I should point out here (for everyone who'll face the same issue) is that the Instantiate method returns a Transform type, and you cannot cast a Transform into an Enemy class (in my case). I mean this line: Enemy enemyInstance = Instantiate(enemyPrefab, sp.position, sp.rotation);. But what I did was to get the component from the instantiated Transform: var stats = enemyInstance.GetComponent<Enemy>().stats. Apart from that, everything's fine and it finally works. Thank you. \$\endgroup\$ – Kapobajza Dec 25 '18 at 15:07
  • \$\begingroup\$ The generic form of Instantiate returns whatever type you pass it, so make sure to pass an Enemy type and you'll get an Enemy out, no GetComponent required. \$\endgroup\$ – DMGregory Dec 25 '18 at 15:34
  • \$\begingroup\$ You are right, once again. I apologize for not noticing it. \$\endgroup\$ – Kapobajza Dec 25 '18 at 15:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.