Mega Bug In Turret - Help

Okay, Still having problems,

I have a turret on my scene that has been duplicated a few times. All the turret does is looks at an enemy within its radius and shoots at it. The enemy are from an array (they are initiated prefabs). So every enemy that steps into the turrets radius it is added to a Generic.List. Heres what works fine: Enemy is added to the List when they enter radius (collider), and are removed when they move out. But a bug crops up when the turret manages to kill (destroy) the enemy, and some turrets targets are 'missing' due to this, therefore they just stop, and don't do anything?

Watch My Video: www.olliejones.com/TurretHelp2.mov

The turrets target is its current enemy selected on the List, so when that gets destroyed, it doesn't find a new enemy, and I don't know how to code for that not to happen.

I have 2 scripts, one called SmoothLookAt (attached to the turret) and DamageReceiver (attached to enemy prefab). I believe this is happening because one of my variables is a static, but I need this for my DamageReicver to talk to my SmoothLookAt.

Here Are My Scripts:

SmoothLookAt:

static var damping = 6.0;
static var smooth = false;
static var ActivateScorpionRange = false;
static var ScorpionRange : float = 8;

static var Scorpion : boolean = true;

var target : Transform;
var closeObjects = new System.Collections.Generic.List.<Transform>();
static var EnemyFound = false;

//static var LookAtDead = false;

function Update () 
{

    //---Movement of Scorpion---\\
    if(Scorpion == true)
    {
        if (smooth) //Smooth Rotation\\ -- Note To Self - damping = lagged fire
        {
            if(closeObjects.Count == 0)
            {
                target = null;
                return;
            }

            target = closeObjects[0];
            var rotation = Quaternion.LookRotation(target.position - transform.position);
            transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
            BroadcastMessage("Fire");

        }
        else //Snap Rotation\\
        {
            //Make sure there is an enemy. \\
            if(closeObjects.Count == 0)
            {
                target = null;
                return;
            }

            target = closeObjects[0];
            transform.LookAt(target);
            BroadcastMessage("Fire");
            //closeObjects.Remove(transform);

        }
    }

    //DamageReceiver uses this to remove target once destroyed \\
    if(DamageReceiver.Dead == true) // I BELIEVE THIS MIGHT BE THE PROBLEM - STATIC!
    {
        closeObjects.Remove(target);
        target = null;
        DamageReceiver.Dead = false;
        return;
    }       
}

//When an enemy enters the trigger, add them to the list.    
function OnTriggerEnter (enemy : Collider) 
{
     closeObjects.Add(enemy.transform);
}

//When they leave, take them off of the list.

function OnTriggerExit (enemy : Collider) 
{
    closeObjects.Remove(enemy.transform);
}

DamageReciver:

var hitPoints = 10.0;
var detonationDelay = 0.0;
var explosion : Transform;
var deadReplacement : Rigidbody;
var credits = 100;

static var Dead = false; //this is used to tell SmoothLookAt the enemy has been destroyed - now remove it from list.

function ApplyDamage (damage : float) {
    // We already have less than 0 hitpoints, maybe we got killed already?
    if (hitPoints <= 0.0)
        //credits += 100;
        return;

    hitPoints -= damage;
    if (hitPoints <= 0.0) {
        // Start emitting particles
        var emitter : ParticleEmitter = GetComponentInChildren(ParticleEmitter);
        if (emitter)
            emitter.emit = true;

        Invoke("DelayedDetonate", detonationDelay);
    }
}

function DelayedDetonate () {
    Dead = false;
    BroadcastMessage ("Detonate");
    //BroadcastMessage("LookAtDead");
    Dead = true;
}

function Detonate () {
    // Destroy ourselves

    //GetComponent closeObjects.Remove
    Destroy(gameObject);

    // Create the explosion
    if (explosion)
        Instantiate (explosion, transform.position, transform.rotation);

    // If we have a dead barrel then replace ourselves with it!
    if (deadReplacement) {
        var dead : Rigidbody = Instantiate(deadReplacement, transform.position, transform.rotation);

        // For better effect we assign the same velocity to the exploded barrel
        dead.rigidbody.velocity = rigidbody.velocity;
        dead.angularVelocity = rigidbody.angularVelocity;
    }

    // If there is a particle emitter stop emitting and detach so it doesnt get destroyed
    // right away
    var emitter : ParticleEmitter = GetComponentInChildren(ParticleEmitter);
    if (emitter) {
        emitter.emit = false;
        emitter.transform.parent = null;
    }
}

Please help me out, this has been bothering me for sometime now, and I can't make any progress with my game until this is fixed.

Thanks, Ollie

http://technology.blurst.com/unity-physics-trigger-collider-examples/

An object destroyed inside of a trigger will not send OnTriggerExit events. This link will explain a fix if you think this is your problem.

Err never mind you seem to understand this. Well it is a good link anyway.

Do the dead turrets ever get removed from the list?

I'm not an expert on static but it could mean that there is one DamageReceiver.Dead for all DamageReceiver components. So you should perhaps make Dead public and not static. Also, the unity convention would be dead (lowercase) since it is a variable.

It does not need to be static. Here's an idea:

function OnTriggerEnter (enemy : Collider) { closeObjects.Add(enemy.gameObject); }

Keep a list of gameObjects instead of transforms. Then you can call:

transform.LookAt(target.GetComponent(Transform));

or

if(target.GetComponent(DamageReceiver).dead) { //blah }

Oops again. I guess I assumed DamageReceiver was a component of all target gameobjects.

In any case, there is certainly a solution without that static and an array of gameObjects could help with that.

I dunno I gotta go to sleep.

Looking at your code comments it appears the only reason 'Dead' is static is so that it is visible inside SmoothLookAt. Using statics and BroadcastMessage for communication between classes is not needed when you have an instance of the object. I agree that you should use GetComponent as Kliffy suggested.

Get an instance of your script component from the Transform and call functions on it directly.

I noticed you are using quite a few static variables. Static variables are easy to use because they're accessible everywhere, but they have major drawbacks and should be used sparingly. Having global scope means whenever you have a bug, that variable is in visible so it can't be ruled out, implicitly increasing the complexity of all your code. Another issue is everything can modify it, so if you've discovered that a variable has the wrong value you can't make any assumptions about what changed it, again because everything has access.

I'm not sure what causes that error based on what I have in front of me. But, I would use your ontriggerenter/exit to make a list of GameObjects of targets. I would make dead a public var. You could just make a function

function IsDead() { return dead; }

and you can call that from the turret script. In that case dead could even be private, and you could make a function for the turret to call to change dead from true to false. And then each target will have its own dead variable.

What is dead receiver attached to currently? When you tell it to Destroy(gameObject) I'm not sure how the subsequent stuff gets called although I guess the destruction happens at the end of the frame?

If you are worried with my implementation that the gameobject will be destroyed before the turret can see that they are dead, you can send a time variable to Destroy(gameobject, time) to have it be destroyed after a certain length of time which would allow a dying animation to be played and while the turret realizes the target is no longer worth shooting / looking at because it is dead.

My guess is that you have multiple turrets changing that static dead back and forth, as well as different targets changing it back and forth and at some point it gets changed in a way / order that you did not intend and causes the bug.

Also if you try to do my suggestion, dead would be a var called in the constructor (aka at the top and outside of functions) of DamageReceiver and it would originally be set to false. And then I presume that the detonation script would just send the message and set dead to true.

What about something like: if(target==null && closeObjects.count > 0) { closeObjects.Remove(target); }

in the update script

I’ve been working on a similar thing for my tower defense, I got this code up and working last night with the use of GameObject.FindGameObjectsWithTag to just search for anything tagged as Enemy (just tag your enemy prefab and it should work out). Oh and you’ll probably have to swap out the GetComponent for the decrement of enemy health with whatever your enemy script name and health value in the firing function

var closest : GameObject;
var rotSpeed : float = 3;
var enemyFound : boolean = false;
var shootRange : float = 35.0;


function Update(){
	FindClosestEnemy();

	if(enemyFound){
		var targetRot = Quaternion.LookRotation(closest.transform.position-transform.position);
	    transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, Time.deltaTime * rotSpeed);

	    if(Vector3.Distance(transform.position, closest.transform.position) < shootRange){
	    	FireAtEnemy();
	    }
	}
}

function FindClosestEnemy () : GameObject {
	var distance = Mathf.Infinity; 
	var position = transform.position; 

	var gos : GameObject[];
	gos = GameObject.FindGameObjectsWithTag("Enemy");

	for (var go : GameObject in gos)  { 
	    var diff = (go.transform.position - position);
	    var curDistance = diff.sqrMagnitude; 
	    if (curDistance < distance) { 
	        closest = go; 
	        distance = curDistance;
	        enemyFound = true;
	    } 
	} 
	return closest;
}

function FireAtEnemy(){
	var enemyHP = closest.GetComponent("enemyScript");
	enemyHP.health = enemyHP.health -1;
}