Skip to content

Instantly share code, notes, and snippets.

@sim2kid
Last active April 5, 2022 07:29
Show Gist options
  • Save sim2kid/d51021c9346db614d3ec9a36fe665d3f to your computer and use it in GitHub Desktop.
Save sim2kid/d51021c9346db614d3ec9a36fe665d3f to your computer and use it in GitHub Desktop.
A tool used to make sure TriggerExit is reliably called for a MonoBehavior. This will ensure when a collider is disabled, your exit events still happens outside of it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
/// <summary>
/// A tool used to make sure TriggerExit is reliably called for a MonoBehavior. This will ensure when a collider is disabled, your exit events still happens outside of it.
/// Just hook in the AddTrigger, FixedUpdate, and RemoveTrigger to your OnTriggerEnter, FixedUpdate, and OnTriggerExit to get started.
/// </summary>
public class TrackTrigger
{
public delegate void OnTriggerExit(Collider other);
private List<TriggerFrame> Triggers = new List<TriggerFrame>();
private GameObject origin;
public TrackTrigger(GameObject origin)
{
this.origin = origin;
}
/// <summary>
/// Called in "OnTriggerEnter" to start tracking the trigger
/// </summary>
/// <param name="other">The other collider passed into OnTriggerEvent</param>
/// <param name="callback">The "OnTriggerExit" function</param>
public void AddTrigger(Collider other, OnTriggerExit callback)
{
if (!Contains(other))
{
Triggers.Add(new TriggerFrame(other, callback));
}
}
/// <summary>
/// Hooked into the FixedUpdate loop. This will check the triggers to see if they need to be removed.
/// </summary>
public void FixedUpdate()
{
// Remove dead triggers first
Triggers.RemoveAll(x => x.removed);
// Check Triggers.
foreach (var trigger in Triggers)
{
// If they haven't been called last frame...
if (trigger.calledLastFrame == false)
{
// Register them for removal
if (trigger.removed != true)
{
// And run a callback
TriggerCallback(trigger);
trigger.removed = true;
}
}
// Set all triggers to false.
trigger.calledLastFrame = false;
}
}
/// <summary>
/// Called in the OnTriggerStay loop. This lets us know the trigger still exists
/// </summary>
/// <param name="other"></param>
public void TriggerUpdate(Collider other)
{
// Set the trigger to True
var tFrame = Find(other);
if (tFrame != null)
{
tFrame.calledLastFrame = true;
}
}
/// <summary>
/// Called in the OnTriggerExit method to let us know the trigger has been removed.
/// </summary>
/// <param name="other"></param>
public void RemoveTrigger(Collider other)
{
// Schedules a trigger for removal but won't remove it until next update
Find(other).removed = true;
}
private void TriggerCallback(TriggerFrame frame)
{
frame.callback.Invoke(frame.collider);
}
private TriggerFrame Find(Collider other)
{
return Triggers.FirstOrDefault(x => x.collider.Equals(other));
}
private bool Contains(Collider other)
{
return Triggers.Any(x => x.collider.Equals(other));
}
private class TriggerFrame : System.IEquatable<TriggerFrame>
{
public Collider collider;
public bool calledLastFrame;
public bool removed;
public OnTriggerExit callback;
public TriggerFrame(Collider other, OnTriggerExit callback)
{
collider = other;
this.callback = callback;
calledLastFrame = true;
removed = false;
}
public bool Equals(TriggerFrame other)
{
return collider.Equals(other.collider);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrackTriggerUsage : MonoBehaviour
{
private TrackTrigger triggerEvent;
void Start()
{
triggerEvent = new TrackTrigger(gameObject);
}
private void FixedUpdate()
{
triggerEvent.FixedUpdate();
}
private void OnTriggerStay(Collider other)
{
triggerEvent.TriggerUpdate(other);
}
private void OnTriggerEnter(Collider other)
{
triggerEvent.AddTrigger(other, OnTriggerExit);
}
private void OnTriggerExit(Collider other)
{
triggerEvent.RemoveTrigger(other);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment