1
\$\begingroup\$

I made a scenario that shooting a bullets on crates make them both bounce nicely (both the bullet and crates are RigidBody2D & Colliders). When I use OnCollisionEnter2D to destroy the bullet when it hits the crate then the bullet destroyed before it pass the force to the crate and thus the crate stay still and not move physically...

How to make the crate move because of the bullet hit and also make the bullet to destroyed? There is more elegant way from just destroy the bullet a frame after the hit?

\$\endgroup\$
  • \$\begingroup\$ I don't know Unity/C#, but could you somehow mark the bullet for deletion (so you end up deleting it later) that way it still has time to perform physics calculations on the crate? \$\endgroup\$ – CodeNMore Mar 6 '16 at 17:59
1
\$\begingroup\$

An easy workaround is to disable SpriteRenderer component In OnCollisionEnter2D and you can then destroy bullet let say after half or a second. Something like,

void OnCollisionEnter2D(Collision2D other)
{
    // Assuming that your crate has tag of "crate"
    if (other.gameObject.tag == "crate")
    {
        GetComponent<SpriteRenderer>().enabled = false;
        Destroy(gameObject, 0.5f);
    }
}
\$\endgroup\$
  • \$\begingroup\$ I object to this answer. Any code that depends on an arbitrary time makes me feel uneasy. \$\endgroup\$ – Evorlor Mar 6 '16 at 22:08
  • \$\begingroup\$ Me too, but it is not anything like spawning or something else like this, in other words your game-logic is not dependent on time. If bullet destroys more or less 0.5f seconds then that'd be alright anyways. \$\endgroup\$ – Hamza Hasan Mar 7 '16 at 4:15
  • \$\begingroup\$ There are always side effects. For examples, FindObjectsOfType<Bullet>().Length is inaccurate. It will run into things half second after it is supposedly despawned, and make the player WTF. If you are Mario and can only have two fire powers at once, there is a half second delay before you can shoot the third. Etc. The smallest misstep is always greater than what is apparent on top. Even my answer above can lead to bugs similar this. \$\endgroup\$ – Evorlor Mar 7 '16 at 13:36
1
\$\begingroup\$

Here are two similar strategies with tiny differences. I suggest the second approach.

The physics will happen the next FixedUpdate. So wait until that FixedUpdate is over. We can do this by waiting for FixedUpdate twice. The first pass will take us to when the physics start taking place. The second pass will take us to the FixedUpdate after the physics have completed.

void OnCollisionEnter2D(Collision2D other)
{
    StartCoroutine(DestroyAfterPhysics());
}

private IEnumerator DestroyAfterPhysics()
{
    yield return new WaitForFixedUpdate();
    yield return new WaitForFixedUpdate();
    Destroy(gameObject);
}

That code makes the assumption that all current physics will be dealt with during the immediate FixedUpdate. We can avoid this assumption and clean the code a bit by waiting for the maximum delta time. This version of the code will assure success, but possibly at the loss of an insignificant amount of time:

void OnCollisionEnter2D(Collision2D other)
{
    Destroy(gameObject, Time.maximumDeltaTime));
}

This is not a flawless approach, and I would be interested to see other ideas. For example, if you check to see if the bullet exists the frame after the collision, it could return a false positive.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.