0
\$\begingroup\$

I have a Unity CharacterController, that can detect collisions using the following code:

void OnControllerColliderHit(ControllerColliderHit hit) {
    Debug.Log(hit.gameObject.name); 
    if (hit.gameObject.name == "PinkBlock"){
        Debug.Log("Do something here");
    }

}

This works, but the code has to be attached to a charactercontroller. I cannot figure out how to detect if any normal RigidBody was hit by a CharacterController. This is my rigidbody code (just a block in the scene).

void OnCollisionEnter(Collision collision) {
    Debug.Log("Collision detected");
    if (collision.gameObject.name == "MyGameObjectName"){
        Debug.Log("Do something here");
    }
}

But this doesn't trigger for the CharacterController. How can I respond to collisions with a character controller from a script that is attached to a a normal rigidbody component?

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

You could add a script like this to your object with a Character Controller:

void OnControllerColliderHit(ControllerColliderHit hit) {
    // Check whether the collidee has a body:
    var hitBody = hit.collider.attachedRigidbody;
    if (hitBody != null) {

        // If so, invoke any matching methods
        // on that object:
        hitBody.SendMessage(
            "OnHitByController",
            gameObject,
            SendMessageOptions.DontRequireReceiver
        );
    }
}

This effectively adds a new message method, so any script on the Rigidbody that our character hit can implement:

public void OnHitByController(GameObject controllerObject) {
   // Do stuff.
}

...and now it will get notified of collisions with character controllers that use the script above.


But that code is "stringly-typed", which is not as efficient or as robust against bugs as we can achieve with just a little more work:

public interface IControllerCollidable {
    void OnHitByController(GameObject controllerObject);
}

Implement this interface on whatever script you want responding to character controller hits.

Then revise the controller script to...

void OnControllerColliderHit(ControllerColliderHit hit) {
    // Switching to a guard clause for less nesting.
    var hitBody = hit.collider.attachedRigidbody;
    if (hitBody == null)
        return;

    // Check if body has a script attached that wants
    // to know about controller collisions.
    // (If not, abort)
    if (!hitBody.TryGetComponent(out IControllerCollidable hitScript)
        return;
    
    // Call the method we want.
    hitScript.OnHitByController(gameObject);
}

Note that in this version has no magic strings, and is able to use a normal virtual function call instead of searching for matching receivers by name, or doing extra type casting on the argument.

It's slightly more restrictive in that it will talk to only one script on the Rigidbody we hit, instead of broadcasting to all scripts on that object. We could adjust this to have the same one-to-many communication as SendMessage, though it comes with some extra cost, and we usually have just one script that needs to handle this anyway, so I've shown the simpler/more efficient single-receiver version here.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks, this is working! But there is a delay of about ~0.5 second before the message actually sends/is received. Any idea what could cause this delay? \$\endgroup\$
    – Kokodoko
    Commented 2 days ago
  • \$\begingroup\$ I'd need to see a Minimal Complete Verifiable Example. It's likely this apparent delay is coming from some other interaction in your project. \$\endgroup\$
    – DMGregory
    Commented 2 days ago

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .