A component that lets a player pick up, hold, drop and throw physics objects in the scene. It traces a ray from the camera to find a rigidbody, stores the grabbed GameObject and Rigidbody, moves it toward a hold position each update, and applies velocity to throw.
using Sandbox;
public sealed class ObjectGrabber : Component
{
[Property] public float MaxReachDistance { get; set; } = 300f;
[Property] public float HoldDistance { get; set; } = 150f;
[Property] public float ThrowForce { get; set; } = 1200f;
private GameObject _heldObject;
private Rigidbody _heldRigidbody;
protected override void OnUpdate()
{
if ( IsProxy ) return;
if ( Input.Pressed( "use" ) || Input.Pressed( "e" ) )
{
if ( _heldObject.IsValid() )
{
DropObject();
}
else
{
TryGrabObject();
}
}
if ( Input.Pressed( "attack1" ) && _heldObject.IsValid() )
{
ThrowObject();
}
if ( _heldObject.IsValid() )
{
UpdateHeldObjectPosition();
}
}
private void TryGrabObject()
{
var cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
if ( !cam.IsValid() ) return;
var ray = cam.Transform.World.Forward;
var start = cam.WorldPosition;
var end = start + ray * MaxReachDistance;
var tr = Scene.Trace.Ray( start, end )
.IgnoreGameObject( GameObject )
.Run();
if ( tr.Hit && tr.GameObject.IsValid() )
{
var rootObj = tr.GameObject.Root;
var rb = rootObj.Components.Get<Rigidbody>() ?? tr.GameObject.Components.Get<Rigidbody>();
if ( rb.IsValid() && !rootObj.Tags.Has( "player" ) )
{
_heldObject = rootObj;
_heldRigidbody = rb;
_heldRigidbody.MotionEnabled = true;
}
}
}
private void UpdateHeldObjectPosition()
{
var cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
if ( !cam.IsValid() ) return;
var targetPos = cam.WorldPosition + cam.Transform.World.Forward * HoldDistance;
var dir = targetPos - _heldObject.WorldPosition;
if ( _heldRigidbody.IsValid() )
{
_heldRigidbody.Velocity = dir * 10f;
}
else
{
_heldObject.WorldPosition = Vector3.Lerp( _heldObject.WorldPosition, targetPos, Time.Delta * 15f );
}
}
private void DropObject()
{
_heldObject = null;
_heldRigidbody = null;
}
private void ThrowObject()
{
if ( !_heldObject.IsValid() ) return;
var cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
var throwDir = cam.IsValid() ? cam.Transform.World.Forward : Transform.World.Forward;
if ( _heldRigidbody.IsValid() )
{
_heldRigidbody.Velocity = throwDir * ThrowForce;
}
CameraJuice.Instance?.Shake( 8f );
_heldObject = null;
_heldRigidbody = null;
}
}