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.
public class Blue_Enemy : MonoBehaviour {
public Transform target;
public int speed = 3;
public GameObject enemy;
public int health = 1;
public Transform spawnPoint;
public bool dead = false;
public float time;
public int timeIncrease = 1;
public GameObject bulletPrefab;
public Transform bulletSpawn;
public float fireRatep;
public float fireRatem;
private float nextFire = 3.0f;
void Awake()
{
//time increment and random.range
InvokeRepeating("AddingTime", 1, 1);
float x = UnityEngine.Random.Range(1, 10);
}
void Update()
{
//movement and firing.
if (time >= 20)
{
transform.LookAt(target);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
if (Time.time > nextFire)
{
Fire();
}
}
void AddingTime()
{
//time increasing
time += timeIncrease;
}
void OnCollisionEnter(Collision collision)
{
//bullets
if (collision.gameObject.tag == "player_bullet")
{
health--;
}
if (health <= 0)
{
Destroy(enemy);
}
//Working on it.
if (collision.gameObject.tag == "Walls")
{
Destroy(enemy);
//var enemies = (GameObject)Instantiate(blueEnemies, spawnPoint.position, spawnPoint.rotation);
//blueEnemies.transform.position = spawnPoint.position;
//print("hey");
}
}
void Fire()
{
//Create the Bullet from the Bullet Prefab
var bullet = (GameObject)Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation);
nextFire = Time.time + UnityEngine.Random.Range(fireRatep, fireRatem);
//Destroy the bullet
Destroy(bullet, 1.0f);
}
}
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”).
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:
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.
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.
void Update()
{
if(iAmAlive == true)
{
//movement and firing.
if (time >= 20)
{
transform.LookAt(target);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
if (Time.time > nextFire)
{
Fire();
}
}else{
//more code for when he dies, maybe a fall movement?
}
}
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?
So i fixed all that now my enemies wont respawn after destroying.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Red_Enemy : MonoBehaviour {
public int speed = 3;
public GameObject enemy;
public int health =1;
public float bottomConstraint = Screen.height;
public float topConstraint = Screen.height;
public float buffer = 1.0f; //set this so the spaceship disappears offscreen before reappearing on other side
public float distanceZ = 10.0f;
public int scoreValue = 50;
public float time;
public int timeIncrease = 1;
public GameObject bulletPrefab;
public Transform bulletSpawn;
public float fireRatep;
public float fireRatem;
private float nextFire = 3.0f;
void Awake()
{
InvokeRepeating("AddingTime", 1, 1);
float x = UnityEngine.Random.Range(1, 10);
bottomConstraint = Camera.main.ScreenToWorldPoint(new Vector3(0.0f, 0.0f, distanceZ)).y;
}
void Update()
{
if (time > 5)
{
//transform.LookAt(target);
transform.Translate(Vector3.down * speed * Time.deltaTime);
}
if (transform.position.y < bottomConstraint - buffer)
{
StartCoroutine(Dead());
}
if (Time.time > nextFire)
{
Fire();
}
}
void AddingTime()
{
time += timeIncrease;
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "player_bullet")
{
health--;
}
if(health==0)
{
//Destroy(enemy);
StartCoroutine(Dead ());
Score_Manager.Score += scoreValue;
}
}
IEnumerator Dead()
{
Debug.Log("Dead");
this.gameObject.GetComponent<Renderer>().enabled = false;
yield return new WaitForSeconds(5);
Debug.Log("respawn");
this.gameObject.GetComponent<Renderer>().enabled = true;
}
void Fire()
{
//Create the Bullet from the Bullet Prefab
var bullet = (GameObject)Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation);
nextFire = Time.time + UnityEngine.Random.Range(fireRatep, fireRatem);
//Destroy the bullet
Destroy(bullet, 1.0f);
}
}
Im trying to also get random respawn and I have another code for that as well.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Enemy_Control : MonoBehaviour {
public Score_Manager Score; //Reference to the player's score
public GameObject enemy; //The enemy prefab to be spawned
public float spawnTime = 2f; //How long inbetween each spawn
public Transform[] spawnPoints; //An array of the spawn points this enemy can spawn from
void Start()
{
//Call the spawn function afer a delay of the spawnTime and then contine to call after the same amount
InvokeRepeating("Spawn", spawnTime, spawnTime);
}
void Spawn()
{
//if the player has no health left...
if(Score.currentScore <= 0)
{
//... exit the function
return;
}
//Find random index between zero and one less than the number of spawn points
int spawnPointIndex = UnityEngine.Random.Range(0, spawnPoints.Length);
//create an instance of the enemy prefab at the randomly selected spawn point's position and rotation
Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
}
void onTriggerEnter(Collider other)
{
if(other.gameObject.tag == "Player")
{
Destroy(enemy);
print("hey2");
}
}
}
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).
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.
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.
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.
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
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).
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…
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.
public List <GameObject> Enemies; // just add 5 from the scene here in the inspector for the start.
GameObject GetEnemy() {
for(int i = 0 ; i < Enemies.Count; ++i) {
if(!Enemies[i].activeSelf) return Enemies[i];
}
GameObject g = Instantiate(prefabOfEnemy);
Enemies.Add(g);
return g;
}
// in your spawn
void Spawn() {
GameObject spawn = GetEnemy();
// here you can:
spawn.GetComponent<Red_Enemy>().resetVariables(); // if you have to reset things from enemies that previously died
// you can set its position if need be , too.
}
// in the other script that controls the enemy, when it dies
gameObject.setActive(false); // that's it. It's like back in the "pool", because the pool checks for disabled.