'Make an object fall when it is near an edge using character controller

I am using the unity's character controller tool.

The problem is that if there exist below collision with the default character controler's capsule collider it won't slide down or fall unless could not detect any touch with a surface.

This is how it looks like: enter image description here

I am using my own gravity just like this:

private void ApplyGravity() {
        if (!characterController.isGrounded) {
            gravity += Physics.gravity * Time.deltaTime * gravityFactor;
            if (gravity.y > 0 && !Input.GetButton(jumpButton))
                gravity += Physics.gravity * Time.deltaTime * gravityFactor * (lowJumpMultiplier - 1);
        }
        else if (!isJumping) gravity = Vector3.down;

        CheckCollisionFlags();
        movement += gravity;
    }

I've not found any way of modifying the character controller's properties to apply any slippery or adding a new collider to take the collision.

I already tried to reduce the capsule collider's radius but this results in a character that can go slightly through the walls.

I've thought about using a number of rays to check if middle of the body is beyond the edge and then apply a force to make it fall, but I think could be an easier and more optimal way to solve this problem.



Solution 1:[1]

I finally managed to solve this problem.

What I headed first was in checking if the middle of the character was grounded or not. I achieved that by launching a ray right in the position of the object. Then, I wanted two know which side was not colliding with a surface. I shooted four more rays, one in each point cardinal around the capsule collider. I retrieved every point that was not grounded and calculated the difference with the current character position. It gaves me a coordinate that I added to the movement of the object that will be increasing until it falls.

Let's see it:

This is how I created every ray. I needed the two values (x,z) to modify the point cardinal every ray had to be placed in. Then, I just set the y in the ground by deducting the half of the height of the capsule collider.

private Ray CreateEdgeCheckRay(float x, float z) {
        Vector3 rayPos = transform.position;
        rayPos.y -= characterController.height / 2;
        rayPos.x += x;
        rayPos.z += z;
        return new Ray(rayPos, Vector3.down);
    }

I used this method just to create every single ray at once:

private Ray[] CreateSideRays() {
        Ray[] sideRays = new Ray[4];
        sideRays[0] = CreateEdgeCheckRay(0.5f, 0);
        sideRays[1] = CreateEdgeCheckRay(-0.5f, 0);
        sideRays[2] = CreateEdgeCheckRay(0, 0.5f);
        sideRays[3] = CreateEdgeCheckRay(0, -0.5f);

        return sideRays;
    }

I used Physics.RaycastNonAlloc retrieving the results in a length 1 array to improve performance.

private bool RayGrounded(Ray ray) {
        return Physics.RaycastNonAlloc(ray, rayResults, groundRayLength) < 1;
    }

Finally, I joined everything together. I don't need any vertical movement as I implemented gravity previously.

private void CheckGroundRays() {
        Ray middleRay = CreateEdgeCheckRay(0, 0);
        if (RayGrounded(middleRay)) {
            Vector3 inEdgeMovement = Vector3.zero;
            foreach(Ray ray in CreateSideRays()) {
                if(RayGrounded(ray))
                    inEdgeMovement += (ray.origin - transform.position);
            }
            inEdgeMovement.y = 0;
            movement += inEdgeMovement;
        }
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rubzero