Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

HingeJoint2D not working after using SetActive

Discussion in 'Physics' started by hugeandy, Dec 16, 2016.

  1. hugeandy

    hugeandy

    Joined:
    Nov 2, 2016
    Posts:
    131
    Hi all,

    I have come across a problem whilst using a HingeJoint2D to connect two RigidBody2Ds. I have the following setup, as shown below. Two GameObjects each with a SpriteRenderer, RigidBody2D and a HingeJoint2D. The red block has no connected body set so is connected to the background. The green block has its connected body set to the red block.

    Capture.PNG
    When I play the scene, it behaves as expected and the green blocks hangs from the red one. I have the following script attached to another GameObject in the scene:

    Code (CSharp):
    1.  public class hingeBugTester : MonoBehaviour {
    2.      public GameObject block1, block2;
    3.    
    4.      void Update () {
    5.          if(Input.GetMouseButtonDown(0)) {
    6.              block1.SetActive(false);
    7.          }
    8.          if(Input.GetMouseButtonUp(0)) {
    9.              block1.SetActive(true);
    10.          }
    11.      }
    12. }
    When I mouse down, the red block disappears, and when I mouse up, it reappears. However now the hinge joint holding the red and green together does not work, and the green block falls away. Then, if I disable and enable the hinge joint, it snaps back into the correct place.

    Any ideas as to what is happening here? And how I can work around this so that the hinge joint works straight away after calling SetActive(true)

    Thanks in advance
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,321
    Presumably this is because gravity is moving the green block when you've deactivated the connected body (red) its hinge joint is connected to. It won't just switch to using the world; the joint just won't work any longer.
     
  3. hugeandy

    hugeandy

    Joined:
    Nov 2, 2016
    Posts:
    131
    But should the joint not reactivate when SetActive(true) is called again?
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,321
    Yes, it should. If you have reproduction project example and can give me a link (or post a bug report and give me the case number) then I'll take a look.
     
  5. hugeandy

    hugeandy

    Joined:
    Nov 2, 2016
    Posts:
    131
    Great, will do. Thanks
     
  6. hugeandy

    hugeandy

    Joined:
    Nov 2, 2016
    Posts:
    131
    Have messaged you the link, case number 863558
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,321
    Thanks, I've just checked the case. Note that the case refers to a FixedJoint2D and not a HingeJoint2D although that doesn't really matter.

    So, there isn't a bug here; deactivating the 'block1' GO where it contains a Rigidbody2D that is being referenced by the 'block2' GO that has a FixedJoint2D correctly disables the FixedJoint2D on 'block2' however, activating the 'block1' GO will not enable the joint. The reason for this is that there's no outbound reference to the 'block2' GO joint at all; the only reason the joint is disabled is because deactivating 'block1' causes the Rigidbody2D body to be deleted and the underlying Box2D system knows that body is referenced by the joint so the joint is also removed. Reactivating the GO creates the Rigidbody2D body but there is no activate joint at that point.

    If you want to have correct behaviour then you should deactivate 'block2' instead. Alternately, instead of deactivating the 'block1' GO, simply turn-on/off simulation with Rigidbody2D.simulated. This way is not only much faster but also the joint on 'block2' stops working but when you turn-on simulation on the 'block1' GO again the joint continues to work i.e.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class hingeBugTester : MonoBehaviour {
    7.  
    8.    public GameObject block1, block2;
    9.  
    10.    private Rigidbody2D rb;
    11.  
    12.    void Start()
    13.    {
    14.      rb = block1.GetComponent<Rigidbody2D> ();
    15.    }
    16.  
    17.    void Update ()
    18.    {
    19.      if(Input.GetMouseButtonDown(0))
    20.      {
    21.        rb.simulated = false;
    22.      }
    23.      if(Input.GetMouseButtonUp(0))
    24.      {
    25.        rb.simulated = true;
    26.      }
    27.    }
    28. }
    29.  
    Note that if you want 'block2' to fall then you'll either need to wake it when you turn-off simulated on 'block1' or stop it from sleeping.
     
    Last edited: Dec 21, 2016
  8. hugeandy

    hugeandy

    Joined:
    Nov 2, 2016
    Posts:
    131
    Ah yes sorry, I had changed it to a FixedJoint2D at the last moment to see if it happened with those as well, and forgot to change it back.

    Ahh I understand, thank you for the clear explanation. Since experiencing this problem we have changed the way our game works a bit to avoid the calls to SetActive(). Would you say that using SetActive() for physics components is a bad idea in general and should be avoided? Or just used with caution and know the side effects?

    Cheers
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,321
    There's no black or white answer to that; you deactivate the GameObject if you want all the components to be deactive so that should be the only reason. If you want to turn off a joint then just disable that specific component. If you want physics objects like joints, colliders (etc) to temporarily stop working i.e. joints don't constraint, colliders don't collider, effectors don't apply forces then simply use the Rigidbody2D.simulated as I mentioned previously as this is not only super-fast but doesn't involve deleting the underlying physics objects which is what disabling/deactivating does. This means if you turn-off simulation via the Rigidbody2D then turn it back on again, it's super quick and all joint connects (and other stuff) is stil intact.