// Update is called once per frame
    void FixedUpdate()
    {
        float   hdir        = Input.GetAxis("Horizontal");
        float   vdir        = Input.GetAxis("Vertical");
        Vector3 movementDir = Vector3.zero;

        Vector3     dirToPanel = Vector3.zero;
        SpiderPanel closest    = null;

        if (Input.GetAxis("RightTrigger") > 0.0)
        {
            closest = FindClosestPanel(out dirToPanel);
        }

        state = closest != null ? DroneBallState.Spider : DroneBallState.Normal;
        rigidbody.useGravity = state == DroneBallState.Normal;
        rigidbody.drag       = state == DroneBallState.Normal ? 0.1f : 2.0f;

        bool onground = true;

        if (state == DroneBallState.Normal)
        {
            RaycastHit hit = new RaycastHit();
            if (Physics.Raycast(transform.position, Vector3.down, out hit, 0.8f))
            {
                /*
                 * Vector3 right = Vector3.Cross(hit.normal, Vector3.forward).normalized;
                 * Vector3 forward = Vector3.Cross(right, hit.normal).normalized;
                 * movementDir = (right * Vector3.Dot(right, Vector3.right) * hdir + forward * Vector3.Dot(forward, Vector3.forward) * vdir).normalized;
                 */
                movementDir = new Vector3(hdir, 0.0f, vdir);
            }
            else
            {
                onground = false;
            }
        }
        else if (state == DroneBallState.Spider)
        {
            float dot = Mathf.Abs(Vector3.Dot(closest.transform.up, dirToPanel));
            rigidbody.AddForce(dirToPanel * spiderStickForce * Time.fixedDeltaTime);
            if (dot > 0.8)
            {
                movementDir = (closest.transform.right * hdir + closest.transform.forward * vdir).normalized;
            }
        }

        rigidbody.AddForce(movementDir * movementForce * Time.fixedDeltaTime);

        if (onground)
        {
            rigidbody.velocity = ClampV3(rigidbody.velocity, -maxSpeed, maxSpeed);
        }

        camera.transform.position = transform.position + Vector3.back * cameraFollowDist;
        camera.transform.LookAt(transform.position);
    }
    Vector3 ClosestPointOnPlane(Vector3 pos, SpiderPanel plane)
    {
        Vector3 planePos = plane.transform.position;
        Vector3 bounds   = plane.ScaledBounds;
        float   zdist    = Mathf.Clamp(DistAlongAxis(pos, planePos, plane.transform.forward), -bounds.z, bounds.z);
        float   xdist    = Mathf.Clamp(DistAlongAxis(pos, planePos, plane.transform.right), -bounds.x, bounds.x);

        return(planePos + plane.transform.forward * zdist + plane.transform.right * xdist);
    }
    SpiderPanel FindClosestPanel(out Vector3 dirToPanel)
    {
        SpiderPanel closestPanel = null;
        float       shortestDist = float.MaxValue;

        dirToPanel = Vector3.zero;

        foreach (SpiderPanel p in spiderPanels)
        {
            Vector3 pointOnP          = ProjectToPlane(transform.position, p.transform.position, p.transform.up);
            Vector3 closestPointOnP   = ClosestPointOnPlane(pointOnP, p);
            Vector3 toClosestPointOnP = closestPointOnP - transform.position;
            float   dist = toClosestPointOnP.magnitude;

            if (dist < maxSpiderGrabDist && (closestPanel == null || dist < shortestDist))
            {
                closestPanel = p;
                shortestDist = dist;
                dirToPanel   = toClosestPointOnP.normalized;
            }
        }

        return(closestPanel);
    }