using UnityEngine; using System; using System.Collections; using System.Collections.Generic; namespace Obi{ /** * Implements common functionality for ObiCollider and ObiCollider2D. */ public abstract class ObiColliderBase : MonoBehaviour { public static Dictionary idToCollider = new Dictionary(); /**< holds pairs of . Contacts returned by Obi will provide the instance ID of the collider involved in the collision, use it to index this dictionary and find the actual object.*/ [SerializeField][HideInInspector] private ObiCollisionMaterial material; public int phase = 0; public float thickness = 0; public ObiCollisionMaterial CollisionMaterial{ set{ material = value; if (material != null) Oni.SetColliderMaterial(oniCollider,material.OniCollisionMaterial); else Oni.SetColliderMaterial(oniCollider,IntPtr.Zero); } get{return material;} } public IntPtr OniCollider{ get{return oniCollider;} } [HideInInspector][SerializeField] protected Component unityCollider; protected IntPtr oniCollider = IntPtr.Zero; protected ObiRigidbody obiRigidbody; protected int currentLayer = -1; protected bool wasUnityColliderEnabled = true; protected float oldPhase = 0; protected float oldThickness = 0; protected HashSet solvers = new HashSet(); /**< set of solvers where this collider is present.*/ protected ObiShapeTracker tracker; /**< tracker object used to determine when to update the collider's shape*/ protected Oni.Collider adaptor = new Oni.Collider(); /**< adaptor struct used to transfer collider data to the library.*/ /** * Creates an OniColliderTracker of the appropiate type. */ protected abstract void CreateTracker(); protected abstract bool IsUnityColliderEnabled(); protected abstract void UpdateColliderAdaptor(); protected void CreateRigidbody(){ obiRigidbody = null; // find the first rigidbody up our hierarchy: Rigidbody rb = GetComponentInParent(); // if we have an rigidbody above us, see if it has a ObiRigidbody component and add one if it doesn't: if (rb != null){ obiRigidbody = rb.GetComponent(); if (obiRigidbody == null) obiRigidbody = rb.gameObject.AddComponent(); Oni.SetColliderRigidbody(oniCollider,obiRigidbody.OniRigidbody); } else{ Oni.SetColliderRigidbody(oniCollider,IntPtr.Zero); } } /** * Registers this collider in a given solver, if interesed in its layer. */ public void RegisterInSolver(ObiSolver solver, bool addToSolver){ if (!solvers.Contains(solver)){ // if the group's collisionLayers mask includes our layer: if (solver.collisionLayers == (solver.collisionLayers | (1 << gameObject.layer))){ solvers.Add(solver); if (addToSolver) Oni.AddCollider(solver.OniSolver,oniCollider); } } } /** * Removes this collider from a given solver. */ public void RemoveFromSolver(ObiSolver solver){ solvers.Remove(solver); Oni.RemoveCollider(solver.OniSolver,oniCollider); } /** * Registers this collider in all solvers interested in its layer. */ private void FindSolvers(bool addToSolvers){ if (gameObject.layer != currentLayer){ currentLayer = gameObject.layer; // Remove from current solvers: foreach(ObiSolver solver in solvers){ Oni.RemoveCollider(solver.OniSolver,oniCollider); } // Clear current groups list: solvers.Clear(); // Recreate the group list: ObiSolver[] sceneSolvers = GameObject.FindObjectsOfType(); // Find new solvers and add ouselves to them: foreach(ObiSolver solver in sceneSolvers){ RegisterInSolver(solver,addToSolvers); } } } private void Update(){ FindSolvers(true); // in case we have moved to a a different layer. } protected virtual void Awake(){ wasUnityColliderEnabled = IsUnityColliderEnabled(); // register the collider: idToCollider.Add(unityCollider.GetInstanceID(),unityCollider); CreateTracker(); oniCollider = Oni.CreateCollider(); FindSolvers(false); if (tracker != null) Oni.SetColliderShape(oniCollider,tracker.OniShape); // Subscribe collider and rigidbody update callbacks: ObiArbiter.OnFrameStart += UpdateIfNeeded; ObiArbiter.OnFrameEnd += UpdateRigidbody; // Send initial collider data: UpdateColliderAdaptor(); Oni.UpdateCollider(oniCollider,ref adaptor); // Update collider material: if (material != null) Oni.SetColliderMaterial(oniCollider,material.OniCollisionMaterial); else Oni.SetColliderMaterial(oniCollider,IntPtr.Zero); // Create rigidbody if necessary, and link ourselves to it: CreateRigidbody(); } private void OnDestroy() { // Unregister collider: if (unityCollider != null) idToCollider.Remove(unityCollider.GetInstanceID()); // Unsubscribe collider and rigidbody update callbacks: ObiArbiter.OnFrameStart -= UpdateIfNeeded; ObiArbiter.OnFrameEnd -= UpdateRigidbody; // Destroy collider: Oni.DestroyCollider(oniCollider); oniCollider = IntPtr.Zero; // Destroy shape tracker: if (tracker != null){ tracker.Destroy(); tracker = null; } } public void OnEnable(){ // Add collider to current solvers: foreach(ObiSolver solver in solvers) Oni.AddCollider(solver.OniSolver,oniCollider); } public void OnDisable(){ // Remove collider from current solvers: foreach(ObiSolver solver in solvers) Oni.RemoveCollider(solver.OniSolver,oniCollider); } /** * Check if the collider transform or its shape have changed any relevant property, and update their Oni counterparts. */ private void UpdateIfNeeded(object sender, EventArgs e){ if (unityCollider != null){ // update the collider: bool unityColliderEnabled = IsUnityColliderEnabled(); if (unityCollider.transform.hasChanged || phase != oldPhase || thickness != oldThickness || unityColliderEnabled != wasUnityColliderEnabled){ unityCollider.transform.hasChanged = false; oldPhase = phase; oldThickness = thickness; wasUnityColliderEnabled = unityColliderEnabled; // remove the collider from all solver's spatial partitioning grid: foreach(ObiSolver solver in solvers) Oni.RemoveCollider(solver.OniSolver,oniCollider); // update the collider: UpdateColliderAdaptor(); Oni.UpdateCollider(oniCollider,ref adaptor); // re-add the collider to all solver's spatial partitioning grid: if (unityColliderEnabled){ foreach(ObiSolver solver in solvers) Oni.AddCollider(solver.OniSolver,oniCollider); } } } // update the shape: if (tracker != null) tracker.UpdateIfNeeded(); // send rigidbody data (done only once per step, even with multiple colliders for 1 rigidbody): if (obiRigidbody != null) obiRigidbody.UpdateIfNeeded(); } private void UpdateRigidbody(object sender, EventArgs e){ // bring modified velocities back from the solver (done only once per step, even with multiple colliders for 1 rigidbody): if (obiRigidbody != null) obiRigidbody.UpdateVelocities(); } } }