namespace Zinnia.Tracking.Collision { using System.Collections.Generic; using UnityEngine; using Zinnia.Data.Collection.List; using Zinnia.Extension; /// /// Ignores the collisions between the source colliders and the target colliders. /// public class CollisionIgnorer : MonoBehaviour { [Tooltip("The sources to ignore colliders from.")] [SerializeField] private GameObjectObservableList sources; /// /// The sources to ignore colliders from. /// public GameObjectObservableList Sources { get { return sources; } set { if (this.IsMemberChangeAllowed()) { OnBeforeSourcesChange(); } sources = value; if (this.IsMemberChangeAllowed()) { OnAfterSourcesChange(); } } } [Tooltip("The targets to ignore colliders with.")] [SerializeField] private GameObjectObservableList targets; /// /// The targets to ignore colliders with. /// public GameObjectObservableList Targets { get { return targets; } set { if (this.IsMemberChangeAllowed()) { OnBeforeTargetsChange(); } targets = value; if (this.IsMemberChangeAllowed()) { OnAfterTargetsChange(); } } } [Tooltip("Whether to process inactive GameObjects when ignoring or resuming collisions.")] [SerializeField] private bool processInactiveGameObjects; /// /// Whether to process inactive s when ignoring or resuming collisions. /// public bool ProcessInactiveGameObjects { get { return processInactiveGameObjects; } set { processInactiveGameObjects = value; } } /// /// A reused instance to store the collection belonging to the . /// protected List sourceColliders = new List(); /// /// A reused instance to store the collection belonging to the . /// protected List targetColliders = new List(); protected virtual void OnEnable() { RegisterSourceListeners(); RegisterTargetListeners(); ToggleCollisions(true); } protected virtual void OnDisable() { UnregisterSourceListeners(); UnregisterTargetListeners(); ToggleCollisions(false); } /// /// Registers the listeners for elements that are added or removed from . /// protected virtual void RegisterSourceListeners() { if (Sources == null) { return; } Sources.Added.AddListener(OnSourceAdded); Sources.Removed.AddListener(OnSourceRemoved); } /// /// Unregisters the listeners for elements that are added or removed from . /// protected virtual void UnregisterSourceListeners() { if (Sources == null) { return; } Sources.Added.RemoveListener(OnSourceAdded); Sources.Removed.RemoveListener(OnSourceRemoved); } /// /// Registers the listeners for elements that are added or removed from . /// protected virtual void RegisterTargetListeners() { if (Targets == null) { return; } Targets.Added.AddListener(OnTargetAdded); Targets.Removed.AddListener(OnTargetRemoved); } /// /// Unregisters the listeners for elements that are added or removed from . /// protected virtual void UnregisterTargetListeners() { if (Targets == null) { return; } Targets.Added.RemoveListener(OnTargetAdded); Targets.Removed.RemoveListener(OnTargetRemoved); } /// /// Responds to a being added to and ignores all collisions against . /// /// The source to ignore collisions from. protected virtual void OnSourceAdded(GameObject source) { ToggleCollisions(source, Sources, Targets, true); } /// /// Responds to a being removed from and resumes all collisions against . /// /// The source to restore collisions with. protected virtual void OnSourceRemoved(GameObject source) { ToggleCollisions(source, Sources, Targets, false); } /// /// Responds to a being added to and ignores all collisions against . /// /// The target to ignore collisions on. protected virtual void OnTargetAdded(GameObject target) { ToggleCollisions(target, Targets, Sources, true); } /// /// Responds to a being removed from and resumes all collisions against . /// /// The target to restore collisions on. protected virtual void OnTargetRemoved(GameObject target) { ToggleCollisions(target, Targets, Sources, false); } /// /// Sets the collision state between and . /// /// Whether to ignore collisions or not. protected virtual void ToggleCollisions(bool state) { foreach (GameObject source in Sources.SubscribableElements) { ToggleCollisions(source, Sources, Targets, state); } } /// /// Sets the collision state between the source and targets. /// /// The source to set the collision state on. /// A collection of sources to check if the given belongs to. /// A collection of targets to set the collision state on. /// Whether to ignore collisions or not. protected virtual void ToggleCollisions(GameObject source, GameObjectObservableList sources, GameObjectObservableList targets, bool state) { if (source == null || (!state && this.CheckIsActiveAndEnabled() && sources.Contains(source))) { return; } source.GetComponentsInChildren(ProcessInactiveGameObjects, sourceColliders); foreach (GameObject target in targets.SubscribableElements) { if (target == null) { continue; } target.GetComponentsInChildren(ProcessInactiveGameObjects, targetColliders); foreach (Collider sourceCollider in sourceColliders) { foreach (Collider targetCollider in targetColliders) { Physics.IgnoreCollision(sourceCollider, targetCollider, state); } } } } /// /// Called before has been changed. /// protected virtual void OnBeforeSourcesChange() { if (Sources != null) { UnregisterSourceListeners(); ToggleCollisions(false); } } /// /// Called after has been changed. /// protected virtual void OnAfterSourcesChange() { if (Sources != null) { RegisterSourceListeners(); ToggleCollisions(true); } } /// /// Called before has been changed. /// protected virtual void OnBeforeTargetsChange() { if (Targets != null) { UnregisterTargetListeners(); ToggleCollisions(false); } } /// /// Called after has been changed. /// protected virtual void OnAfterTargetsChange() { if (Targets != null) { RegisterTargetListeners(); ToggleCollisions(true); } } } }