private void Awake()
 {
     cachedTrans      = transform;
     cachedCam        = GetComponent <Camera>();
     currentlyHolding = null;
     prevHeldVelocity = 0f;
     curDragDistance  = defaultDragDistance;
 }
    private void Update()
    {
        // Control the hand rig positioning and try to avoid clipping using the raycast.
        UpdateHandVisual();

        // Controlling hand distance by scrolling.
        float scrollWheel = Input.GetAxis("Mouse ScrollWheel");

        if (scrollWheel < -Mathf.Epsilon)
        {
            curDragDistance -= dragDistanceStep;
            curDragDistance  = Mathf.Max(minDragDistance, curDragDistance);
        }
        else if (scrollWheel > Mathf.Epsilon)
        {
            curDragDistance += dragDistanceStep;
            curDragDistance  = Mathf.Min(curDragDistance, maxDragDistance);
        }

        // Handle dragging and releasing/tossing an object.
        if (Input.GetMouseButtonDown(0))
        {
            Ray        rayToCursor = cachedCam.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;

            // Buffer distance to allow grabbing objects further from hand.
            float rayDistance = curDragDistance * 2f;

            if (Physics.Raycast(rayToCursor, out hit, rayDistance))
            {
                if (currentlyHolding != null)
                {
                    // Ensure previously held object is released before holding new object.
                    ReleaseHeldObject();
                }

                TossableObject tossable = hit.collider.GetComponent <TossableObject>();

                if (tossable != null)
                {
                    // Grab the object so that it's centered around where the cursor was on the object.
                    grabObjOffset = hit.collider.bounds.center - hit.point;
                    HoldObject(tossable);
                }
            }
        }
        else
        {
            if (Input.GetMouseButtonUp(0) && currentlyHolding != null)
            {
                ReleaseHeldObject();
            }
        }
    }
    private void HoldObject(TossableObject obj)
    {
        if (obj == null)
        {
            ReleaseHeldObject();
            return;
        }

        currentlyHolding = obj;
        prevHeldVelocity = obj.cachedRigid.velocity.magnitude;

        // Updating hand distance to where the visual hand is.
        curDragDistance = visualHandDist;
        curDragDistance = Mathf.Clamp(curDragDistance, minDragDistance, maxDragDistance);

        // Send grab event.
        currentlyHolding.OnGrabbed();

        // "Freeze" rotation while held. Restore angular drag upon releasing.
        rigidOrigAngDrag            = obj.cachedRigid.angularDrag;
        obj.cachedRigid.angularDrag = 10f;
    }
    private void ReleaseHeldObject()
    {
        if (currentlyHolding == null)
        {
            return;
        }

        // No need to add force since the velocity is already set during drag in FixedUpdate.
        // Restore original angular drag prior to grabbing.
        currentlyHolding.cachedRigid.angularDrag = rigidOrigAngDrag;

        // Add torque based on multiple factors.
        // Faster objects have more torque.
        float velocityTorqueFactor = currentlyHolding.cachedRigid.velocity.magnitude * 0.0005f;
        // Grabbing object near the edge will add more torque as opposed to grabbing near middle.
        float grabOffCenterTorqueFactor = grabObjOffset.magnitude * 0.002f;
        float torqueAmount = 0.0005f + velocityTorqueFactor + grabOffCenterTorqueFactor;

        currentlyHolding.cachedRigid.AddTorque(Random.onUnitSphere * torqueAmount, ForceMode.Impulse);

        // Send toss event for boomerang and other behaviors.
        currentlyHolding.OnTossed();
        currentlyHolding = null;
    }