- Home /
Problems when i have multiple enemies on the same scene C#
This might be a complex one but i have a basic AI script i have written that can "Hear" the player using colliders, the script works fine except when there are multiple enemies at once. When one of them hears the player, they all do. Here is the complete EnemyAI script:
using UnityEngine;
using System.Collections;
public class EnemyAi : MonoBehaviour {
Transform target; //the enemy's target
float moveSpeed = 2; //move speed
float minDist = 0.9f;
float maxDistance = 5;
float rotationSpeed = 3; //speed of turning
bool canHear = false;
float attackDistance = 1;
bool canAttack = true;
Transform player;
float hearRange = 20;
public bool couldHearGun = false;
Transform myTransform; //current transform data of this enemy
// Use this for initialization
void Start () {
target = GameObject.FindWithTag("PlayerCenter").transform; //target the player
}
void Awake()
{
myTransform = transform; //cache transform data for easy access/preformance
}
// Update is called once per frame
void Update () {
player = GameObject.Find("Player").transform;
if(Vector3.Distance(transform.position, player.position) <= hearRange && couldHearGun){
Debug.Log("hear the gun");
canHear = true;
StartCoroutine(HearPlayer(10));
}
GameObject Player = GameObject.Find("Player");
CharacterControler characterControler = Player.GetComponent<CharacterControler>();
if(characterControler.couldHear && characterControler.makesSound){
canHear = true;
}
if(canHear){
StartCoroutine(HearPlayer(10));
}
//rotate to look at the player
if(Vector3.Distance(transform.position, target.position) <= maxDistance || canHear){
myTransform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
}
//move towards the player
if(Vector3.Distance(transform.position, target.position) <= maxDistance || canHear){
if(Vector3.Distance(transform.position, target.position) >= minDist){
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
}
}
if(Vector3.Distance(transform.position, target.position) <= attackDistance && canAttack){
characterControler.health -= 0.15f;
canAttack = false;
characterControler.health -= 0.15f;
StartCoroutine(attackCooldown(1));
}
}
public IEnumerator HearPlayer(float delay){
yield return new WaitForSeconds(delay);
canHear = false;
}
public IEnumerator attackCooldown (float delay){
yield return new WaitForSeconds(delay);
canAttack = true;
}
}
and here is the complete gun script which makes the sound:
using UnityEngine;
using System.Collections;
public class PistolRay : MonoBehaviour {
bool canFire = true;
public GameObject decal;
public float clipAmmo = 8;
public float storedAmmo = 80;
float missingShots = 0;
GameObject player;
GameObject characterControler;
bool reloading = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
player = GameObject.Find("Player");
CharacterControler characterControler = player.GetComponent<CharacterControler>();
characterControler.ammoCount = clipAmmo;
characterControler.totalAmmo = storedAmmo;
RaycastHit hit;
Ray shooterRay = new Ray(transform.position, transform.forward);
if(Input.GetButton("Fire1") && canFire && clipAmmo > 0){
if(GameObject.Find ("EnemyAi") != null){
GameObject enemy = GameObject.Find("EnemyAi");
Debug.Log("Test");
EnemyAi enemyAi = enemy.GetComponent<EnemyAi>();
enemyAi.couldHearGun = true;
StartCoroutine (SoundCooldown(1));
}
}
if(Input.GetButton("Fire1") && canFire && clipAmmo > 0){
Debug.DrawRay (transform.position, transform.forward, Color.green);
if(Physics.Raycast(shooterRay, out hit, Mathf.Infinity)){
if(hit.collider.tag == "Enemy"){
hit.transform.GetComponent<EnemyCC>().health -= 15;
}
}
var hitRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
Instantiate(decal, hit.point, hitRotation);
//GameObject decalInstance =
//decal.transform.parent = hit.transform;
canFire = false;
clipAmmo -= 1;
missingShots += 1;
StartCoroutine(FireRate(1));
}
if(Input.GetButtonDown("Fire1") && canFire && clipAmmo == 0 && !reloading){
reloading = true;
StartCoroutine(ReloadTime(3));
}
if(Input.GetButtonDown("Reload") && storedAmmo != 8 && !reloading){
reloading = true;
StartCoroutine(ReloadTime(3));
}
}
public IEnumerator FireRate (float delay){
yield return new WaitForSeconds(delay);
canFire = true;
}
public IEnumerator SoundCooldown(float delay){
GameObject enemy = GameObject.Find("EnemyAI");
EnemyAi enemyAi = enemy.GetComponent<EnemyAi>();
yield return new WaitForSeconds(delay);
enemyAi.couldHearGun = false;
}
public IEnumerator ReloadTime (float delay){
yield return new WaitForSeconds(delay);
reloading = false;
if(storedAmmo >= 8){
storedAmmo -= missingShots;
clipAmmo += missingShots;
missingShots = 0;
}
}
}
there is also a character controller script which does a similar thing but its a long script and very messy but i can post if needed.
thanks for any help
There are a thousand and one inefficiencies in this code that you probably want to deal with.
Caching your own transform does nothing for performance
Don't call Find every frame, call it once during start and store the result. Same with GetComponent
You call the HearPlayer coroutine every frame. This has the potential to start hundreds of coroutines that all do the same thing. A better option would be to start the coroutine in the set method of canHear.
You calculate distance for a comparison every frame. Use Vector3.sqr$$anonymous$$agnitude ins$$anonymous$$d
That's just a few places to start. I would strongly suggest using events for this structure ins$$anonymous$$d.
Answer by Kiwasi · Jul 22, 2014 at 05:28 AM
As I see it you problem could be on line 39. Hear you check a couple of values on the characterController, then set canHear to true. canHear will be set true on every frame that the characterController makes a sound.
To fix this simply add the distance check to line 39.
thanks for the reply, i assume you mean change it to:
if(Vector3.Distance(transform.position, target.position) <= hearRange){
if(characterControler.couldHear && characterControler.makesSound){
canHear = true;
}
}
which has the effect of only one of the enemies hearing the gunshot sound. i think its to do with the Gameobject.find only finding and storing the first target (PistolRay line 31: GameObject enemy = GameObject.Find("EnemyAi");) but i am not to sure
That is correct. GameObject.Find returns the first object Unity finds. You could make a List of Enemies and use foreach. But events will ultimately be better.
i have had no experience with lists and i dont quite understand the tutorials i was just watching, would you $$anonymous$$d explaining how i would make it work?
Lists are pretty easy. They function the same as arrays, but are sized dynamically. Some important pseudo code follows.
// This line tells C# you want to use Lists
using system.collections.generic;
// Declare and initialise a List like so
private List<EnemyAI> enimies = new List<EnemyAI>();
// Call the add method to add a new item
enimies.add(enemyAI);
// Use foreach to do something to every item
foreach(EnemyAI enemy in enimies){
enemy.couldHearGun = false;
// Any other code that needs to apply to each enemy goes in here
}
was that all supposed to go in Update? aside from: using system.collections.generic;
il keep fiddling with it but having the rest in update causes a ton of errors
EDIT: ok i forgot to declare it properly Derp... well now i got that done the only error i get is: enemies.add(EnemyAi); 'system.collections.generic.list' does not contain a definition for 'add'
EDIT EDIT: OH typo in your script. its Add with a capitol. now i just have another error saying that enemies.Add(EnemyAi); 'EnemyAi is a type but used like a variable'