Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Respawn Enemy on Collision?

Discussion in 'Getting Started' started by jwathen, Jun 15, 2017.

  1. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    Hello everyone! I am currently working on a SHUMP project for a class and I am having trouble with enemies respawning after they go below the screen. (I put a wall there so it could collide with it.) I've tried loads of different codes and none of them have worked. The nearest I got to that was on a OnTriggerEvent, but it would move the wall and not the enemy.

    Basically this blue enemy needs to randomly shoot off a bullet (which it does), move towards the player (done), enemy is destroyed if hit with a player bullet (done), and respawn when it goes below the screen (does not).

    I don't know if I need to change my code completely or maybe try lerp? Any help would be extremely helpful.

    This is what the set up looks like off screen.


    So here is everything for my blue enemy:

    1. Inspector:


    2. Script:

    Code (CSharp):
    1. public class Blue_Enemy : MonoBehaviour {
    2.  
    3.     public Transform target;
    4.     public int speed = 3;
    5.  
    6.     public GameObject enemy;
    7.     public int health = 1;
    8.     public Transform spawnPoint;
    9.     public bool dead = false;
    10.  
    11.     public float time;
    12.     public int timeIncrease = 1;
    13.  
    14.     public GameObject bulletPrefab;
    15.     public Transform bulletSpawn;
    16.     public float fireRatep;
    17.     public float fireRatem;
    18.     private float nextFire = 3.0f;
    19.  
    20.     void Awake()
    21.     {
    22.         //time increment and random.range
    23.         InvokeRepeating("AddingTime", 1, 1);
    24.  
    25.         float x = UnityEngine.Random.Range(1, 10);
    26.     }
    27.  
    28.     void Update()
    29.     {
    30.         //movement and firing.
    31.         if (time >= 20)
    32.         {
    33.             transform.LookAt(target);
    34.             transform.Translate(Vector3.forward * speed * Time.deltaTime);
    35.         }
    36.  
    37.         if (Time.time > nextFire)
    38.         {
    39.             Fire();
    40.         }
    41.     }
    42.  
    43.     void AddingTime()
    44.     {
    45.         //time increasing
    46.         time += timeIncrease;
    47.     }
    48.  
    49.     void OnCollisionEnter(Collision collision)
    50.     {
    51.         //bullets
    52.         if (collision.gameObject.tag == "player_bullet")
    53.         {
    54.             health--;
    55.         }
    56.  
    57.         if (health <= 0)
    58.         {
    59.             Destroy(enemy);
    60.         }
    61.  
    62.         //Working on it.
    63.         if (collision.gameObject.tag == "Walls")
    64.         {
    65.             Destroy(enemy);
    66.             //var enemies = (GameObject)Instantiate(blueEnemies, spawnPoint.position, spawnPoint.rotation);
    67.             //blueEnemies.transform.position = spawnPoint.position;
    68.             //print("hey");
    69.         }
    70.     }
    71.  
    72.     void Fire()
    73.     {
    74.         //Create the Bullet from the Bullet Prefab
    75.         var bullet = (GameObject)Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation);
    76.  
    77.         nextFire = Time.time + UnityEngine.Random.Range(fireRatep, fireRatem);
    78.  
    79.         //Destroy the bullet
    80.         Destroy(bullet, 1.0f);
    81.     }
    82. }
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Is the part about collision with the walls related to your question?
    If so, you could simply set the enemy's position there, instead of destroying it. Then, reset any variables on the instance that may need resetting.

    For comparing tags, it's a little bit more efficient to use: CompareTag (eg: collision.CompareTag("whatever here"); instead of collision.tag == "whatever here").
     
    vegetamaker likes this.
  3. vegetamaker

    vegetamaker

    Joined:
    Feb 28, 2017
    Posts:
    37
    The truth is that I understand you. As a newbie I am still finding it difficult to understand the difference between "collision" and "collider" and how they interact with each other and how the methods works.

    Here is a page that might be of great help to you:
    https://docs.unity3d.com/Manual/CollidersOverview.html

    For now, maybe you should try this: set TRIGGER to on in your Wall's Collider. I suppuse that it is OFF in your enemy.

    Then this method should do the trick:
    Code (CSharp):
    1. void OnTriggerEnter (Collider other)
    2. {
    3. if (other.gameObject. == "Walls")
    4. {
    5. //Your Code
    6. }
    7. }
    ====================

    On the other hand your problem can also come because you are destroying the enemy when his life is 0, so it will never touch the wall, because he doesn't exist anymore.

    If this is the problem you should create a bool that if is true, active all your enemy movement and shooting (I am talking about the code that you have in Update). And instead of destroy the enemy, set that bool to false, so he will stop move and fire.

    Code (CSharp):
    1. void Update()
    2.     {
    3.     if(iAmAlive == true)
    4. {
    5.         //movement and firing.
    6.         if (time >= 20)
    7.         {
    8.             transform.LookAt(target);
    9.             transform.Translate(Vector3.forward * speed * Time.deltaTime);
    10.         }
    11.         if (Time.time > nextFire)
    12.         {
    13.             Fire();
    14.         }
    15. }else{
    16. //more code for when he dies, maybe a fall movement?
    17. }
    18.     }
    and

    Code (CSharp):
    1.         if (health <= 0)
    2.         {
    3.             iAmAlive = false;
    4.         }
    I hope it helps you, good luck with your project!
     
  4. vegetamaker

    vegetamaker

    Joined:
    Feb 28, 2017
    Posts:
    37
    Didn't know that CompareTag, thanks :)
     
  5. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    You're welcome :)
     
  6. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    I didn't have any luck. It will either go through the bottom wall or it will get stuck in it. :l
     
  7. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    So, if you don't destroy with the "Walls" collision and just print "Hey" .. does that appear in the log?
    In other words, is the collision being/not being detected?
     
    jwathen likes this.
  8. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    So i fixed all that now my enemies wont respawn after destroying.
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.AI;
    6.  
    7. public class Red_Enemy : MonoBehaviour {
    8.  
    9.     public int speed = 3;
    10.  
    11.     public GameObject enemy;
    12.     public int health =1;
    13.  
    14.     public float bottomConstraint = Screen.height;
    15.     public float topConstraint = Screen.height;
    16.  
    17.     public float buffer = 1.0f; //set this so the spaceship disappears offscreen before reappearing on other side
    18.  
    19.     public float distanceZ = 10.0f;
    20.     public int scoreValue = 50;
    21.  
    22.     public float time;
    23.     public int timeIncrease = 1;
    24.  
    25.     public GameObject bulletPrefab;
    26.     public Transform bulletSpawn;
    27.     public float fireRatep;
    28.     public float fireRatem;
    29.     private float nextFire = 3.0f;
    30.  
    31.     void Awake()
    32.     {
    33.         InvokeRepeating("AddingTime", 1, 1);
    34.  
    35.         float x = UnityEngine.Random.Range(1, 10);
    36.  
    37.         bottomConstraint = Camera.main.ScreenToWorldPoint(new Vector3(0.0f, 0.0f, distanceZ)).y;
    38.     }
    39.  
    40.     void Update()
    41.     {
    42.  
    43.         if (time > 5)
    44.         {
    45.             //transform.LookAt(target);
    46.             transform.Translate(Vector3.down * speed * Time.deltaTime);
    47.         }
    48.  
    49.         if (transform.position.y < bottomConstraint - buffer)
    50.         {
    51.             StartCoroutine(Dead());
    52.         }
    53.  
    54.         if (Time.time > nextFire)
    55.         {
    56.             Fire();
    57.         }
    58.     }
    59.  
    60.     void AddingTime()
    61.     {
    62.         time += timeIncrease;
    63.     }
    64.  
    65.     void OnCollisionEnter(Collision collision)
    66.     {
    67.  
    68.         if (collision.gameObject.tag == "player_bullet")
    69.         {
    70.             health--;
    71.         }
    72.  
    73.         if(health==0)
    74.         {
    75.             //Destroy(enemy);
    76.             StartCoroutine(Dead ());
    77.             Score_Manager.Score += scoreValue;
    78.         }
    79.     }
    80.  
    81.     IEnumerator Dead()
    82.     {
    83.         Debug.Log("Dead");
    84.         this.gameObject.GetComponent<Renderer>().enabled = false;
    85.         yield return new WaitForSeconds(5);
    86.         Debug.Log("respawn");
    87.         this.gameObject.GetComponent<Renderer>().enabled = true;
    88.     }
    89.  
    90.     void Fire()
    91.     {
    92.         //Create the Bullet from the Bullet Prefab
    93.         var bullet = (GameObject)Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation);
    94.  
    95.         nextFire = Time.time + UnityEngine.Random.Range(fireRatep, fireRatem);
    96.  
    97.         //Destroy the bullet
    98.         Destroy(bullet, 1.0f);
    99.     }
    100. }
    101.  
     
  9. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    Im trying to also get random respawn and I have another code for that as well.
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.AI;
    6.  
    7. public class Enemy_Control : MonoBehaviour {
    8.  
    9.     public Score_Manager Score;       //Reference to the player's score
    10.     public GameObject enemy;            //The enemy prefab to be spawned
    11.     public float spawnTime = 2f;        //How long inbetween each spawn
    12.     public Transform[] spawnPoints;    //An array of the spawn points this enemy can spawn from
    13.  
    14.     void Start()
    15.     {
    16.         //Call the spawn function afer a delay of the spawnTime and then contine to call after the same amount
    17.         InvokeRepeating("Spawn", spawnTime, spawnTime);
    18.     }
    19.  
    20.     void Spawn()
    21.     {
    22.         //if the player has no health left...
    23.         if(Score.currentScore <= 0)
    24.         {
    25.             //... exit the function
    26.             return;
    27.         }
    28.  
    29.         //Find random index between zero and one less than the number of spawn points
    30.         int spawnPointIndex = UnityEngine.Random.Range(0, spawnPoints.Length);
    31.  
    32.         //create an instance of the enemy prefab at the randomly selected spawn point's position and rotation
    33.         Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
    34.     }
    35.  
    36.     void onTriggerEnter(Collider other)
    37.     {
    38.         if(other.gameObject.tag == "Player")
    39.         {
    40.             Destroy(enemy);
    41.             print("hey2");
    42.         }
    43.     }
    44. }
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    For your first script, starting the coroutine in Update() is probably not what you want. That will start one up every frame.
    You need to limit it to start once, when your condition occurs and prevent further calls until it's complete (and the condition happens, again).

    What's wrong with the second script?
     
  11. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    Nothing, I thought it might help.

    So for the first script, I don't call the coroutine when the enemy is passed that position?
    When it goes below the screen I want that particular enemy to die but I want them to still keep spawning. But I if I call Destroy object then I can't respawn or instantiate a new one.
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Just so we don't get confused about which part we're talking about.. there's nothing wrong with your coroutine, per se, as you have it. It's just that I would say don't run it every update without a condition that it's finished.
    Anything else aside, I'd say even having a bool that is set true when the coroutine is called, and at the end of it, set back to false.
    Then, where you call the coroutine, only allow the call if that bool is false. That make sense?

    After that, if something else is wrong we could talk about it.
     
  13. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Though, you disable the renderer but the object isn't moved at all.. so if Dead() were called , then 5 seconds later its renderer is back on.. Not sure if that's what you want, or to move it before that.
     
  14. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    I will test it out and let you know! :) Thank you so much for all the help!
     
  15. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    Im trying to bring the prefab back to the scene. I get the error lining said the assets was destroyed
     
  16. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Alright, let me try from the beginning.

    A good way to handle your enemies would be from an enemy manager.
    This way, you can continue spawning, without regard to destruction of the enemy or the game object being disabled.

    As for destroying your enemies, you could use 'destroy' and instantiate, but I think a slightly better idea would be to try pooling the objects. All this means is that you add a # of enemies at the start (a number you consider "good"). When one dies, you disable it and keep it in a list. Now, when you need a new one, you check the list for a disabled object.
    If one is available, you use that object (resetting any values if need be to their defaults).
    If one is not found (none are disabled - meaning every enemy in the list is in-use), then you create a new one (also add it to the list of objects (ie: "the pool").

    Now, you always have as many objects as you need on demand without having to create new ones as often, or only when needed.

    Add this to your game flow and things should be good :)
     
  17. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    You may already have the enemy manager if that's what "Enemy_Control" is. (probably is).

    So here, instead of destroying, use the pool list I mentioned in my last post.

    Then, in the script that does "Dead()" just disable the object when it dies. In fact, in that script, you don't even need to re-enable the object, because your Enemy Control will be spawning new enemies at regular intervals and can take it back from the pool there (instead of the script that determined it died).
     
  18. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    Oh, so pretty much I need to make a list and I will be good?
    Cause I had respawning work once but it was respawning so quickly that it was lagging my game. I think thats where a list would be handy then I won't over crowd my scene..
     
  19. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Sure.. Let's say you commonly have x number of enemies. Could be +/- a few.
    I'd say, add those 5 to the scene to begin with.
    then, you have a list that keeps those 5 and can grow a little bit if need be.
    Code (csharp):
    1.  
    2. public List <GameObject> Enemies; // just add 5 from the scene here in the inspector for the start.
    3.  
    4. GameObject GetEnemy() {
    5.   for(int i = 0 ; i < Enemies.Count; ++i) {
    6.      if(!Enemies[i].activeSelf) return Enemies[i];
    7.      }
    8.    GameObject g = Instantiate(prefabOfEnemy);
    9.    Enemies.Add(g);
    10.    return g;
    11.  }
    12.  
    13. // in your spawn
    14. void Spawn() {
    15.   GameObject spawn = GetEnemy();
    16.   // here you can:
    17.   spawn.GetComponent<Red_Enemy>().resetVariables(); // if you have to reset things from enemies that previously died
    18.   // you can set its position if need be , too.
    19.   }
    20.  
    21. // in the other script that controls the enemy, when it dies
    22. gameObject.setActive(false); // that's it. It's like back in the "pool", because the pool checks for disabled.
    23.  
     
  20. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I can't say that will 100% be ready for you, but hopefully the main idea is clear :)
     
  21. jwathen

    jwathen

    Joined:
    Mar 16, 2017
    Posts:
    11
    It should lead me to other forms of it! :D Thank you! I will try it out
     
  22. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Cool :)

    Let me know how it goes & if you have any other questions/issues afterwards.