HL2 / Amnesia-Style Physics Object Manipulation

Godot Version

4.6.1

Question

TLDR: Specific questions below. The text here provides background and details of my experiments.

I’m interested in implementing a physics-based object dragging mechanic in the style of HL2 and Amnesia. I’ve tried several approaches, including using:

rigidBody.ApplyForce((targetPosition - rigidBody.GlobalPosition) * someMagicNumber)

custom PID controllers, and SpringArm3D.
All of these methods have significant drawbacks that can only be ignored if the required result doesn’t need to be very precise. The main problem is that the held object either follows the pointer too slowly or interacts unrealistically with other physics bodies, colliding heavily with them or even passing through walls.

A more “proper” approach seems to be using constraints/joints. They ensure correct physical behavior and require minimal code. However, the same dilemma remains: either the object follows the pointer too slowly, or it interacts unrealistically with other bodies. For example, with a stiff LinearSpring/LinearLimitSpring on a Generic6DOFJoint (Godot Jolt Extension), movement becomes very responsive, but lightweight objects strongly push heavier ones. Making the spring softer makes interactions more predictable and “realistic”, but the responsiveness is reduced.

I’ve tried splitting movement into two states: free, without contacts and with a stiff spring, and a soft spring during collisions. This didn’t achieve the desired result. Adjusting max_force on all axes (linear_spring_x/max_force) caused the held object to sag along the Y axis, even though it was only supposed to be constrained in the XZ plane, and apply weaker forces to other objects. Attempts to change max_force locally, for example depending on the collision normal, also didn’t solve the issue. Additionally, object mass must be considered. Simply juggling numbers in the hope of finding the right configuration doesn’t work. The current situation is: a light object can push a heavy one too strongly, while simply limiting the held object’s velocity makes movement feel “dead”.

Specific questions:

  1. Does the position of the joint and static_body nodes (with static_body used as node_a in the joint) in the scene tree matter?
  2. Is it possible to solve this issue without writing a custom Constrain Solver, using only the capabilities of Generic6DOFJoint?
  3. Are there any publicly available examples of how similar mechanics are implemented in other games?

Any advice or guidance is appreciated — I’m mainly looking for directions to explore further. It feels like I’m missing a small detail that could make everything fall into place.

I think the important thing you’re missing is that the rigid physics are a very simle and limited part of the real physics. What exactly the game should do, when the object you are carrying is caught on a wall, or another heavy object? In a real world you would have been caught there along with the object unable to move thru. If you pushed strong enough still the object or the wall (or your arms) would eventually break before teleporting through. Unless in-game physics are this complete they won’t be able to carry the interaction you want on their own.

Rigid physics are useful to bounce bodies under reasonable forces, but you have to detect and fake or prevent the extreme cases. Perhaps allocate an area around the holding point in front of the player, where the body would have the complete collision, but disable it once the body goes outside for whatever reason to bring it back in. Or have the object fall out of the players hands. Also alter the player collision shape so it can’t walk up to a wall and place the object on the other side. Basically, a set of hacks to give the appearance of the whatever behavior you have in mind.

1 Like

Hello, thank you for your reply. Indeed, I have tried using hacks for extreme cases, mostly raycasts. Dropping the object from the player’s hands sounds interesting, but it requires determining when the object is actually “stuck.” For that to happen, when there is a collision between the held object and another body, the force that pulls the object toward the holding point would need to be limited — I haven’t managed to implement that properly.

The idea of using an Area in front of the player sounds interesting. I will try to explore the direction of adding controlled hacks. Thanks again for your response.

Hi I would say have a look at the openXR examples or VR stuff i dont see much difference except you run OpenXR and ad a xr camera. it should tell you how to add grips and stuff to like a mugs just you run it as 3d game and not VR as its the same principles when you pick up something see if that can point you in the right direction.

1 Like

Hi, thank you for the suggestion.

In my case, the goal is not just a basic grab interaction, but physically consistent object manipulation with proper constraint behavior. As far as I can tell, most XR/VR examples focus on attachment logic rather than on solving this type of physics-driven constraint problem.

I appreciate the direction nonetheless.

After a deep dive into the Source SDK codebase, I most likely found a solution that produces the desired behavior.

In Half-Life 2, object holding is implemented using a controller that is essentially a PD-like control system (positional error + damping). The gravity gun code can be found here.

Although the implementation is tightly coupled to their physics engine, the core principles can be reproduced in Godot. The key point is that all logic must run inside _IntegrateForces of the held RigidBody.

In my understanding, important part was detecting contact with a massive body. A body is considered heavy if it is static or a RigidBody3D with a mass above a defined threshold.

for (int i = 0; i < state.GetContactCount(); i++)
{
    var colliderRaw = state.GetContactColliderObject(i);
    if (colliderRaw is StaticBody3D || colliderRaw is CharacterBody3D)
    {
        touchingHeavyObjects = true;
        break;
    }
    else if (colliderRaw is RigidBody3D other && (other.Mass > _heavyMassThreshold || other.Freeze))
    {
        touchingHeavyObjects = true;
        break;
    }
}

Depending on whether the object is touching a heavy body, the _contactAmount coefficient is smoothly adjusted:

_contactAmount = Mathf.MoveToward(
    _contactAmount,
    touchingHeavyObjects ? 0.1f : 1.0f,
    delta * 2f
);

This magic coefficient is then used to limit the maximum angular velocity:

float currentMaxAngular =
    _maxAngularSpeed *
    (_contactAmount * _contactAmount * _contactAmount);

As a result, when the held object is in contact with a heavy object, its angular motion is significantly reduced. This completely solves the problem where a light object behaves like a locomotive.

I would also like to clarify that this is not a direct reproduction of Valve’s implementation, but rather an adaptation based on my understanding of what their code does. The main points of interest in the source code are CGrabController, specifically the methods ComputeError, UpdateObject, ComputeMaxSpeed, and Simulate.

I provided examples in C#, but the same principle can certainly be implemented in GDScript as well.

Thanks again to everyone for the suggestions. I hope this information helps someone implement their own cool mechanic.

1 Like