I wrote a script that I attach to my gameobject with a charactercontroller to handle determining when a controller is entered, stayed, and exited.
I just track what colliders had hit me last update, and determine the status off that. If the new hit was never in my collection, it’s an enter. If it’s in my collection, it’s a stay. And if there’s something in my collection that wasn’t hit this update, we exited.
If ManuallySet is true, you should call Resolve after every move.
Otherwise, if auto, it presumes that Move is called EVERY Update/FixedUpdate (whichever you flag it for). Because the OnControllerColliderHit message occurs only during the call to Move.
This script of mine dispatches the enter, stay, exit messages using my custom ‘Notification’ system. You can dispatch it as custom messages if you’d like, mono/.net events, or your own notification system. I prefer mine because my notification system understands my ‘entity’ structure which allows messaging between gameobjects in a group of gameobjects that have a ‘root’ gameobject, as well as being type safe. If you’re at all interested in that notification system, I can share that as well.
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using com.spacepuppy.Collections;
namespace com.spacepuppy.Hooks
{
[AddComponentMenu("SpacePuppy/Hooks/CharacterController Collision Notifier")]
public class CharacterControllerCollisionNotifier : SPComponent
{
public bool ManuallySet = false;
public bool UseFixedUpdate = false;
public bool NotifyThisRoot = true;
public bool NotifyOther = false;
public bool NotifyOtherRoot = false;
private CharacterController _controller;
private UniqueList<Collider> _stayedColliders = new UniqueList<Collider>();
private UniqueList<Collider> _enteredColliders = new UniqueList<Collider>();
protected override void Awake()
{
base.Awake();
_controller = this.GetComponent<CharacterController>();
}
void Update()
{
if (ManuallySet || UseFixedUpdate) return;
this.Resolve();
}
void FixedUpdate()
{
if (ManuallySet || !UseFixedUpdate) return;
this.Resolve();
}
public void Resolve()
{
//TODO - reimplement this so that we have all the hit information
var entered = _enteredColliders.Except(_stayedColliders).ToArray();
var stayed = _stayedColliders.Intersect(_enteredColliders).ToArray();
var exited = _stayedColliders.Except(_enteredColliders).ToArray();
foreach (var c in entered)
{
_stayedColliders.Add(c);
var n = new CharacterControllerCollisionEnterNotification(_controller, c);
Notification.PostNotification<CharacterControllerCollisionEnterNotification>(this, n, this.NotifyThisRoot);
if (this.NotifyOther) Notification.PostNotification<CharacterControllerCollisionEnterNotification>(c, n, this.NotifyOtherRoot);
}
foreach (var c in stayed)
{
var n = new CharacterControllerCollisionStayNotification(_controller, c);
Notification.PostNotification<CharacterControllerCollisionStayNotification>(this, n, this.NotifyThisRoot);
if (this.NotifyOther) Notification.PostNotification<CharacterControllerCollisionStayNotification>(c, n, this.NotifyOtherRoot);
}
foreach (var c in exited)
{
_stayedColliders.Remove(c);
var n = new CharacterControllerCollisionExitNotification(_controller, c);
Notification.PostNotification<CharacterControllerCollisionExitNotification>(this, n, this.NotifyThisRoot);
if (this.NotifyOther) Notification.PostNotification<CharacterControllerCollisionExitNotification>(c, n, this.NotifyOtherRoot);
}
_enteredColliders.Clear();
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
_enteredColliders.Add(hit.collider);
}
}
}
You’ll notice my code has a todo note saying to reimplement so that we have all the hit information. Currently I only track the collider that was stayed, and not all the ‘hit’ info for that stay. So my notification only informs all what collider was entered, stayed, exited. If you need more, then you’ll need to store that info as well. I never needed it, so I didn’t bother wasting the memory tracking it.
You’ll also notice I use a ‘UniqueList’, this is a custom list that implements IList. Really all it does is when adding, inserting, and setting, it checks if the item being put in the list already exists or not. Making sure that no item is added twice. You could make your own version of this… or you just test if the list contains the object before adding.