Search Unity

2 OnCollisionEnter2D() with Destroy(gameObject);

Discussion in 'Scripting' started by Pedro_R, Jun 6, 2018.

  1. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    50
    Hello,

    How can I make it so when 2 colliders collide, they both trigger their OnCollisionEnter2D() entirely despite one being destroyed by Destroy(gameObject)? I made my scripts and it was working yesterday but today it simply won't work I don't have a clue why lol it appears that only one OnCollisionEnter2D() is running to the end... I have a variable isStomping that is checked in each OnCollisionEnter2D() and do different things in each one, but in one of them it's set to false at the end... And then it bugs the other OnCollisionEnter2D().

    Player Script:
    Code (CSharp):
    1.  
    2. public class Player : MonoBehaviour
    3. {
    4.  
    5.     // Some not related code.
    6.     Rigidbody2D playerRB = gameObject.getComponent<Rigidbody2D>();
    7.     // Some not related code.
    8.     private void OnCollisionEnter2D(Collision2D collision)
    9.     {    
    10.             // Some not related code.
    11.             if (isStomping == true)
    12.             {
    13.                 // Some code to add forces to playerRB.
    14.                 isStomping = false;
    15.                 return;
    16.             }
    17.             // Some not related code.
    18.     }
    19.     // Some not related code.
    20. }
    Breakable Block Script:
    Code (CSharp):
    1.  
    2. public class BreakableBlock : MonoBehaviour
    3. {
    4.     // Some not related code.
    5.     private void OnCollisionEnter2D(Collision2D collision)
    6.     {
    7.         // Some not related code.
    8.         if (collision.gameObject.GetComponent<Player>().isStomping == true)
    9.             Destroy(gameObject);
    10.     }
    11. }
    The problem is that sometimes the breakable block is not destroyed as it detects isStomp as false because it was set to false at Player's OnCollisionEnter2D(); despite it was true when the player and the block collided. Why does one OnCollisionEnter2D(); triggers before the other?

    Thanks!
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    Destroy doesn't do anything until the end of the frame so that's a red herring here.

    The scripts aren't running synchronously, so one of them will go first. If that happens to be the player script, he's stomped and the variable is false. If that happens to be the stompable thing the variable will still be true since the player script hasn't processed it's collision code yet.

    Solution: don't rely on two collider scripts executing.

    For example, have the stompable thing expose a function "BeStomped()" or whatever, this performs the destruction. Then in the player collision code, check if the player is in stomp mode, check if it collided with a stompable thing, call "BeStomped" on the "other" object and update the player state as needed.


    You'll note the "-able" word.... look into Interfaces :)
     
  3. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    50
    The thing is that the player can stomp at anything, any ground, any block. However, it will have the same effect on itself despite of what it's stomping, but the only thing that will "suffer" from the stomping is this particular block. Isn't there any way to avoid writing code in the player script just to this particular block, and instead just write the script of this block separately?

    Thanks!
     
  4. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    ... you're getting more into "how to design an interaction system" rather than the original question about why the collision code isn't working...

    Yes, you can design things in such a way where any interaction between any two objects is handled by the same framework which takes into account the type of action and the relative properties of the two objects and effects whatever outcomes. But even that would follow the basic pattern of "one gameobject drives the initiation of the occurrence, the occurrence is handled, the appropriate functions are called on the various gameobjects/component to effect the outcome.

    At some point the code is going to have to check "is the thing I'm stomping something I can stomp"... the fact this one action only affects one type of interactable object is more game design choice than code implementation.

    If it were my design I would still want the object being stomped to react in someway even if it cannot be destroyed. Just a little shake or something to give feedback to the player that the stomp happened, but didn't work on this thing.
     
  5. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    50
    I hope my solution helps someone:

    Player Script:
    Code (CSharp):
    1. public class Player : MonoBehaviour
    2. {
    3.     // Some code.
    4.     private void OnCollisionEnter2D(Collision2D collision)
    5.     {
    6.         if (collision.gameObject.GetComponent<ICollidable>() != null)
    7.         {
    8.             ICollidable collider = collision.gameObject.GetComponent<ICollidable>();
    9.             // Some code.
    10.             if (collider is IInteractable)
    11.             {
    12.                 IInteractable interaction = collision.gameObject.GetComponent<IInteractable>();
    13.                 // Some code.
    14.                 if (isStomping == true)
    15.                     // Some code unrelated to destroying the breakable block.
    16.                 else
    17.                     // Some code.
    18.                 // Some code.
    19.                 collider.GetHit(this);
    20.             }
    21.             else
    22.                 collider.GetHit(this);
    23.         }
    24.     }
    25.     // Some code.
    26. }
    Breakable Block Script:
    Code (CSharp):
    1. public class BreakableBlock : MonoBehaviour, ICollidable, IInteractable
    2. {
    3.     // Some code.
    4.     public void GetHit(Player player)
    5.     {
    6.         if (player.isStomping)
    7.         {
    8.             // Some code.
    9.             player.isStomping = false;
    10.             Destroy(gameObject);
    11.         }
    12.         else
    13.             // Some code.
    14.     }
    15. }
    ICollidable Interface:
    Code (CSharp):
    1. public interface ICollidable
    2. {
    3.     void GetHit(Player player);
    4. }
    5.  
    IInteractable Interface:
    Code (CSharp):
    1. public interface IInteractable
    2. {
    3.     // Some properties.
    4. }
    This way I was able to define what should just be collidable (Does something when gets hit) and what should be both collidable and interactable (Player does something when hits it). I don't know if my explanation went well but it's because there's so much code involved... Anyway, if someone has any tips or optimizations, please let me know!

    Thanks for the help!