Hello, let me start by saying I am not very experienced in C# or Unity, so please have patience with my explanation.
My Setup:
I am trying to spawn a mob a variable number of times. The spawn count can change randomly. The object being spawned is a prefab with nested trigger colliders set to different radii that each activate a different mob behavior(shoot, melee attack, and shield) when breached. These colliders are empty objects named for their function and have scripts attached that use Boolean reference variables of the parent to toggle the state of the parent’s behavior on trigger enter and exit(shoot on enter, cease fire on exit).
My Problem:
Only the first instantiation of the prefab has total functionality. Its position is set to one of six different spawn points. Its stats are set to one of three categories(weak, average, and strong). It correctly follows and auto faces the player. It takes damage correctly and destroys when health is at zero or out of bounds. When the colliders are entered the behaviors are toggled and executed correctly and on exit the behaviors stop correctly.
Every other prefab after the first instantiation has partial functionality. Only positioning, movement and destruction out of bounds work correctly. These are not triggered by the colliders or scripts with the reference variables. Behaviors that are triggered with reference variables cause a null reference error and are traced back to the scripts attached to colliders or in the case of take damage, the player related script that references the mob’s health in order to deduct from it.
The following is a sample of code that uses the reference:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShoot : MonoBehaviour
{
// reference variable
private Enemy enemyController;
// Start is called before the first frame update
void Start()
{
// allow access to enemy
enemyController = GameObject.Find("Mob(Clone)").GetComponent<Enemy>();
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D collision)
{
// activates enemy projectile upon collider breach
if (collision.gameObject.CompareTag("Player"))
{
enemyController.projectileHasTarget = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
// deactivates enemy projectiles upon collider breach
if (collision.gameObject.CompareTag("Player"))
{
enemyController.projectileHasTarget = false;
}
}
}
What I think is happening, is that since every mob is named Mob(Clone) in the hierarchy, only one is recognized as being Mob(Clone) and the rest are passed over. Another possibility is that all instances are recognized but only the last instance is set.
Would anyone be able to tell me what I am doing wrong or provide an alternate solution to my problem?
Thanks ahead of time, I will keep trying in the meantime.
Upon further testing I believe the problem lies in my spawn code.
void SpawnEnemy (int numberOfEnemies)
{
for (int i = 0; i < numberOfEnemies; i++)
{
int spawnLocation = Random.Range(0, spawners.Length);
Instantiate(enemy, spawners[spawnLocation].transform.position, enemy.transform.rotation);
enemy.GetComponent<Enemy>().SetEnemyType(AnySidedDie(0, mobTypes));
}
}
The game seems to see only one prefab even though they all act separately. Damaging one mob seems to damage another random mob. The shoot, melee and shield are also triggered by what seems to be a random mobs collider. The mob that performs those actions are always the first mob listed in the hierarchy.
I need a way to give each instance of the prefab some type of unique identifier, so their respective colliders sync after creation and their behaviors get triggered appropriately.
I’ve tried creating a List and Add()ing them, but that fails after the first for loop and only one perfect mob gets created.
I’ve also tried a placeholder variable, which only sets the attributes correctly and does not solve the collider trigger problem. The random behavior described above still persists.
I’ve also tried every available GameObject method to find the object(Find, find by type, and find with tag), all with the same result as the original reference code used.
I could always just make separate mobs and not use prefabs, but that would be silly, cumbersome, and inefficient. Prefabs are definitely what I need.
Any help would be greatly appreciated.
The original spawn code was definitely part of the problem. Every loop referred to the same instance of the prefab as evidenced by GetInstanceID(). Using a variable to hold each instantiation is most likely the proper choice, but the random behaviors still persist. The following shows my new SpawnEnemy() that also assigns the instance id to the prefab each time it is instantiated.
void SpawnEnemy (int numberOfEnemies)
{
for (int i = 0; i < numberOfEnemies; i++)
{
GameObject mob;
int id;
int spawnLocation = Random.Range(0, spawners.Length);
mob = Instantiate(enemy, spawners[spawnLocation].transform.position, enemy.transform.rotation);
mob.GetComponent<Enemy>().SetEnemyType(AnySidedDie(0, mobTypes));
id = mob.GetInstanceID();
mob.GetComponent<Enemy>().id = id;
Debug.Log(id);
}
}
It shouldn’t matter that I am using the instance id to find the specific mob as each mob is only required as a one time use object and doesn’t need to be saved.
I still can’t add these variables to a List properly either, which I thought would have solved the problem.
How can I use this new id variable to control the specific colliders associated with each prefab?
Or should I be getting the IDs of the collider objects as well and attaching them to each mob script to be used for the if statement in the collider trigger script?
Or should I give up on this altogether and find a different way to implement mob AI since the prefab seems incapable of differentiating between copies of itself?
This method of controller seems like it is going to be to inefficient to run smoothly with all the object references needed.
Can someone please point me in the right direction for adding AI to my prefab, even if you can’t help me use this method I devised?
Found the Solution!!!
In the SpawnEnemy method I needed to add a placeholder variable to get it to produce a new instance every loop, allowing it to assign mob type accurately.
To deal with the random damage I needed the collision to use GetComponent<>() to access the specific enemy’s health.
To have the specific prefab’s corresponding collider trigger its desired behavior, I needed the gameObject to GetComponentInParent<>() to allow access the Boolean and toggle it on trigger enter and exit.
Hope someone else finds this useful.
Moral of the story:
Sometimes talking to a wall is better than talking to a real person.
1 Like