Basic Grab

Prerequisites:

 

Setup for XR Rig

For each XRController3D node (VR hands), add an Area3D node as a child. You should get a warning about the Area node needing a collision shape, so add a new CollisionShape3D node as a child of the Area3D node. For my Shape type, I chose a new SphereShape3D and gave it a radius of 0.1m. Do this for all XRController3D nodes if you are using pickup interactions.

Screenshot 2025-04-28 102007.png

Screenshot 2025-04-28 102025.png

 

Setup basic grabbing

In your "world" scene, add a RigidBody3D node to your scene and add a CollisionShape3D and MeshInstance3D node as children, as well as any setup needed for those nodes. In the RigidBody3D node, create a new script called Grippable.cs and ensure the script is attached. Now, let's add some code:

using Godot;
using System;

public partial class SimpleGrab : RigidBody3D, IGrabbable
{
	Node3D parentNode;

    public override void _Ready()
    {
        parentNode = (Node3D)this.GetParent();
    }

    public void PickUp(Node3D receivedController)
	{
		Freeze = true;

		Reparent(receivedController, true);
	}

	public void Drop(Vector3 receivedVelocity)
	{
		Freeze = false;

		Reparent(parentNode);

		//CallDeferred("set_axis_velocity", receivedVelocity);
		SetAxisVelocity(receivedVelocity);
	}
}

Let's review what's happening here:

 

Add functionality to our hand (each)

Assuming you have completed all necessary prerequisites, you should have a LeftHand.cs and a RightHand.cs with some possible functionality. Here is additional code to add to each hand:

    Vector3 velocity;
    Vector3 previousPosition;

    Area3D area;
    Grippable grippable;

    public override void _Ready()
    {
        area = GetNode<Area3D>("Area3D");
    }


    public override void _Process(double delta)
    {
        velocity = (Position - previousPosition) / (float)delta;
        previousPosition = Position;
    }

    public override void OnGripPressed()
    {
        var bodies = area.GetOverlappingBodies();
        foreach (var body in bodies)
        {
            if (body is Grippable _)
            {
                grippable = body as Grippable;

                grippable.PickUp(this);
                
                return;
            }
        }
    }

    public override void OnGripReleased()
    {
        if (grippable != null)
        {
            grippable.Drop(velocity);
        }
        grippable = null;
    }

Let's review what this code achieves:

 

Testing

If you have reached this stage, go ahead and test functionality with both hands.

 

Where to go from here?

If you are looking to add advanced interactions like sliders and dials, I may recommend keeping these methods for grabbing types, but add new classes that track the controller's movement instead of parenting when picked up or dropped.


Revision #3
Created 28 April 2025 14:16:02 by Wes
Updated 5 May 2025 14:33:37 by Wes