Can you make CharacterController generate Collide/Trigger events?

I’m making a game where the characters change their scale. In order to make this work robustly, I need to have detailed information about collisions. But the CharacterController’s ControllerColliderHit and ColliderFlags seem to be very unreliable. Other components, such as Box Collider, generate much more consistent and reliable information. Is there a way to make the CharacterController generate the collision events as well, or at least have more reliable events?

You’re right about ColliderFlags - they are almost useless. The ControllerColliderHit is reliable, but it reports collisions with the ground all the time. You can filter out these collisions checking the normal: since normal.y is the Sin of the angle from the horizontal plane, you can ignore those collisions with y above some fixed value:

function OnControllerColliderHit(col: ControllerColliderHit){

    if (col.normal.y < 0.9){
        // lateral collision
        Debug.DrawRay(col.point, col.normal, Color.blue);
    } else {
        // bottom collision
        Debug.DrawRay(col.point, col.normal, Color.red);
    }
}

This code draws the collision normal in red if it’s higher than about 65 degrees (0.9 = Sin(65)), and in blue for lateral collisions (remember to click Gizmos in the Game view to enable Debug.DrawRay).

An alternative is to use a rigidbody instead of the CharacterController. You must freeze rotations to avoid weird reactions, and use transform.Translate(direction) at FixedUpdate to move the character. This script simulates a character controller with a rigidbody. Create an empty object, add a rigidbody and a capsule collider (the box collider behaves strangely), then attach this script. It rotates with AD and moves with WS, and can even jump.

var speed : float = 8.0;
var jumpSpeed : float = 9.0;
var turnSpeed : float = 90;

private var moveDirection: Vector3 = Vector3.zero;
private var distGround: float;
private var isGrounded: boolean = false;

function Start(){
    rigidbody.freezeRotation = true; // disable physics rotation
    var caps: CapsuleCollider = transform.collider;
    distGround = caps.height / 2 - caps.center.y; // distance to ground
}

function Update(){
    var hit: RaycastHit; // use raycast to check if character grounded
    isGrounded = Physics.Raycast(transform.position, -transform.up, distGround + 0.1);
    if (isGrounded && Input.GetButtonDown("Jump")){ // only jump from the ground
        rigidbody.velocity.y += jumpSpeed; 
    }
    // rotate character and define direction to move
    transform.Rotate(0, Input.GetAxis("Horizontal") * turnSpeed * Time.deltaTime, 0);
    moveDirection = Vector3.forward * Input.GetAxis("Vertical") * speed;
}

function FixedUpdate(){ // move at FixedUpdate to get stable results
    transform.Translate(moveDirection * Time.deltaTime);
}