Search code examples
c#unity-game-enginegame-physics

Why are my characters flipping through the floor?


I am working on a simple patrol script, and everything works but the characters are randomly rolling through the floor when they turn around. Here is a short clip of them...

https://gfycat.com/gifs/detail/EnchantedShabbyChihuahua

Here is my script...

public class Patrol : MonoBehaviour
{
    public float speed = 5;
    public float directionChangeInterval = 1;
    public float maxHeadingChange = 30;
    public bool useRootMotion;

    CharacterController controller;
    float heading;
    Vector3 targetRotation;

    Vector3 forward
    {
        get { return transform.TransformDirection(Vector3.forward); }
    }

    void Awake()
    {
        controller = GetComponent<CharacterController>();

        // Set random initial rotation
        heading = Random.Range(0, 360);
        transform.eulerAngles = new Vector3(0, heading, 0);

        StartCoroutine(NewHeadingRoutine());
    }

    void Update()
    {
        transform.eulerAngles = Vector3.Slerp(transform.eulerAngles, targetRotation, Time.deltaTime * directionChangeInterval);
        if (useRootMotion)
        {
            return;
        }
        else
        {
            controller.SimpleMove(forward * speed);
        }
    }

    void OnControllerColliderHit(ControllerColliderHit hit)
    {
        if (hit.gameObject.tag == "Player")
        {
            // Bounce off the obstacle and change direction
            var newDirection = Vector3.Reflect(forward, hit.normal);
            transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
            heading = transform.eulerAngles.y;
            NewHeading();
        }
        if (hit.gameObject.tag == "Boundary")
        {
            // Bounce off the obstacle and change direction
            var newDirection = Vector3.Reflect(forward, hit.normal);
            transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
            heading = transform.eulerAngles.y;
            NewHeading();
        }
    }


    /// Finds a new direction to move towards.
    void NewHeading()
    {
        var floor = transform.eulerAngles.y - maxHeadingChange;
        var ceil = transform.eulerAngles.y + maxHeadingChange;
        heading = Random.Range(floor, ceil);
        targetRotation = new Vector3(0, heading, 0);
    }

    /// Repeatedly calculates a new direction to move towards.
    IEnumerator NewHeadingRoutine()
    {
        while (true)
        {
            NewHeading();
            yield return new WaitForSeconds(directionChangeInterval);
        }
    }
}

I have tried adding a rigidbody to the characters and constraining rotation, but that doesnt work. Oddly enough, the character control isnt rotating at all. In the scene view I can see the character collider staying as it should, but the character flips through the mesh on its own.


Solution

  • It looks like it's because they are walking into a corner and being bounced between the two walls constantly which causes them to behave strangely. I would add a method of checking for a series of very quick collisions to detect that they are in a corner or stuck and then adapt accordingly, perhaps with a method to rotate 180 degrees and keep walking or the like.

    You can do it like this:

    float fTime = 0.1f
    float fTimer = 0;
    int iCollisionCounter;
    if(collision){
       if(fTimer > 0) iCollisionCounter++;
       if(iCollisionCounter >= 5) //Character is stuck
       fTimer = fTime;
    }
    Void Update(){
        fTimer -= time.deltaTime;
    }
    

    That means that if there are multiple collisions within 0.1 seconds of each other you can handle it.

    Hope this helps!