Rigidbody Hinge Joint "Grab" System

Hello,

I have a “Grab” script I’ve repurposed from an online active ragdoll tutorial.

So, I have my “Player” (blue), with a hanging ‘hand’, on the player hand is a sphere collider with a Rigidbody attached, I then have a box (pink) with the tag “Grabbable”, with a box collider and a Rigidbody.

So, I can manoeuvre the Player to the box, and I’m able to ‘grab’ it by creating a hinge joint, this is fine so far, however, I have noticed some glitches, mainly

1. Grabbing is difficult, sometimes I have to click a number of times before it actually ‘grabs’ the object, anything I can do to help with this?
2. Not all the time, but fairly often, when I ‘grab’, multiple hinge joints are created, why, how might I fix?
3. Distance between my Players hand and the box object is ‘far’, making it look like my player is using ‘the force’ to move the object, rather than the actual ‘grab’, how to close the distance between the hinge joint setup?

The Grab code is here

using UnityEngine;

public class Grab : MonoBehaviour
{
    public bool canGrab;
    private bool hold;

    void Update()
    {
        if (canGrab)
        {
            if (Input.GetMouseButton(0))
            {
                hold = true;
            }
            else
            {
                hold = false;

                Destroy(GetComponent<FixedJoint>());
            }
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (hold && collision.transform.tag != "Player" && collision.gameObject.tag == "Grabbable")
        {
            Rigidbody rb = collision.transform.GetComponent<Rigidbody>();

            if (rb != null)
            {
                FixedJoint fj = transform.gameObject.AddComponent(typeof(FixedJoint)) as FixedJoint;
                fj.connectedBody = rb;
            }
            else
            {
                FixedJoint fj = transform.gameObject.AddComponent(typeof(FixedJoint)) as FixedJoint;
            }
        }
    }
}

For #1: find out why by debugging. First question is, what is the state of canGrab when it fails to grab?

For #2: keep a reference to the joint you create (AddComponent() returns the new reference). That way if it exists, don’t remake it. This also lets you Destroy() it explicitly and set it to null, thus avoiding so much GetComponent() noise.

For #3: again, debug to find out why the distances are bigger than you expect. Are the colliders perhaps larger? Is the hand bouncing off first (use layers to inhibit)? etc.

Whenever you need more information about what your program is doing as well as how and where it is deviating from your expectations, that means it is…

Time to start debugging!

By debugging you can find out exactly what your program is doing so you can fix it.

Here is how you can begin your exciting new debugging adventures:

You must find a way to get the information you need in order to reason about what the problem is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the names of the GameObjects or Components involved?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as Debug.Log("Problem!",this);

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer for iOS: How To - Capturing Device Logs on iOS or this answer for Android: How To - Capturing Device Logs on Android

If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

If your problem is with OnCollision-type functions, print the name of what is passed in!

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

If you are looking for how to attach an actual debugger to Unity: Unity - Manual: Debugging C# code in Unity

“When in doubt, print it out!™” - Kurt Dekker (and many others)

Note: the print() function is an alias for Debug.Log() provided by the MonoBehaviour class.

Keep in mind that using GetComponent() and its kin (in Children, in Parent, plural, etc) to try and tease out Components at runtime is definitely deep into super-duper-uber-crazy-Ninja advanced stuff.

Here’s the bare minimum of stuff you absolutely MUST keep track of if you insist on using these crazy Ninja methods:

  • what you’re looking for:
    → one particular thing?
    → many things?
  • where it might be located (what GameObject?)
  • where the Get/Find command will look:
    → on one GameObject? Which one? Do you have a reference to it?
    → on every GameObject?
    → on a subset of GameObjects?
  • what criteria must be met for something to be found (enabled, named, etc.)

If you are missing knowledge about even ONE of the things above, your call is likely to FAIL.

This sort of coding is to be avoided at all costs unless you know exactly what you are doing.

Botched attempts at using Get- and Find- are responsible for more crashes than useful code, IMNSHO.

If you run into an issue with any of these calls, start with the documentation to understand why.

There is a clear set of extremely-well-defined conditions required for each of these calls to work, as well as definitions of what will and will not be returned.

In the case of collections of Components, the order will NEVER be guaranteed, even if you happen to notice it is always in a particular order on your machine.

It is ALWAYS better to go The Unity Way™ and make dedicated public fields and drag in the references you want.

1 Like

Hello, @Kurt-Dekker

Thanks for your informative post, I hope you’ve copied and pasted this a few times, and didn’t come up with all that on the fly! :stuck_out_tongue:

Point 1 : The canGrab is not relevant within this script, it is publicly set and simply lets the script know whether it’s in play or not. The private and ‘main’ bool would be the ‘hold’. I used Debug.Log to determine when it was being triggered and when it was not, this held true when I pressed the left mouse button down, otherwise it was always false.

I suspect when clicking to grab the box, on some occasions, I am possibly ‘inside’ the box’s ‘box collider’, and so multiple clicks might be registered before it finally ‘registers’ a grab, as in I might need to move my players ‘arm’ outside of the box, and then ‘collide’ ‘again’ to register a click. I probably need a better way to register the collision, I can work on this.

Point 2 : Answers to point one, somewhat explain problems arising in point 2, if I’m clicking multiple times in quick succession to try and register a ‘grab’, then multiple hinge joints could be created, your point of tracking the reference and if it exists, not remaking it, is good advice, I will try to implement that method.

Point 3 : Yes, the hand does bounce off, but with the hand and box being rigid bodies, I can’t avoid this(?), making the sphere collider on the hand smaller and ‘within’ the hand certainly helps with the distance disparity though, so I will look into this further!

Thanks for your guidance and help, appreciated!

Regards

EDIT: Code with fixes implemented
```csharp
**using UnityEngine;

public class Grab : MonoBehaviour
{
public bool canGrab;

private bool hold;
private bool hasJoint;

void Update()
{
    if (canGrab)
    {
        if (Input.GetMouseButton(0))
            hold = true;
        else
        {
            // Set boolean conditions

            hold = false;
            hasJoint = false;

            // Destroy fixed Joint

            Destroy(GetComponent<FixedJoint>());
        }
    }
}

private void OnCollisionStay(Collision collision)
{
    // If hold is TRUE, AND collision object is not Player, AND collision object has tag "Grabbable"

    if (hold && !collision.gameObject.CompareTag("Player") && collision.gameObject.CompareTag("Grabbable"))
    {
        // Check If collision object HAS a Rigidbody, and hasJoint is FALSE

        if (collision.gameObject.GetComponent<Rigidbody>() != null && !hasJoint)
        {
            // If both conditions are TRUE, add a fixed Joint

            gameObject.AddComponent<FixedJoint>();

            // Disable Preprocessing | Helps with Joint stability

            gameObject.GetComponent<FixedJoint>().enablePreprocessing = false;

            // Add the collision object (rigidBody) as a connected Body

            gameObject.GetComponent<FixedJoint>().connectedBody = collision.rigidbody;

            // Set hasJoint to TRUE

            hasJoint = true;
        }
    }
}

}**
```