1
\$\begingroup\$

I'm working on a level in a 2D game that includes two big triggers.

Each time the player enters them, I Instantiate an enemy prefab. Then, the player can shoot bullets to destroy that prefab. And finally, the enemy should die by playing an animation, disabling his collider, and destroying itself.

However, the problem is that every time a bullet hits an enemy, I get the warning "Animator is not playing an AnimatorController" and the error "Destroying assets is not permitted to avoid data loss." in the console. And these two prevent the enemy clones from getting destroyed. The warning is caused by the line enemySpawnPoint1.destroyPurpleGoon.GetComponent<Animator>().SetTrigger("dead"); and the error is caused by the line Destroy(enemySpawnPoint1.destroyPurpleGoon, 4f);.

This is the script for the first trigger:

using UnityEngine;

public class EnemySpawnPoint1 : MonoBehaviour
{
    [HideInInspector] public int enemyCount = 0;

    // I assigned the enemy prefab to the Inspector.
    public GameObject purpleGoon;
    public GameObject destroyPurpleGoon;


    private void Awake()
    {
       // I wrote this because for some reason, instantiated enemies fall when the game begins. 
       purpleGoon.GetComponent<CapsuleCollider2D>().enabled = true;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject == Player.Instance.gameObject && enemyCount < 20)
        {
            InstantiateEnemy1();
        }
    }

    void InstantiateEnemy1()
    {
        destroyPurpleGoon = Instantiate(purpleGoon, new Vector3(65.46f, -1.11f, 0), Quaternion.identity);
        enemyCount++;
    }
}

And this is the script for the second trigger:

using UnityEngine;

public class EnemySpawnPoint2 : MonoBehaviour {

    public EnemySpawnPoint1 enemySpawnPoint1;


    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject == Player.Instance.gameObject && enemySpawnPoint1.enemyCount < 20)
        {
            InstantiateEnemy2();
        }
    }

    void InstantiateEnemy2()
    {
        enemySpawnPoint1.destroyPurpleGoon = Instantiate(enemySpawnPoint1.purpleGoon, new Vector3(-1.309999f,
-1.11f, 0), Quaternion.identity);
        enemySpawnPoint1.enemyCount++;
    } 
}

I write and call DestroyInstantiatedEnemy() function in ShotgunBullet script:

public EnemySpawnPoint1 enemySpawnPoint1;


public void DestroyInstantiatedEnemy()
{
    enemySpawnPoint1.destroyPurpleGoon.GetComponent<Animator>().SetTrigger("dead");
    enemySpawnPoint1.destroyPurpleGoon.GetComponent<CapsuleCollider2D>().enabled = false;

    Destroy(enemySpawnPoint1.destroyPurpleGoon, 4f);
}

void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "Enemy")
    {
        DestroyInstantiatedEnemy();
        Destroy(gameObject);
    }
}

The warning is strange to me because the animator components on the original prefab and all the prefab clones have the AnimatorController assigned to them in their Animator components. Even the idle animation plays perfectly fine in the runtime, which shows the Animator component works.

About the error, ideally I should use an empty game object and destroy that instead. But since I destroy the enemy prefabs in another script, I had to make it public. So now, Unity thinks I want to destroy the actual prefab and not the clones. And I can't figure out how to fix it.

I also noticed that sometimes the collider gets disabled on the original prefab (the one in Project window) and dies in Awake. So, I wrote a line of code to keep it enabled from the beginning. This and the fact that I get that error prove that I'm directly making changes to the root prefab instead of the clones. But I don't know why.

It is worth noting that in the other levels, the player uses a knife to kill the enemy prefabs (I directly put those prefabs in the scene without instantiating them) and their death function (which is the same as DestroyInstantiatedEnemy() but without the references to destroyPurpleGoon) works perfectly fine. This means that this warning only occurs in this specific scene where they are instantiated in runtime and can only die by bullets.

I researched a lot about this problem and found some similar questions with solutions on Unity forums; but none of them have worked for me. Additionally, as mentioned in the comments below, I'm aware that the way I use destroyPurpleGoon is problematic but I don't know a better way to use that variable.

I would appreciate it if you tell me the reason behind the warning and the error and how I should fix them.

\$\endgroup\$
9
  • 2
    \$\begingroup\$ You have one obvious error. Your spawn script keeps only the reference to the last spawned enemy. You should fix that first. Basically as soon as you have more than one enemy, you kill one, the last one gets destroyed. Killing a second one still looks at the last one, which is now null \$\endgroup\$
    – Zibelas
    Commented Jul 19, 2023 at 6:02
  • \$\begingroup\$ And second, where are you setting enemyPatrol? \$\endgroup\$
    – Zibelas
    Commented Jul 19, 2023 at 6:12
  • \$\begingroup\$ Thank you for your comments. About your first question, based on my observations, none of the enemies can die with the bullet. So, the last spawned enemy does not get destroyed. Also, shooting every single one of them will create that error (which is in fact a warning but since it's blocking progress, I call it an error). If that's not what you mean, please let me know. I didn't quite understand the problem you're referring to. About your second question, I dragged enemyPatrol script into the Inspector of Enemy Spawn Point 1 game object. \$\endgroup\$
    – Ali
    Commented Jul 19, 2023 at 16:28
  • 1
    \$\begingroup\$ What I'm saying is, how does DestroyInstantiatedEnemy know which of your destroyPurpleGoon should be destroyed? InstantiateEnemy1 is always only saving the latest created destroyPurpleGoon, a previous assigned one is getting overwritten \$\endgroup\$
    – Zibelas
    Commented Jul 19, 2023 at 16:48
  • 1
    \$\begingroup\$ The easiest way would be to have the destroy method either on the enemy itself if you need to destroy/ call more functions or if destroy is enough, the collision script can handle it as well. A spawn script should just handle thr spawning. If different enemies need different clean ups, the spawner will end in a lot of if blocks instead of having it clean on the enemy itself \$\endgroup\$
    – Zibelas
    Commented Jul 19, 2023 at 21:09

2 Answers 2

0
\$\begingroup\$

I haven't tried this, but you probably could, instead of letting the bullet decide what happens to the enemy, add a new component, for example Killable. This component should then be added to every enemy (i.e. the prefab). Inside this new Killable component, you would have a public method Kill:

public void Kill() {
    GetComponent<Animator>().SetTrigger("dead");
    GetComponent<CapsuleCollider2D>().enabled = false;
    Destroy(gameObject, 4f);
}

You can then, from your bullet, see if the colliding object has this component:

void OnTriggerEnter2D(Collider2D collision)
{
    Killable killable = collision.gameObject.GetComponent<Killable>();
    if (killable != null) {
        killable.Kill();
        Destroy(gameObject);
    }
}

This way you have also decoupled the bullet from the enemy, which means that enemies can now take damage from multiple sources if you want. And if we want to add a tougher enemy, like a boss, we can supply a damage value to the Kill method, and decrement a health value instead of just destroying the gameObject immediately.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you very much for your comment! Since I already have a function called Death() with the same lines as Kill() in the enemy script, I used that in this experiment. But I got the same warning along with an error (Destroying assets is not permitted to avoid data loss). This is because here we are destroying the root prefab. I had received that error with the previous versions of my code, but now I use a variable called destroyPurpleGoon in the Instantiate function and then destroy that variable in DestroyInstantiatedEnemy() to avoid the error. \$\endgroup\$
    – Ali
    Commented Aug 1, 2023 at 18:57
  • \$\begingroup\$ To be sure, I also used && enemyPatrol != null) in the first statement in the bullet script but I got the same warning again. It seems like the enemy script is not the problem. I think there is something wrong with the way the bullet script interacts with the prefab and the fact that it uses the root prefab by default. Nevertheless, I really appreciate your help. \$\endgroup\$
    – Ali
    Commented Aug 1, 2023 at 19:01
0
\$\begingroup\$

I solved this problem more than a month ago but forgot to post an answer. So, here's the answer:

Basically, instead of constantly instantiating enemies, I put a number of them above both triggers and deactivated all of them. Then, wrote and added this script to Enemy Trigger 1:

using UnityEngine;

public class EnemyTrigger1 : MonoBehaviour
{
    public GameObject purpleGoon, purpleGoon_2, purpleGoon_3, purpleGoon_4, purpleGoon_5;


    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject == Player.Instance.gameObject && purpleGoon != null)
        {
            purpleGoon.SetActive(true);
        }

        if (collision.gameObject == Player.Instance.gameObject && purpleGoon == null && purpleGoon_2 != null && purpleGoon_3 != null && purpleGoon_4 != null && purpleGoon_5 != null)
        {
            purpleGoon_2.SetActive(true);
            purpleGoon_3.SetActive(true);

            purpleGoon_4.SetActive(true);
            purpleGoon_5.SetActive(true);
        }
    }
}

And added this to Enemy Trigger 2:

using UnityEngine;

public class EnemyTrigger2 : MonoBehaviour
{
    public GameObject purpleGoon_1, purpleGoon_6, purpleGoon_7, purpleGoon_8, purpleGoon_9, purpleGoon_10, purpleGoon_11;
    public EnemyTrigger1 enemyTrigger1;


    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject == Player.Instance.gameObject && purpleGoon_1 != null)
        {
            purpleGoon_1.SetActive(true);
        }

        if (collision.gameObject == Player.Instance.gameObject)
        {
            if (purpleGoon_1 == null && purpleGoon_6 != null && purpleGoon_7 != null && purpleGoon_8 != null && purpleGoon_9 != null && purpleGoon_10 != null && purpleGoon_11 != null)
            {
                if (enemyTrigger1.purpleGoon_2 == null && enemyTrigger1.purpleGoon_3 == null && enemyTrigger1.purpleGoon_4 == null && enemyTrigger1.purpleGoon_5 == null)
                {
                    purpleGoon_6.SetActive(true);
                    purpleGoon_7.SetActive(true);
                    purpleGoon_8.SetActive(true);

                    purpleGoon_9.SetActive(true);
                    purpleGoon_10.SetActive(true);
                    purpleGoon_11.SetActive(true);
                }
            }
        }
    }
}

After dragging and dropping the game objects into the Inspector, everything worked as expected. I didn't get that warning and that error, the enemies played their death animations, disabled their colliders, and destroyed themselves after some time.

These scripts are probably not the best in terms of performance or efficiency, but they fit my needs.

If you're using the scripts for your own project and know how to make them more efficient, feel free to do so.

I hope this answer helps!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .