- Home /
Applying explosion force to destructible object AFTER it's been destroyed
Hi there,
I have an FPS game with a rocket launcher, and a crate which is destructible.
Basically, when the rocket collides with anything, it spawns an explosion object, which has this script to apply explosive force to any object in range:
public class Explosion : MonoBehaviour
{
public float explosionRadius = 10f;
public float explosionUpwardsForce = 1f;
public int explosionDamage = 100;
public float explosionForce = 70f;
public float explosionDamageFalloff = 1f;
private IEnumerator Start()
{
// wait one frame before starting, to ensure all objects are affected
yield return null;
var rigidbodies = new List<Rigidbody>();
Collider[] objectsInRange = Physics.OverlapSphere(transform.position, explosionRadius);
// for every object within the explosion radius with a rididbody component, add them to the rigidbody list
foreach (Collider collision in objectsInRange)
{
if (collision.attachedRigidbody != null && !rigidbodies.Contains(collision.attachedRigidbody))
{
rigidbodies.Add(collision.attachedRigidbody);
}
// if any object has a target script attached, run the take damage function, with a fall-off effect, i.e. the further away the target is from the explosion origin, the less damage they take
Target target = collision.GetComponent<Target>();
if (target != null)
{
float proximity = (transform.position - target.transform.position).magnitude;
float effect = explosionDamageFalloff - (proximity / explosionRadius);
// if the target is very close to the explosion origin, take full damage
if (proximity <= 0.7f)
{
target.takeDamage(explosionDamage);
}
// else, take splash damage
else
{
target.takeDamage(explosionDamage * effect);
}
}
}
// wait one frame before starting, to ensure all objects are affected
yield return null;
// add explosion force to each rigidbody
foreach (var rb in rigidbodies)
{
rb.AddExplosionForce(explosionForce, transform.position, explosionRadius, explosionUpwardsForce, ForceMode.Impulse);
}
}
}
And when the crate is destroyed, it spawns a destroyed version which is made up of lots of small pieces of the crate, all of which have rigidbodies. This is the script for the crate:
public class Destructible : MonoBehaviour
{
public GameObject destroyedVersion; // Reference to the shattered version of the object
void OnDestroy ()
{
// When the object is destroyed, spawn a shattered object
Instantiate(destroyedVersion, transform.position, transform.rotation);
}
}
So at the moment the explosion does not affect the crate after it's been destroyed, the pieces just fall to the ground. I'm not sure how to make it so that the explosion affects them too?
I've tried adding a small delay to the explosion script before applying force but that doesn't work because the pieces are not in the collider array.
Any help appricated! Thanks :)
Uhm where do you actually apply the explosion damage and when / where do you destroy the objects. You seem to have an ordering problem of two concepts and you only showed one of them.
Shouldn't this code first apply the damage to the rigidbodies in range which makes them either destroy or just loose some hp and afterwards you would apply the explosion force to all objects that are now in range.
I excluded the damage part to keep things simple, as I didn't think it was relevant, but i'll add it to the original post :)
And here is the target script:
public class Target : $$anonymous$$onoBehaviour { public float targetHealth = 1000f;
public void takeDamage(float amount)
{
targetHealth -= amount;
// display name and remaining health of target
Debug.Log(gameObject + " has: " + targetHealth + " health remaining");
if (targetHealth <= 0f)
{
Die();
}
}
void Die()
{
Destroy(gameObject);
}
}
Answer by Bunny83 · Sep 21, 2017 at 01:59 PM
Well, what you essentially need is to perform another Physics.OverlapSphere after you applied your damage. Since after applying the damage there might be new rigidbodies.
IEnumerator Start()
{
yield return null;
Collider[] objectsInRange = Physics.OverlapSphere(transform.position, explosionRadius);
foreach(var collider in objectsInRange)
{
Target target = collider.GetComponent<Target>();
if (target != null)
{
// your damage code
}
}
yield return null;
var rigidbodies = new HashSet<Rigidbody>();
objectsInRange = Physics.OverlapSphere(transform.position, explosionRadius);
foreach (Collider collider in objectsInRange)
{
var rb = collider.attachedRigidbody;
if (rb != null && rigidbodies.Add(rb))
{
rb.AddExplosionForce(explosionForce, transform.position, explosionRadius, explosionUpwardsForce, ForceMode.Impulse);
}
}
}
HashSet.Add will return "true" if the object is not yet in the set and returns false if it is already in the set.
Answer by Twistorian · Sep 21, 2017 at 12:46 PM
In your script void OnDestroy, it instantiates the destroyedVersion.
Use that script to give the destroyedVersion the same force (or same velocity), perhaps with some multiplier, that was given to the original object.
public class Destructible : MonoBehaviour
{
public GameObject destroyedVersion; // Reference to the shattered version of the object
void OnDestroy ()
{
// When the object is destroyed, spawn a shattered object
GameObject pieces = Instantiate(destroyedVersion, transform.position, transform.rotation);
float someMultiplier = 3f; // the small pieces maybe move faster than the whole, right?
pieces.GetComponent<Rigidbody>().velocity = someMultiplier*GetComponent<Rigidbody>().velocity;
}
}
This assumes that both the original and the destroyedVersion are of the type Rigidbody.
Tweak this and you should get a working model up.
I gave this a try but it didn't work for me, thanks though! Bunny83's answer worked.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Fixed force in direction of touch. 1 Answer
AddExplosionForce help! 1 Answer