Unityâs physics/collision system has lots of issues
-OnCollision/TriggerExit is not called when the other object in a current collision is destroyed.
-OnCollision/TriggerExit is not called when the self object in a current collision is destroyed.
-OnCollision/TriggerExit is not called when the other object in a current collision is disabled.
-OnCollision/TriggerExit is not called when the self object in a current collision is disabled.
-OnCollision/TriggerExit is not called when the other object in a current collisionâs layer is changed (If I remember right)
-OnCollision/TriggerExit is not called when the self object in a current collision layer is changed (If I remember right)
-The order of OnCollision/Trigger Enter/Exit is not consistent. If an object is changed to/from a Collider to a Trigger you can get 2 Enterâs or 2 Exitâs in a row.
-Unityâs collision system does not allow you to check a list of current collisions (and maintaining a list is unreliable due to bugs above)
-Unityâs collision system does not allow you to instantly spot check what is in a new mesh collider. For example if a dragon breathes fire you wanted to create a cone mesh and take a snapshot to see what is within the cone there is no way to do instantly do this. You must instantiate the cone, and then wait for the next fixed update.
-Unityâs collision system does not handle hierarchy well. If you have several colliders on each object itâs difficult to tell what collided/triggered with what. Colliders & Triggers also do not behave consistently in hierarchies.
-Rigidbody-interpolate affects velocity (https://fogbugz.unity3d.com/default.asp?987466_rtr9jodnm5n8afbp)
Hereâs the code I used to fix. Itâs really ugly. I have to add a ColliderWatcher to each collider. But it fixes most bugs above. Maybe somebody can clean it up a bit.
using UnityEngine;
using System.Collections;
using System.Collections.Generic; //allow List and Dictionary
//This script is always placed in the same level of the hierarchy as the collider
public sealed class ColliderWatcher : MonoBehaviour
{
public Collider colliderSelf { get; private set; }
private Rigidbody attachedRigidbody; //store this value for errorChecking at a later time (to make sure a rigidbody isn't added AFTER colliderWatcher is initialized)
public List<ColliderWatcher> listColliderWatcherOther { get; private set; } //list of current triggers/collisions
//public List<Collider> listColliderOther { get; private set; }
//public delegate void DelegateCollider(Collider colliderOther, Collision collisionCBNIn); //signature of function
//private DelegateCollider OnEnterEventSender; //called by OnCollisionEnter OR OnTriggerEnter
//private DelegateCollider OnExitEventSender; //called by OnCollisionExit OR OnTriggerExit OR when either collider is disabled/destroyed
public delegate void DelegateTriggerCollision(TriggerCollisionData triggerCollisionData); //signature of function
private DelegateTriggerCollision OnTriggerCollisionEventSender; //called by OnTriggerXXX OR OnCollisionXXX OR when disabled (or when something it is currently colliding with is disabled)
//public delegate void DelegateTriggerCollide(EnumEnterExit enumEnterExitIn, Collider colliderOther, Collision collisionCBNIn)
//public Collider[] debugColliderArray; //serialized for debug
//public string[] debugCurrentCollisions;
private void Awake()
{
//listColliderOther = new List<Collider>(); //initialize list
listColliderWatcherOther = new List<ColliderWatcher>(); //initialize list
InitializeFindColliderSelf();
}
private void Start()
{
InitializeAddColliderWatcherHelper(); //is it better to do this during Start()? That way any changing of hierarchy is complete before it adds ColliderWatcherHelper?
}
private void InitializeFindColliderSelf()
{
colliderSelf = GetComponent<Collider>(); //every ColliderWatcher must be in the same hierarchy level as it's collider (even though OnCollision/Trigger/Enter/Exit may not be called on this layer)
if (colliderSelf == null) //ErrorCheck
{
BatDebug.Error(this, "768uytyrt3r467", "colliderSelf == null");
}
}
private void InitializeAddColliderWatcherHelper()
{
if (colliderSelf != null) //ErrorCheck
{
attachedRigidbody = colliderSelf.attachedRigidbody; //the rigidbody MUST be attached before ColliderWatcher is initialized. otherwise ColliderWatcherSender will be in the wrong layer of the hierarchy
if (attachedRigidbody == null) //this collider does not have an attachedRigidbody
{
colliderSelf.transform.gameObject.AddComponent<ColliderWatcherHelper>(); //add the ColliderWatcherHelper directly to this layer of hierarchy
}
else //attachedRigidbody = true
{
ColliderWatcherHelper colliderWatcherHelperTemp = attachedRigidbody.gameObject.GetComponent<ColliderWatcherHelper>();
if (colliderWatcherHelperTemp == null)
{
//Debug.Logz("Adding ColliderWatcherHelper to rigidbody");
attachedRigidbody.gameObject.AddComponent<ColliderWatcherHelper>(); //add the ColliderWatcherHelper to the parent layer of hierarchy that contains rigidbody
}
//else
//{
// Debug.Logz("Not ColliderWatcherHelper to rigidbody. Already has this compoment. Can occur if there are multiple colliders all feeding into 1 rigidbody");
//}
}
}
else
{
BatDebug.Error(this, "5y6uhtrgr3645y7u6jyin", "colliderSelf == null");
}
}
//public void AddColliderOther(Collider colliderOtherIn)
//{
// Debug.Logz("ColliderWatcher2.AddColliderOther");
//}
//public void RemoveColliderOther(Collider colliderOtherIn)
//{
// Debug.Logz("ColliderWatcher2.RemoveColliderOther");
//}
/*
public void AddRemoveColliderOther(Collider colliderOtherIn, EnumEnterExit enumEnterExitIn) //called by ColliderWatcherHelper when a pair is found
{
if(colliderOtherIn != null) //ErrorCheck
{
if(enumEnterExitIn == EnumEnterExit.Enter)
{
AddColliderToList(colliderOtherIn);
}
else if (enumEnterExitIn == EnumEnterExit.Exit)
{
RemoveColliderFromList(colliderOtherIn);
}
else
{
Debug.Logz("ERROR: Invalid enum");
}
}
else
{
Debug.Logz("ERRORz");
}
}
*/
private void AddColliderWatcherToList(ColliderWatcher colliderWatcherOtherIn) //Add to list w/ ErrorCheck
{
if (!listColliderWatcherOther.Contains(colliderWatcherOtherIn)) //ErrorCheck, make sure list does not already contain colliderWatcherOther
{
listColliderWatcherOther.Add(colliderWatcherOtherIn); //add to list
}
else
{
BatDebug.Error(this, "656uyjnhgrer3243", "Already contains collider: Should never happen");
}
}
private void RemoveColliderWatcherFromList(ColliderWatcher colliderWatcherOtherIn) //Remove from list w/ ErrorCheck
{
if (listColliderWatcherOther.Contains(colliderWatcherOtherIn)) //ErrorCheck, make sure list does not already contain colliderWatcherOther
{
listColliderWatcherOther.Remove(colliderWatcherOtherIn); //remove from list
}
else
{
BatDebug.Error(this, "457yu6jythrgtr345y", "List does not contain ColliderWatcher: Should never happen");
}
}
/*
private void AddColliderToList(Collider colliderOtherIn)
{
//Debug.Logz(gameObject.name + ".AddColliderToList " + colliderOtherIn.gameObject.name);
if (!listColliderOther.Contains(colliderOtherIn)) //ErrorCheck, make sure list does not already contain colliderOther
{
listColliderOther.Add(colliderOtherIn); //add to list
if (colliderOtherIn.GetComponent<ColliderWatcher>() == null) //ErrorCheck, make sure colliderOther has an associated ColliderWatcher
{
Debug.Logz("ERROR: ColliderOther does not contain ColliderWatcher: " + colliderOtherIn.gameObject.name + " " + colliderOtherIn.transform.root.gameObject.name);
}
if (OnEnterEventSender != null) //at least 1 listener
{
Debug.Logz("FIXME NULL PLACEHOLDER"); OnEnterEventSender.Invoke(colliderOtherIn, null);
}
}
else
{
Debug.Logz("ERROR: Already contains collider: Should never happen. " + gameObject.NameHierarchy() + " attempting to add " + colliderOtherIn.gameObject.NameHierarchy());
}
//DebugRefreshDebugColliderArray();
}
private void RemoveColliderFromList(Collider colliderOtherIn)
{
//Debug.Logz(gameObject.name + ".RemoveColliderFromList " + colliderOtherIn.gameObject.name);
if (colliderOtherIn != null)
{
if (listColliderOther.Contains(colliderOtherIn)) //ErrorCheck, make sure list contains colliderOther before it's removed
{
listColliderOther.Remove(colliderOtherIn); //remove from list
if (OnExitEventSender != null) //at least 1 listener
{
Debug.Logz("FIXME NULL PLACEHOLDER"); OnExitEventSender.Invoke(colliderOtherIn, null);
}
}
else
{
Debug.Logz("ERROR: List does not contain collider. It cannot be removed. Should never happen." + gameObject.NameHierarchy() + " otherGORoot=" + colliderOtherIn.gameObject.NameHierarchy());
}
}
else
{
Debug.Logz("ERROR: colliderOtherIn == null" + gameObject.name + " " + gameObject.transform.root.gameObject.name);
}
//DebugRefreshDebugColliderArray();
}
*/
public void OnTriggerCollisionPairXXX(TriggerCollisionData triggerCollisionDataIn) //the other collision info is sent to this ColliderWatcher, and this ColliderWatcher's collision info is sent to the other ColliderWatcher
{
//Debug.Logz("OnTriggerCollisionPairXXX: Self:" + gameObject.NameHierarchy() + " Other:" + triggerCollisionDataIn.colliderWatcherOther.gameObject.NameHierarchy() + " " + Time.time);
if(triggerCollisionDataIn != null) //ErrorCheck
{
if(triggerCollisionDataIn.enumEnterExit == EnumEnterExit.Enter)
{
//listColliderOther.Add(triggerCollisionDataIn.coll); //add to list
//listColliderWatcherOther.Add(triggerCollisionDataIn.colliderWatcherOther);
AddColliderWatcherToList(triggerCollisionDataIn.colliderWatcherOther);
//new TriggerCollisionData()
}
else if (triggerCollisionDataIn.enumEnterExit == EnumEnterExit.Exit)
{
//listColliderWatcherOther.Remove(triggerCollisionDataIn.colliderWatcherOther);
RemoveColliderWatcherFromList(triggerCollisionDataIn.colliderWatcherOther);
}
else
{
BatDebug.Error(this, "5467yur4t35r42", "Unhandled enum case");
}
if (OnTriggerCollisionEventSender != null) //at least 1 listener
{
OnTriggerCollisionEventSender.Invoke(triggerCollisionDataIn);
}
}
else
{
BatDebug.Error(this, "vf34FDSadf", "triggerCollisionDataIn == null");
}
//DebugRefreshCurrentCollisions();
}
private void OnEnable()
{
//Debug.Logz("OnEnable(): " + gameObject.NameHierarchy());
if (colliderSelf != null)
{
colliderSelf.enabled = true;
}
else
{
BatDebug.Error(this, "456tjhgrer243r54t", "colliderSelf == null");
}
}
private void OnDestroy() //If something is Destroy(gameObject) during OnCollision in the Physics loop it will be disabled immediately, but disabled at the END of all triggers/collisions. Attempting to execute stack during OnDestroy is a good spot.
{
//Debug.Logz(this.GetType().Name + ".OnDestroy(): " + gameObject.NameHierarchy() + " " + Time.time + " currentCollisions=" + listColliderWatcherOther.Count);
ColliderWatcherManager.ColliderWatcherDisabled.ExecuteStack("OnDestroy");
}
private void OnDisable() //this is called before something is destroyed. it should also be called if a dead monster is made hollow (the collider.enabled should NOT be set directly) this is used because other scripts will still exist (they won't be null'd yet)
{
//Debug.Logz(this.GetType().Name + ".OnDisable(): " + gameObject.NameHierarchy() + " " + Time.time + " currentCollisions=" + listColliderWatcherOther.Count + " " + ScriptExecutionOrder.enumLoop);
if (colliderSelf != null)
{
colliderSelf.enabled = false;
}
else
{
BatDebug.Error(this, "3454thtbgrr233rt", "colliderSelf == null");
}
new ColliderWatcherManager.ColliderWatcherDisabled(this); //adds to stack & executes stack. Even if current collisions = 0 it still adds to stack because it's possible that some collisions may still occur during this loop and add to list
//ColliderWatcherManager
//Debug.Logz("OnDisable2: " + gameObject.NameHierarchy() + " " + Time.time + " currentCollisions=" + listColliderWatcherOther.Count);
//for (int i = 0; i <= 10000; i++) //~while loop, prevent infinite looping
//{
// if (i == 10000)
// {
// Debug.Logz("ERROR: Infinite Loop");
// }
// if (listColliderWatcherOther.Count > 0)
// {
// //listColliderWatcherOther[0].ExecuteAndRemoveFromStack();
// }
// else //has cleared all collisions
// {
// break;
// }
//}
/*
for (int i = listColliderOther.Count - 1; i >= 0; i--) //reverse iterate because collection will be modified
{
if (listColliderOther[i] != null)
{
ColliderWatcher colliderWatcherOther = listColliderOther[i].GetComponent<ColliderWatcher>();
if (colliderWatcherOther != null)
{
colliderWatcherOther.RemoveColliderFromList(colliderSelf); //remove this collliderSelf from the OTHER watcher
RemoveColliderFromList(listColliderOther[i]); //remove colliderOther from this' list
}
else
{
Debug.Logz("ERROR: ColliderOther does not contain ColliderWatcher: " + listColliderOther[i].gameObject.name + " " + listColliderOther[i].transform.root.gameObject.name);
}
}
else
{
Debug.Logz("ERROR: listColliderOther[" + i + "] == null. This name = " + gameObject.transform.root.gameObject.name);
}
}
*/
}
public void OnDisabledFromStack() //called from ColliderWatcherManager when this is removed from stack (occurs after OnDisable)
{
//Debug.Logz("OnDisabledFromStack(): " + gameObject.NameHierarchy() + " " + Time.time + " currentCollisions=" + listColliderWatcherOther.Count);
TriggerCollisionData triggerCollisionDataA = new TriggerCollisionData(EnumTriggerCollision.Disable, EnumEnterExit.Exit, this, null);
for (int i = listColliderWatcherOther.Count - 1; i >= 0; i--) //reverse iterate because collection will be modified
{
if (listColliderWatcherOther[i] != null)
{
TriggerCollisionData triggerCollisionDataB = new TriggerCollisionData(EnumTriggerCollision.Disable, EnumEnterExit.Exit, listColliderWatcherOther[i], null);
TriggerCollisionData.ApplyPair(triggerCollisionDataA, triggerCollisionDataB);
//ColliderWatcher colliderWatcherOther = listColliderOther[i].GetComponent<ColliderWatcher>();
//if (colliderWatcherOther != null)
//{
//colliderWatcherOther.RemoveColliderFromList(colliderSelf); //remove this collliderSelf from the OTHER watcher
//RemoveColliderFromList(listColliderOther[i]); //remove colliderOther from this' list
//}
//else
//{
// Debug.Logz("ERROR: ColliderOther does not contain ColliderWatcher: " + listColliderOther[i].gameObject.name + " " + listColliderOther[i].transform.root.gameObject.name);
//}
}
else
{
BatDebug.Error(this, "5u6trhyt4r35", "listColliderOther[" + i + "] == null");
}
}
}
/*
public void AddListenerEnter(DelegateCollider delegateColliderIn) //subscribe to eventSender
{
if (delegateColliderIn != null)
{
OnEnterEventSender += delegateColliderIn;
}
else
{
Debug.Logz("ERROR: Null");
}
}
public void AddListenerExit(DelegateCollider delegateColliderIn) //subscribe to eventSender
{
if (delegateColliderIn != null)
{
OnExitEventSender += delegateColliderIn;
}
else
{
Debug.Logz("ERROR: Null");
}
}
*/
public void AddListener(DelegateTriggerCollision delegateTriggerCollisionIn)
{
if (delegateTriggerCollisionIn != null)
{
OnTriggerCollisionEventSender += delegateTriggerCollisionIn;
}
else
{
BatDebug.Error(this, "54675yutjhrtr423", "delegateTriggerCollisionIn == null");
}
}
public void RemoveListener(DelegateTriggerCollision delegateTriggerCollisionIn)
{
if (delegateTriggerCollisionIn != null)
{
OnTriggerCollisionEventSender -= delegateTriggerCollisionIn;
}
else
{
BatDebug.Error(this, "545ythgbfrew2rt45y", "delegateTriggerCollisionIn == null");
}
}
//private void DebugRefreshDebugColliderArray()
//{
// debugColliderArray = listColliderOther.ToArray();
//}
/*
private void DebugRefreshCurrentCollisions()
{
List<string> listStringTemp = new List<string>();
for(int i = 0; i < listColliderWatcherOther.Count; i++)
{
string otherNameTemp = "null";
if(listColliderWatcherOther[i] != null)
{
otherNameTemp = listColliderWatcherOther[i].gameObject.NameHierarchy();
}
listStringTemp.Add("[" + i + "]" + otherNameTemp);
}
debugCurrentCollisions = listStringTemp.ToArray();
}
*/
public bool isTrigger
{
get
{
return colliderSelf.isTrigger;
}
set
{
if(isTrigger != value) //value has change
{
//bool isEnabledPre = enabled;
if(listColliderWatcherOther.Count > 0) //currently enabled
{
BatDebug.Error(this, "465yhgrfe23rt4y", "annot change isTrigger while currently colliding?? Since Trigger always occurs before Collided it will either be added twice in a row or removed twice in a row causing errors");
}
}
else
{
BatDebug.Error(this, "45ythgrr3454y5", "Set to same value of " + value);
}
}
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic; //allow List
public class ColliderWatcherHelper : MonoBehaviour
{
//private static EnumEnterExit enumEnterExit = EnumEnterExit.Enter;
//private static Collider colliderA = null; //for OnTriggerXXX
//private static Collider colliderB = null; //for OnTriggerXXX
//private static Collision collisionA = null; //for OnCollisionXXX (will be null for trigger)
//private static Collision collisionB = null; //for OnCollisionXXX (will be null for trigger)
//private static float timeFixedTimeA = 1.111f;
//private static float timeFixedTimeB = 2.222f;
//private static List<ColliderPair> stackColliderPair = new List<ColliderPair>();
//private static bool currentlyExecutingStack = false;
private void Awake()
{
ColliderWatcher[] colliderWatcherArray = GetComponents<ColliderWatcher>();
if(colliderWatcherArray.Length >= 2) //There can only be 1 collisionHelper
{
BatDebug.Error(this, "54657u6yihjrge", "Already contains ColliderWatcherHelper. There should only be 1");
}
}
public void OnTriggerEnter(Collider colliderOtherIn)
{
//Debug.Logz("OnTriggerEnter: " + gameObject.NameHierarchy() + " other: " + colliderOtherIn.gameObject.NameHierarchy());
//Debug.Logz(gameObject.name + ".OnTriggerEnter");
//OnEnter(colliderOtherIn);
//if(ErrorCheckAndFindColliderWatcher(colliderOtherIn))
//{
// ColliderWatcherManager.OnTriggerXXX(EnumEnterExit.Enter, colliderOtherIn);
//}
//OnTriggerXXX(EnumEnterExit.Exit, colliderOtherIn);
OnTriggerCollisionXXX(EnumTriggerCollision.Trigger, EnumEnterExit.Enter, colliderOtherIn, null);
}
public void OnTriggerExit(Collider colliderOtherIn)
{
//Debug.Logz("OnTriggerExit: " + gameObject.NameHierarchy() + " other: " + colliderOtherIn.gameObject.NameHierarchy());
//Debug.Logz(gameObject.name + ".OnTriggerExit");
//OnExit(colliderIn);
//OnTriggerXXX(EnumEnterExit.Exit, colliderOtherIn);
OnTriggerCollisionXXX(EnumTriggerCollision.Trigger, EnumEnterExit.Exit, colliderOtherIn, null);
}
//private void OnTriggerXXX(EnumEnterExit enumEnterExitIn, Collider colliderOtherIn)
//{
// ColliderWatcher colliderWatcherOtherTemp = ErrorCheckAndFindColliderWatcher(colliderOtherIn); //if this fails it spits out errors
// if (colliderWatcherOtherTemp != null) //ErrorCheck
// {
// ColliderWatcherManager.OnTriggerXXX(EnumEnterExit.Exit, colliderWatcherOtherTemp, colliderOtherIn);
// }
//}
public void OnCollisionEnter(Collision collisionOtherIn)
{
//Debug.Logz("OnCollisionEnter: " + gameObject.NameHierarchy() + " other: " + collisionOtherIn.collider.gameObject.NameHierarchy());
//Debug.Logz(gameObject.name + ".OnCollisionEnter");
//OnEnter(collisionOtherIn.collider);
//ContactPoint[] contactPointArray = collisionOtherIn.contacts;
//Debug.Logz(gameObject.NameHierarchy() + ".OnCollisionEnter: Contact Points = " + contactPointArray.Length);
//for(int i = 0; i < contactPointArray.Length; i++)
//{
// Debug.Logz("[" + i + "] Self: " + contactPointArray[i].thisCollider.gameObject.NameHierarchy() + " Other: " + contactPointArray[i].otherCollider.gameObject.NameHierarchy());
//}
//if (!ErrorCheckColliderHasColliderWatcher(collisionOtherIn.collider))
//{
// ColliderWatcherManager.OnCollisionXXX(EnumEnterExit.Enter, colliderIn);
//}
OnTriggerCollisionXXX(EnumTriggerCollision.Collision, EnumEnterExit.Enter, collisionOtherIn.collider, collisionOtherIn);
}
public void OnCollisionExit(Collision collisionOtherIn)
{
//Debug.Logz("OnCollisionExit: " + gameObject.NameHierarchy() + " other: " + collisionOtherIn.collider.gameObject.NameHierarchy());
//Debug.Logz(gameObject.name + ".OnCollisionExit");
//OnExit(collisionOtherIn.collider);
OnTriggerCollisionXXX(EnumTriggerCollision.Collision, EnumEnterExit.Exit, collisionOtherIn.collider, collisionOtherIn);
}
//private void OnCollisionXXX(EnumEnterExit enumEnterExitIn, Collision collisionOtherIn)
//{
// ColliderWatcher colliderWatcherOtherTemp = ErrorCheckAndFindColliderWatcher(collisionOtherIn.collider); //if this fails it spits out errors
// if (colliderWatcherOtherTemp != null) //ErrorCheck
// {
// ColliderWatcherManager.OnCollisionXXX(EnumEnterExit.Exit, colliderWatcherOtherTemp, collisionOtherIn);
// }
//}
//private void OnEnter(Collider colliderOtherIn) //collision AND trigger
//{
// //Debug.Logz("OnEnter: " + gameObject.NameHierarchy() + " other: " + colliderOtherIn.gameObject.NameHierarchy());
// enumEnterExit = EnumEnterExit.Enter;
// OnEnterExit(colliderOtherIn);
//}
//private void OnExit(Collider colliderOtherIn) //collision AND trigger
//{
// //Debug.Logz("OnEnter: " + gameObject.NameHierarchy() + " other: " + colliderOtherIn.gameObject.NameHierarchy());
// enumEnterExit = EnumEnterExit.Exit;
// OnEnterExit(colliderOtherIn);
//}
private void OnTriggerCollisionXXX(EnumTriggerCollision enumTriggerCollisionIn, EnumEnterExit enumEnterExitIn, Collider colliderOtherIn, Collision collisionCBNIn) //Collider is used for Trigger, and Collision is used for Collision
{
//Debug.Logz("ColliderWatcherHelper.OnTriggerCollisionXXX Self: " + gameObject.NameHierarchy() + " Other: " + colliderOtherIn.gameObject.NameHierarchy() + " " + enumTriggerCollisionIn + " " + enumEnterExit);
//ScriptExecutionOrder.DebugDisplay();
ColliderWatcher colliderWatcherOtherTemp = ErrorCheckAndFindColliderWatcher(colliderOtherIn); //if this fails it spits out errors
if (colliderWatcherOtherTemp != null) //ErrorCheck
{
//ColliderWatcherManager.OnTriggerXXX(EnumEnterExit.Exit, colliderWatcherOtherTemp, colliderOtherIn);
//ColliderWatcherManager.OnTriggerCollisionXXX(enumTriggerCollisionIn, enumEnterExitIn, colliderWatcherOtherTemp, collisionCBNIn);
TriggerCollisionData triggerCollisionData = new TriggerCollisionData(enumTriggerCollisionIn, enumEnterExitIn, colliderWatcherOtherTemp, collisionCBNIn); //one half of the collision (takes 2 to make a pair)
ColliderWatcherManager.OnTriggerCollisionXXX(triggerCollisionData); //send one half of the pair (may be the 1st or 2nd half)
}
}
/*
private void OnEnterExit(Collider colliderOtherIn) //used for both enter/exit for both collision/trigger
{
if (colliderOtherIn != null) //ErrorCheck
{
if (colliderOtherIn.GetComponent<ColliderWatcher>()) //ErrorCheck just to make sure the other collider contains ColliderWatcher (common error if you forget to add ColliderWatcher to every collider in the game)
{
if (colliderA == null)
{
colliderA = colliderOtherIn;
//timeFixedTimeA = Time.fixedTime;
}
else
{
colliderB = colliderOtherIn;
//timeFixedTimeB = Time.fixedTime;
EnterExitPair();
}
}
else
{
Debug.Logz("ERROR: " + colliderOtherIn.gameObject.NameHierarchy() + " does not contain ColliderWatcher");
}
}
else
{
Debug.Logz("ERROR: colliderOtherIn == null");
}
}
*/
/*
private static void EnterExitPair() //called OnTriggerEnter/OnCollisionEnter
{
//Debug.Logz("Pair " + enumEnterExit + " A:" + colliderA.gameObject.NameHierarchy() + " B:" + colliderB.gameObject.NameHierarchy());
if (colliderA != null && colliderB != null) //ErrorCheck
{
ColliderWatcher colliderWatcherA = colliderA.GetComponent<ColliderWatcher>();
ColliderWatcher colliderWatcherB = colliderB.GetComponent<ColliderWatcher>();
if (colliderWatcherA != null && colliderWatcherB != null) //ErrorCheck
{
bool enabledBeforeA = colliderWatcherA.enabled;
bool enabledBeforeB = colliderWatcherB.enabled;
colliderWatcherA.AddRemoveColliderOther(colliderB, enumEnterExit);
colliderWatcherB.AddRemoveColliderOther(colliderA, enumEnterExit);
if(colliderWatcherA.enabled != enabledBeforeA) //ErrorCheck, cannot change collider.enabled status during OnCollisionEnter/Exit
{
Debug.Logz("ERROR: During OnCollisionEnter/Exit the ColliderWatcher must not be disabled/eanbled during the execution of the pair");
}
if (colliderWatcherB.enabled != enabledBeforeB) //ErrorCheck, cannot change collider.enabled status during OnCollisionEnter/Exit
{
Debug.Logz("ERROR: During OnCollisionEnter/Exit the ColliderWatcher must not be disabled/eanbled during the execution of the pair");
}
//stackColliderPair.Add(new ColliderPair(colliderA, colliderWatcherA, colliderB, colliderWatcherB, enumEnterExit));
//if (currentlyExecutingStack == false)
//{
// currentlyExecutingStack = true;
// while(stackColliderPair.Count > 0)
// {
// stackColliderPair[0].colliderWatcherA.AddRemoveColliderOther(stackColliderPair[0].colliderB, stackColliderPair[0].enumEnterExit);
// stackColliderPair[0].colliderWatcherB.AddRemoveColliderOther(stackColliderPair[0].colliderA, stackColliderPair[0].enumEnterExit);
// stackColliderPair.RemoveAt(0);
// }
// currentlyExecutingStack = false;
//}
}
else
{
Debug.Logz("ERROR: colliderWatcherA A or B is null");
}
}
else
{
Debug.Logz("ERROR: collider A or B is null");
}
//regardless if there are errors or not, clear the static collider pair
colliderA = null;
colliderB = null;
}
*/
//public enum EnumEnterExit { Enter, Exit}
//public static void ErrorCheckUpdate() //called by GameController
//{
// if(colliderA != null || colliderB != null)
// {
// colliderA = null;
// colliderB = null;
// Debug.Logz("ERROR: collisions should always occur in pairs. during update there should never be a colliderA or B that is not null");
// }
//}
/*
private struct ColliderPair
{
public Collider colliderA { get; private set; }
public ColliderWatcher colliderWatcherA { get; private set; }
public Collider colliderB { get; private set; }
public ColliderWatcher colliderWatcherB { get; private set; }
public EnumEnterExit enumEnterExit { get; private set; }
public ColliderPair(Collider colliderAIn, ColliderWatcher colliderWatcherAIn, Collider colliderBIn, ColliderWatcher colliderWatcherBIn, EnumEnterExit enumEnterExitIn) //constructor
{
colliderA = colliderAIn;
colliderWatcherA = colliderWatcherAIn;
colliderB = colliderBIn;
colliderWatcherB = colliderWatcherBIn;
if(colliderA == null || colliderWatcherA == null || colliderB == null || colliderWatcherB == null) //ErrorCheck
{
Debug.Logz("ERROR: Something null");
}
enumEnterExit = enumEnterExitIn;
}
}
*/
private ColliderWatcher ErrorCheckAndFindColliderWatcher(Collider colliderIn) //return true if error, false if no error
{
if(colliderIn != null)
{
ColliderWatcher returnMe = colliderIn.GetComponent<ColliderWatcher>(); //attempt to get
if (returnMe != null) //contains a colliderWatcher
{
return returnMe;
}
else
{
BatDebug.Error(this, "565uyjhtrgtr3", "returnMe == null: no ColliderWatcher");
return null;
}
}
else
{
BatDebug.Error(this, "35zcxv353543", "colliderIn == null");
return null;
}
}
}
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic; //allow List
public class ColliderWatcherManager : MonoBehaviour
{
private static ColliderWatcherManager singleton;
private static TriggerCollisionData triggerCollisionDataA = null;
private static TriggerCollisionData triggerCollisionDataB = null;
private void Awake()
{
InitializeSingleton();
}
private void InitializeSingleton()
{
if(singleton != null)
{
BatDebug.Error(this, "756avs34aweasf", "singleton set twice");
}
singleton = this;
}
//private void Update() //due to scriptExecutionOrder this will be the FIRST thing that will be run. Essentially the 1st thing ran after Physics performs a stack of collisions.
//{
// ColliderWatcherDisabled.ExecuteStack("ColliderWatcherManager.Update");
//}
public static void OnTriggerCollisionXXX(TriggerCollisionData triggerCollisionDataIn) //for trigger AND collision
{
//Debug.Logz("OnTriggerCollisionXXX: " + triggerCollisionDataIn.enumTriggerCollision + " " + triggerCollisionDataIn.enumEnterExit + " " + triggerCollisionDataIn.colliderWatcherOther.gameObject.NameHierarchy() + " " + triggerCollisionDataIn.collisionCBN);
if (triggerCollisionDataA == null)
{
triggerCollisionDataA = triggerCollisionDataIn;
}
else //A is already assign, so assign B
{
triggerCollisionDataB = triggerCollisionDataIn;
//PLACEHOLDER, errorChecks
TriggerCollisionData.ApplyPair(triggerCollisionDataA, triggerCollisionDataB);
triggerCollisionDataA = null; //clear
triggerCollisionDataB = null; //clear
}
}
/*
public abstract class Stackable// : IComparable<Stackable>
{
private static List<Stackable> listStack = new List<Stackable>();
private static bool isExecutingStack = false;
public Stackable() //constructor
{
listStack.Add(this);
//ExecuteStack();
Debug.Logz("StackCount: Add = " + listStack.Count);
}
private void ExecuteAndRemoveFromStack()
{
Debug.Logz("ExecuteAndRemoveFromStack: " + this.GetType().Name);
listStack.Remove(this);
Debug.Logz("StackCount: Remove = " + listStack.Count);
ExecuteAndRemoveFromStack1();
}
protected abstract void ExecuteAndRemoveFromStack1();
public static void ExecuteStack()
{
if (isExecutingStack == false && listStack.Count > 0)
{
Debug.Logz("ExecuteStack: count = " + listStack.Count);
listStack.Sort();
isExecutingStack = true;
for (int i = 0; i <= 10000; i++) //~while loop, prevent infinite looping
{
if (i == 10000)
{
isExecutingStack = false;
Debug.Logz("ERROR: Infinite Loop");
}
if (listStack.Count > 0)
{
listStack[0].ExecuteAndRemoveFromStack();
}
else
{
isExecutingStack = false;
break;
}
}
}
//else
//{
// Debug.Logz("Currently executing stack, not executing again");
//}
}
//public int CompareTo(Stackable other)
//{
// return other.sortValue - sortValue;
//}
//protected abstract int sortValue
//{
// get;
//}
}
*/
/*
public sealed class TriggerCollisionPair : Stackable
{
private TriggerCollisionData triggerCollisionDataA;
private TriggerCollisionData triggerCollisionDataB;
public TriggerCollisionPair(TriggerCollisionData triggerCollisionDataAIn, TriggerCollisionData triggerCollisionDataBIn) //constructor
{
if(triggerCollisionDataAIn == null || triggerCollisionDataBIn == null)
{
Debug.Logz("ERRORz");
}
triggerCollisionDataA = triggerCollisionDataAIn;
triggerCollisionDataB = triggerCollisionDataBIn;
//ExecuteStack(); //execute AFTER everything is assigned (which is why ExecuteStack cannot be part of parent constructor)
}
protected sealed override void ExecuteAndRemoveFromStack1()
{
if(triggerCollisionDataA == null)
{
Debug.Logz("ERRORz");
}
if(triggerCollisionDataA.colliderWatcher == null)
{
Debug.Logz("ERRORz");
}
triggerCollisionDataA.colliderWatcher.OnTriggerCollisionPairXXX(triggerCollisionDataB);
triggerCollisionDataB.colliderWatcher.OnTriggerCollisionPairXXX(triggerCollisionDataA);
//triggerCollisionDataA.colliderWatcher.AddRemoveColliderOther()
}
protected sealed override int sortValue //trigger/collisions happen first
{
get
{
return 1;
}
}
}
*/
public sealed class ColliderWatcherDisabled// : Stackable
{
private static List<ColliderWatcherDisabled> listStack = new List<ColliderWatcherDisabled>(); //list of all disabled colliders (they are added to stack of they occur during the physics loop, and are execute immediately if they occur at a different time)
private ColliderWatcher colliderWatcher;
private static bool isExecutingStack = false;
public ColliderWatcherDisabled(ColliderWatcher colliderWatcherIn) //constructor
{
//Debug.Logz("New ColliderWatcherDisabled: Loop = " + ScriptExecutionOrder.enumLoop);
colliderWatcher = colliderWatcherIn;
listStack.Add(this);
if (ScriptExecutionOrder.enumLoop != ScriptExecutionOrder.EnumLoop.Physics) //anything other than the physics trigger/collision loop
{
//Debug.Logz("Execute Stack Immediate");
ExecuteStack("ColliderWatcherDisabled Immediate"); //execute stack. if other colliderWatchers are disabled while executing stack then they will also be added to the stack
}
else //was disabled during the trigger/collision loop
{
//Debug.Logz("Execute Stack Delayed");
//Debug.Logz("Postponing execution of disable stack because it occured within the Physics loop");
}
}
private void ExecuteAndRemoveFromStack()
{
listStack.Remove(this);
colliderWatcher.OnDisabledFromStack();
}
public static void ExecuteStack(string callerNameIn)
{
//Debug.Logz("ExecuteStack: currentlyExecuting = " + isExecutingStack + " stackCount=" + listStack.Count);
if (isExecutingStack == false && listStack.Count > 0)
{
//Debug.Logz("ExecuteStack: count = " + listStack.Count + " " + Time.time + " callerNameIn=" + callerNameIn);
//listStack.Sort();
isExecutingStack = true;
for (int i = 0; i <= 10000; i++) //~while loop, prevent infinite looping
{
if (i == 10000)
{
isExecutingStack = false;
BatDebug.Error(typeof(ColliderWatcherManager), "455yjunghr32r4t5h", "Infinite Loop");
}
if (listStack.Count > 0)
{
//Debug.Logz("ExecuteAndRemoveFromStack: " + listStack[0].colliderWatcher.gameObject.NameHierarchy());
listStack[0].ExecuteAndRemoveFromStack();
}
else
{
isExecutingStack = false;
break;
}
}
}
}
}
}