gameobjects destroyed before acted upon by addexplosionforce.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

namespace Player
{
    public class GrenadeExplosion : MonoBehaviour
    {

        private Collider[] hitColliders;
        public float blastRadius;
        public float explosionPower;
        public LayerMask explosionLayers;
        private float destroyTime = 3;

        void ExplosionWork(Vector3 explosionPoint)
        {
                hitColliders = Physics.OverlapSphere(explosionPoint, blastRadius, explosionLayers);
                foreach (Collider hitCol in hitColliders)
                {
                    if (hitCol.GetComponent<NavMeshAgent>() != null)
                    {
                        hitCol.GetComponent<NavMeshAgent>().enabled = false;

                    }
                    if (hitCol.GetComponent<Rigidbody>() != null)
                    {
                        hitCol.GetComponent<Rigidbody>().isKinematic = false;
                        hitCol.GetComponent<Rigidbody>().AddExplosionForce(explosionPower, explosionPoint, blastRadius, 2, ForceMode.Impulse);
                    }
                if (hitCol.CompareTag("Enemy"))
                {
                    if (hitCol.GetComponent<Rigidbody>().velocity.magnitude == 0)
                    {
                        Destroy(hitCol.gameObject);
                    }
                    else
                    {
                        if (hitCol.GetComponent<NavMeshAgent>() != null)
                        {
                            hitCol.GetComponent<NavMeshAgent>().enabled = true;
                        }
                        if (hitCol.GetComponent<Rigidbody>() != null)
                        {
                            hitCol.GetComponent<Rigidbody>().isKinematic = true;
                        }
                    }
                }
            }
        }

        void OnCollisionEnter(Collision col)
        {

            ExplosionWork(col.contacts[0].point);
            Destroy(gameObject);
        }

        // Use this for initialization
        void Start()
        {

        }

        // Update is called once per frame
        void Update()
        {

        }
    }
}

the desired effect of this code is to destroy the gameobject with the tag enemy after its been blown up in the air and then lands on the floor and comes to stop however it gets destroyed as soon as the grenade collides with the game object with the tag enemy.
How can I get the desired effect?

Edit: I will be so greatful for any help honestly Ive been playing around with this problem for quite a while now :slight_smile:

Well one simple way is to add a time to Destroy. You can estimate how long your explosion is going to take say it takes 2-3 seconds for the enemy to fly up in to the air and hit the ground. Lets just add 1/2 second to be sure and call it 3.5 seconds:

 Destroy(hitCol.gameObject,3.5f);

If that seems to long or short… just change it. There are more complicated ways to determine the exact time the enemy hits the ground by checking colliders on the enemy (assuming you have a collider on your ground). But I bet this will be fine for your purposes.

1 Like

^winnar!

takatok my aim is to have the instantiated game object to disappear when its stopped moving.
Heres what I have come up with so far:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

namespace Player
{
    public class GrenadeExplosion : MonoBehaviour
    {

        private Collider[] hitColliders;
        public float blastRadius;
        public float explosionPower;
        public LayerMask explosionLayers;
        private float destroyTime = 3;
        GameObject[] enemies;
        List<string> hit = new List<string>();
        private int destroyEnemy;

        void ExplosionWork(Vector3 explosionPoint)
        {
            hitColliders = Physics.OverlapSphere(explosionPoint, blastRadius, explosionLayers);
            foreach (Collider hitCol in hitColliders)
            {
                if (hitCol.GetComponent<NavMeshAgent>() != null)
                {
                    hitCol.GetComponent<NavMeshAgent>().enabled = false;

                }
                if (hitCol.GetComponent<Rigidbody>() != null)
                {
                    hitCol.GetComponent<Rigidbody>().isKinematic = false;
                    hitCol.GetComponent<Rigidbody>().AddExplosionForce(explosionPower, explosionPoint, blastRadius, 2, ForceMode.Impulse);
                }
            }
            FindDead();
        }

        void FindDead()
        {
            foreach (GameObject enemy in enemies)
            {
                print(enemy.GetComponent<Rigidbody>().velocity.sqrMagnitude.ToString());
                if (enemy != null)
                {
                    if (enemy.GetComponent<NavMeshAgent>().enabled == false)
                    {
                        if (enemy.GetComponent<Rigidbody>().velocity.sqrMagnitude > 0.1f && hit.Contains(enemy.GetInstanceID().ToString()) == false)
                        {
                            print("Added");
                            hit.Add(enemy.GetInstanceID().ToString());
                        }
                        if (hit.Contains(enemy.GetInstanceID().ToString()) && enemy.GetComponent<Rigidbody>().velocity.sqrMagnitude < 0.1)
                        {
                            print("Destroyed");
                            GameObject.Destroy(enemy.gameObject);
                        }
                    }
                }
            }
        }

        void OnCollisionEnter(Collision col)
        {
            ExplosionWork(col.contacts[0].point);
            Destroy(gameObject);
        }

        // Use this for initialization
        void Start()
        {
            enemies = GameObject.FindGameObjectsWithTag("Enemy");
        }

        // Update is called once per frame
        void Update()
        {
            FindDead();
        }

    }
}

If I compare the squareroot magnitude to above 5 it starts working on gameobjects with rigid bodys above 5 however when It’s compared to values below 0.1 it works if I shoot at adjacent game objects after the original game object has hit the ground can you please help me on this? :slight_smile:

I’m not really sure what your asking. I didn’t understand what you meant here:

But I do see one flaw in your code that could cause problems. Your searching every enemy in your game with FindDead. What if there was an enemy that didn’t get hit with the explosion? I wasn’t following exactly what you were doing with the ID.String… maybe that was your way of getting only the enemies hit by the explosion? Anyway this code here seems more straightforward on checking the enemies:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

namespace Player
{
    public class GrenadeExplosion : MonoBehaviour
    {

        private Collider[] hitColliders;
        public float blastRadius;
        public float explosionPower;
        public LayerMask explosionLayers;
        private float destroyTime = 3;
        List<GameObject> enemies;
        List<string> hit = new List<string>();
        private int destroyEnemy;

        void ExplosionWork(Vector3 explosionPoint)
        {
            hitColliders = Physics.OverlapSphere(explosionPoint, blastRadius, explosionLayers);
            foreach (Collider hitCol in hitColliders)
            {
                if (hitCol.GetComponent<NavMeshAgent>() != null)
                {
                    hitCol.GetComponent<NavMeshAgent>().enabled = false;

                }
                if (hitCol.GetComponent<Rigidbody>() != null)
                {
                    hitCol.GetComponent<Rigidbody>().isKinematic = false;
                    hitCol.GetComponent<Rigidbody>().AddExplosionForce(explosionPower, explosionPoint, blastRadius, 2, ForceMode.Impulse);
                    // store any enemies hit in this list
                    if (hitCol.CompareTag("Enemy"))
                        enemies.Add(hitCol.gameObject);
                }
            }
            FindDead();
        }

        void FindDead()
        {
            if (enemies.Count == 0)
                return;
            List<GameObject> enemiesDestroyed = new List<GameObject>();
            foreach (GameObject enemy in enemies)
            {
                if (enemy.GetComponent<Rigidbody>().velocity.sqrMagnitude < 0.1)
                {
                    enemiesDestroyed.Add(enemy);
                }
            }
            // get rid of any enemy we destoryed from our list of enemies
            // hit by the explosion
            foreach (GameObject destroyedEnemy in enemiesDestroyed)
           {
                enemies.Remove(destroyedEnemy);
                GameObject.Destroy(destroyedEnemy);
            }
        }

        void OnCollisionEnter(Collision col)
        {
            ExplosionWork(col.contacts[0].point);
            Destroy(gameObject);
        }

        // Use this for initialization
        void Start()
        {
            enemies = new List<GameObject>();
        }

        // Update is called once per frame
        void Update()
        {
            FindDead();
        }

    }
}

Thank you for your reply, the method I was using to check if it’s been hit ( if sqr magnitude > 0.1) was to allow for the fact that the navmeshagent doesn’t affect velocity so the enemy will be destroyed straight away in your code ( I have tested it ) I found with my code that it works but… when I call the method, (I added a debug.log at the the top the method) it shows when I call the method ffrom update, it doesnt run the code like the while loop in python I mean it doesnt run the code continuously to check for velocity.sqrMagnitude. The FindDead() method only works when I click the mouse, and Im really not sure how to fix that. Any ideas?

Oh and to explain myself, I want the physics to affect the enemy, it get blown up in the air, then I want it to be destroyed when resting on the ground. I find my script works when for example X enemy(clone) is resting on the ground after been blown up) then I press the mouse button and the grenade works but I cant hit the enemy again, I have to hit a close by enemy Z (clone) or a certain distance away from the X enemy (clone).

The script works better with the > 5 sqr magnitude and I have to blow it up more than a certain velocity. but again only works on mouse clicks so I think this is where the issue lies with my code. Please help takatok.

That doesn’t make much sense to me. Your clearly calling FindDead in Update(). It should run every frame regardless of whether you click the mouse or not.

It might make sense to relegate this out to each enemy. They won’t all stop moving at the same time. We can add a script to all your Enemies and call a function on that script to start checking when it stops moving and destroy itself.
So our enemies will have this script:

using System;using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyExplosion : MonoBehaviour {
    bool isExploding  = false;

    public void GetExploded()
    { 
        // Don't let us get exploded more than once.
         // it might cause problems if we have 2+ Coroutines going on.
        if (isExploding) 
             return;
        StartCoroutine(WaitForDeath());
    }

    IEnumerator WaitForDeath()
    {
        isExploding = true;
        Rigidbody rb = GetComponent<Rigidbody>();
        // Wait 1/2 second to make sure the explosion has started us moving
        yield return new WaitForSeconds(0.5);

        while (rb.velocity.sqrMagnitude >= 0.1)
            yield return null;
        Destroy(gameObject);
    }
}

Then our GrenadeExplosion script looks like this. We don’t bother checking for enemy death, we pass it along to each of them:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

namespace Player
{
    public class GrenadeExplosion : MonoBehaviour
    {

        private Collider[] hitColliders;
        public float blastRadius;
        public float explosionPower;
        public LayerMask explosionLayers;
 

        void ExplosionWork(Vector3 explosionPoint)
        {
            hitColliders = Physics.OverlapSphere(explosionPoint, blastRadius, explosionLayers);
            foreach (Collider hitCol in hitColliders)
            {
                if (hitCol.GetComponent<NavMeshAgent>() != null)
                {
                    hitCol.GetComponent<NavMeshAgent>().enabled = false;

                }
                if (hitCol.GetComponent<Rigidbody>() != null)
                {
                    hitCol.GetComponent<Rigidbody>().isKinematic = false;
                    hitCol.GetComponent<Rigidbody>().AddExplosionForce(explosionPower, explosionPoint, blastRadius, 2, ForceMode.Impulse);

                    if (hitCol.CompareTag("Enemy"))
                    {
                        EnemyExplosion eeScript = hitCol.gameObject.GetComponent<EnemyExplosion>();
                        eeScript.GetExploded();
                    }
                }
            }
         
        }

        void OnCollisionEnter(Collision col)
        {
            ExplosionWork(col.contacts[0].point);
            Destroy(gameObject);
        }

    }

I added the code to the enemy prefab and the effect I got is that it get destroyed in the air.
I also have another script on the enemy prefab which is.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

namespace Enemy
{
    public class EnemyChase : MonoBehaviour
    {

        private Transform myTransform;
        private NavMeshAgent myNavMeshAgent;
        private Collider[] hitColliders;
        private float checkRate;
        private float nextCheck;
        public LayerMask detectionLayer;
        private float detectionRadius = 20;

        void SetInitialReferences()
        {
            myTransform = transform;
            myNavMeshAgent = GetComponent<NavMeshAgent>();
            checkRate = Random.Range(0.8f, 1.2f);
        }

        void CheckIfPlayerInRange()
        {
            if (Time.time > nextCheck)
            {
                nextCheck = Time.time + checkRate;
                hitColliders = Physics.OverlapSphere(myTransform.position, detectionRadius, detectionLayer);
                if (hitColliders.Length > 0)
                {
                    if(GetComponent<NavMeshAgent>().enabled == true)
                    {
                        myNavMeshAgent.SetDestination(hitColliders[0].transform.position);
                    }
                }
            }
        }

        // Use this for initialization
        void Start()
        {
            SetInitialReferences();
        }

        // Update is called once per frame
    }
}

edit: I got a bit excited…
If i set the <= value to lower digits it means the enemy doesnt get destroyed in the air
the nav mesh is now not working but I can see the desired effect there.

Please come back and help, we’re almost there :slight_smile:

I forgot about the fact taht as it explodes and flies into mid air its velocity is going to be 0 when it reaches the peak and starts to fall back. Might need a check for height, but that could get complicated depending on how your ground is set up.

So you say you got it to work with a smaller epsilon for velocity. What is the problem now exactly? Is it destroying enemies in mid air still or after they hit the ground?

Just check if the rigidbody is sleeping - Unity - Scripting API: Rigidbody.IsSleeping

1 Like

added the is sleeping to while loops and now works perfectly thanks all [solved]