Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Rigidbody carrying a rigidbody

Discussion in 'Physics' started by ayenawine, Nov 19, 2023.

  1. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    Does anyone know how to go about having a player pick up carry a rigid body around (eg picking up and carrying a box) like shown in the link below:

    https://x.com/maxence_perrin/status/1015708026320322560?s=46

    I want the box to collide with the environment along with the player as one, and stay fixed in the players hands. I created a generic pickup function, but I can’t get the physics to work correctly. There doesn’t seem to be any good full explanation online of how to do this.

    I have messed around in Unity with a few different approaches but none of them seem to work for different reasons:

    1. Setting the box to kinematic keeps it in the desired position but will phase through the environment, so this is not a suitable option
    2. Setting the picked up box as child of player and Using addForce or movePosition allows the box to remain a rigid body and interact with the environment, but springs out of place when bumping into things, and snags on objects.
    3. Using a spring or fixed joint to the desired position gets the same results as #2
    4. What I haven't tried yet is adding a second collider to the player during runtime when the box is picked up, superimposed over the box.
    The only problem is that I don't know if 4 will work and even if it does it seems inefficient and slow, especially if I have a multiplayer component in the final game.

    Has anyone dealt with this issue before or figured out a fix?
     
  2. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    757
    Remove the rigidbody from the box and make it a child of the player. The box should still have a collider on it.
     
    MelvMay likes this.
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,105
    As above, colliders will be created against the Rigidbody on the same GameObject or will search the parent hierarchy to find one. If you do as above, by removing the Rigidbody component, the Collider(s) will be recreated against the parent Rigidbody so it'll act like any other normal Collider you'd add it to it.

    When making games, you need to stop thinking how the end-user will see something work i.e. one thing "connected" to another thing and consider how the engine works and use that. In this case, the thing you're "connecting" is just part of the main thing moving.
     
  4. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    thank you for the tip, what commands would you use to get rid of the rigid body and then re-add it once it’s been dropped?
     
  5. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,273
    In most cases it's generally enough to mark it as kinematic for the duration it's attached. There is no "enable" on the Rigidbody component. Destroying the Rigidbody means losing its original mass and centerOfMass and other data which you'd rather keep. "Phasing" or clipping through the environment shouldn't happen; check to make sure you're setting kinematic true BEFORE parenting, and restoring kinematic false AFTER separating.

    Joints between two active Rigidbodies are susceptible to external forces, that's kind of the point.
     
  6. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    This is generally used for picking up objects but not in my case as I want it to collide with objects in the environment as stated in #1 on the original post
     
  7. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    Here is how I coded my pickup and drop functions based on your recommendation. My box is not colliding with other objects in the scene when picked up despite having a box collider on it. I only void the collisions between the player and the picked up box.

    Code (CSharp):
    1.     void PickupObject() {
    2.         Debug.Log("Picking up object");
    3.         currentObjectRB.isKinematic = true;
    4.         Physics.IgnoreCollision(currentObjectRB.GetComponent<Collider>(),boxCollider,true); // turn collisions with player off
    5.         currentObjectRB.transform.SetParent(pickupTarget);
    6.         currentObjectRB.transform.position = pickupTarget.position; // sets the position of the object to your hand position
    7.     }
    8.  
    9.   void DropObject() {
    10.         Debug.Log("Dropping object");        
    11.         currentObjectRB.transform.parent = null;
    12.         Physics.IgnoreCollision(currentObjectRB.GetComponent<Collider>(),boxCollider,false); // turn collisions with player back on
    13.         currentObjectRB.isKinematic = false;
    14.         currentObjectRB = null;
    15.     }
    note all of this is happening in the player script. Do I need to be doing something in a script attached to the picked up box itself?
     

    Attached Files:

    Last edited: Nov 20, 2023
  8. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    I made a copy of the box in the scene, except I removed the rigid boxy entirely. I modified my player script so it can pick up collider objects and rigidbody objects. Both are phasing through objects in the scene when picked up, so there must be some other issue happening.
     
  9. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    757
    Is your player a character controller?. You'll probably need to switch it to a rididbody. And you'll have a much easier time in the long run as there's more possibilities with a rigidbody.
     
  10. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    Well, after some debugging I discovered part of my issue. I was setting the picked up box to the child of a child that I was using to locate the pickup/hold location. I have now fixed that and setting the picked up object to child directly of the player. Now the non-rigid body box will collide with the scene while picked up as desired, but the box that has a rigidbody still does not...
     
  11. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    It does have a rigidbody but I am also currently controlling movement in the script like this:

    Code (CSharp):
    1.         // initialize a movement vector
    2.         Vector3 inputVector = new Vector3(0,0,0);
    3.  
    4.         if (Input.GetKey(KeyCode.W)) {
    5.             inputVector.z += 1;
    6.         }
    7.         if (Input.GetKey(KeyCode.S)) {
    8.             inputVector.z += -1;
    9.         }
    10.         if (Input.GetKey(KeyCode.A)) {
    11.             inputVector.x += -1;
    12.         }
    13.         if (Input.GetKey(KeyCode.D)) {
    14.             inputVector.x += 1;
    15.         }
    16.         // get the normalized movement direction
    17.         inputVector = (Vector3)inputVector.normalized;
    18.  
    19.         // add jumpforce
    20.         if (isGrounded() && Input.GetKeyDown(KeyCode.Space)) {
    21.             rigidBody.AddForce(jumpDir * jumpForce, ForceMode.Impulse);
    22.         }
    23.  
    24.         // set walking state
    25.         if ((float)inputVector.magnitude > 0f) {
    26.             animator.SetBool("isWalking",true);
    27.             //isWalking = true;
    28.         }
    29.         else {
    30.             animator.SetBool("isWalking",false);
    31.         }
    32.  
    33.         // add movement to position, cast to 3d vector
    34.         transform.position += inputVector * Time.deltaTime * moveSpeed;
    35.  
    36.         // perform rotation based on movement direction
    37.         float rotateSpeed = 10f;
    38.         transform.forward = Vector3.Slerp(transform.forward, inputVector, Time.deltaTime * rotateSpeed);
     
  12. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    757
    You shouldn't change the transform of a rigidbody.

    If you really need to set the position then you can do something like this:
    Code (CSharp):
    1.         // add movement to position, cast to 3d vector
    2.         rb.MovePosition(rb.position + inputVector * Time.deltaTime * moveSpeed);
    3.         // perform rotation based on movement direction
    4.         float rotateSpeed = 10f;
    5.         rb.MoveRotation(Quaternion.LookRotation(Vector3.Slerp(transform.forward, inputVector, Time.deltaTime * rotateSpeed)));
    Ideally you should use AddForce for moving your character around.
     
  13. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    759
    How about just making a duplicate representation of the box without a rigidbody when the player picks it up? Disable the original box with the rb. Then swap them again when the player drops the box.
     
  14. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    Thank you! This made my movement much smoother. Any idea why the rotateSpeed has to be much larger value in this case? Is that just a result of using quaternions? I have to set the value to ~75 for the rotation to look normal vs 10 in my previous method.
     
  15. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    I have implemented your suggestion (thank you!) using the following code, and I feel like I am very close:

    Code (CSharp):
    1.     void PickupObject() {
    2.         Debug.Log("Picking up object");
    3.         GameObject currentObject = currentObjectRB.transform.Find("WoodenBox_noRB").gameObject;
    4.         currentObjectBC = (BoxCollider)currentObject.GetComponent<Collider>();
    5.         Physics.IgnoreCollision(currentObjectBC,boxCollider,true); // turn collisions with player off
    6.         currentObjectBC.transform.SetParent(this.transform);
    7.         currentObjectBC.transform.position = pickupTarget.position; // sets the position of the object to your hand position
    8.         currentObjectRB.gameObject.SetActive(false);
    9.     }
    10.  
    11.     void DropObject() {
    12.         Debug.Log("Dropping object");
    13.         currentObjectRB.gameObject.SetActive(true);
    14.         currentObjectRB.transform.position = currentObjectBC.transform.position;
    15.         currentObjectBC.transform.SetParent(currentObjectRB.transform);
    16.         Physics.IgnoreCollision(currentObjectBC,boxCollider,false); // turn collisions with player back on
    17.         currentObjectRB = null;
    18.         currentObjectBC = null;
    19.     }

    However after dropping the box it seems the position of the collider/rigidbody don't match exactly once its reactivated even though I am setting the transform position. It also causes the movement to spaz out a bit after dropping it and picking it back up:



    Any idea how to fix this? It feels like its really close
     
    Last edited: Nov 21, 2023
  16. ayenawine

    ayenawine

    Joined:
    Nov 6, 2023
    Posts:
    11
    I did some more debugging and I think I solved it!

    I was not setting the rotation in addition to the position when returning the rigidbody object to active. Also, I added a script to the rigidbody box to ignore collision with the nested "hidden" box at the start of the scene. I'm not sure if this is necessary but thought it might be causing some issue when dropping the box.

    I am now getting the desired behavior! See completed code below:

    Code (CSharp):
    1.     void PickupObject() {
    2.         Debug.Log("Picking up object");
    3.         GameObject currentGameObject = currentObjectRB.transform.Find("WoodenBox_noRB").gameObject;
    4.  
    5.         currentObjectBC = (BoxCollider)currentGameObject.GetComponent<Collider>(); // get the collider we want to pickup
    6.         currentObjectBC.gameObject.SetActive(true); // activate the collider we want to pickup
    7.         currentObjectBC.transform.SetParent(this.transform); // pickedup object now belongs to player
    8.  
    9.         currentObjectBC.transform.position = pickupTarget.position; // sets the position of the object to your hand position
    10.  
    11.         currentObjectRB.gameObject.SetActive(false); // deactivate the rigidbody object
    12.     }
    13.  
    14.     void DropObject() {
    15.         Debug.Log("Dropping object");
    16.         currentObjectRB.gameObject.SetActive(true); // reactivate the rigidbody
    17.         currentObjectRB.transform.position = currentObjectBC.transform.position; // move the RB object to the BC current location
    18.         currentObjectRB.transform.rotation = currentObjectBC.transform.rotation;
    19.  
    20.         currentObjectBC.transform.SetParent(currentObjectRB.transform); // return the BC to the RB object
    21.         currentObjectBC.gameObject.SetActive(false); // deactivate the now hidden collider object
    22.  
    23.         currentObjectRB = null;
    24.         currentObjectBC = null;
    25.     }
     
  17. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    757
    If you feel having a duplicate object is a little messy then you could destroy the rigidbody component before picking it up and restore the rigidbody when placing the box back down.

    To remove a rigidbody:
    Code (CSharp):
    1. Destroy(box.GetComponent<Rigidbody>());