using System.Collections.Generic; using UnityEngine; namespace Lightbug.Utilities { /// /// An implementation of a PhysicsComponent for 2D physics. /// public sealed class PhysicsComponent2D : PhysicsComponent { /// /// Gets the Rigidbody associated with this object. /// public Rigidbody2D Rigidbody { get; private set; } = null; /// /// Gets an array with all the colliders associated with this object. /// //public Collider2D[] Colliders { get; private set; } = null; public Collider2D Collider { get; private set; } = null; readonly ContactPoint2D[] _contactsBuffer = new ContactPoint2D[10]; readonly RaycastHit2D[] _hitsBuffer = new RaycastHit2D[10]; readonly Collider2D[] _overlapsBuffer = new Collider2D[10]; ContactFilter2D _contactFilter; protected override void Awake() { base.Awake(); Collider = GetComponent(); _contactFilter = (new ContactFilter2D()).NoFilter(); _contactFilter.useLayerMask = true; } protected override void Start() { base.Start(); Rigidbody = GetComponent(); } void OnTriggerStay2D(Collider2D other) { if (ignoreCollisionMessages) return; if (!other.isTrigger) return; bool found = false; float fixedTime = Time.fixedTime; // Check if the trigger is contained inside the list for (int i = 0; i < Triggers.Count && !found; i++) { if (Triggers[i] != other) continue; found = true; var trigger = Triggers[i]; trigger.Update(fixedTime); Triggers[i] = trigger; } if (!found) Triggers.Add(new Trigger(other, Time.fixedTime)); } void OnTriggerExit2D(Collider2D other) { if (ignoreCollisionMessages) return; for (int i = Triggers.Count - 1; i >= 0; i--) { if (Triggers[i].collider2D == other) { Triggers.RemoveAt(i); break; } } } void OnCollisionEnter2D(Collision2D collision) { if (ignoreCollisionMessages) return; int bufferHits = collision.GetContacts(_contactsBuffer); // Add the contacts to the list for (int i = 0; i < bufferHits; i++) { ContactPoint2D contact = _contactsBuffer[i]; Contact outputContact = new Contact(true, contact, collision); Contacts.Add(outputContact); } } void OnCollisionStay2D(Collision2D collision) { if (ignoreCollisionMessages) return; int bufferHits = collision.GetContacts(_contactsBuffer); // Add the contacts to the list for (int i = 0; i < bufferHits; i++) { ContactPoint2D contact = _contactsBuffer[i]; Contact outputContact = new Contact(false, contact, collision); Contacts.Add(outputContact); } } protected override LayerMask GetCollisionLayerMask() { int objectLayer = gameObject.layer; LayerMask output = 0; for (int i = 0; i < 32; i++) { bool exist = !Physics2D.GetIgnoreLayerCollision(i, objectLayer); if (exist) output.value |= 1 << i; } return output; } public override void IgnoreCollision(in HitInfo hitInfo, bool ignore) { if (hitInfo.collider2D == null) return; Physics2D.IgnoreCollision(Collider, hitInfo.collider2D, ignore); } public override void IgnoreCollision(Transform otherTransform, bool ignore) { if (otherTransform == null) return; if (!otherTransform.TryGetComponent(out Collider2D collider)) return; Physics2D.IgnoreCollision(Collider, collider, ignore); } public void IgnoreCollision(Collider2D collider, bool ignore) { if (collider == null) return; Physics2D.IgnoreCollision(Collider, collider, ignore); } [System.Obsolete] public void IgnoreCollision(Collider2D collider, bool ignore, int layerMask) { if (collider == null) return; if (!CustomUtilities.BelongsToLayerMask(collider.gameObject.layer, layerMask)) return; Physics2D.IgnoreCollision(Collider, collider, ignore); } public override void IgnoreLayerCollision(int targetLayer, bool ignore) { Physics2D.IgnoreLayerCollision(gameObject.layer, targetLayer, ignore); if (ignore) CollisionLayerMask &= ~(1 << targetLayer); else CollisionLayerMask |= (1 << targetLayer); } public override void IgnoreLayerMaskCollision(LayerMask layerMask, bool ignore) { int layerMaskValue = layerMask.value; int currentLayer = 1; for (int i = 0; i < 32; i++) { bool exist = (layerMaskValue & currentLayer) > 0; if (exist) IgnoreLayerCollision(i, ignore); currentLayer <<= 1; } if (ignore) CollisionLayerMask &= ~(layerMask.value); else CollisionLayerMask |= (layerMask.value); } #region Physics queries protected override int InternalRaycast(Vector3 origin, Vector3 castDisplacement, int layerMask, bool ignoreTriggers, bool allowOverlaps) { var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; var hits = Physics2D.RaycastNonAlloc( origin, Vector3.Normalize(castDisplacement), _hitsBuffer, castDisplacement.magnitude, layerMask ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return hits; } protected override int InternalSphereCast(Vector3 center, float radius, Vector3 castDisplacement, int layerMask, bool ignoreTriggers, bool allowOverlaps) { var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; _contactFilter.layerMask = layerMask; var hits = Physics2D.CircleCast( center, radius, Vector3.Normalize(castDisplacement), _contactFilter, _hitsBuffer, castDisplacement.magnitude ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return hits; } protected override int InternalCapsuleCast(Vector3 bottom, Vector3 top, float radius, Vector3 castDisplacement, int layerMask, bool ignoreTriggers, bool allowOverlaps) { Vector3 bottomToTop = top - bottom; Vector3 center = bottom + CustomUtilities.Multiply(bottomToTop, 0.5f); Vector2 size; size.x = 2f * radius; size.y = bottomToTop.magnitude + size.x; float castAngle = Vector2.SignedAngle(bottomToTop, Vector2.up); var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; _contactFilter.layerMask = layerMask; var hits = Physics2D.CapsuleCast( center, size, CapsuleDirection2D.Vertical, castAngle, Vector3.Normalize(castDisplacement), _contactFilter, _hitsBuffer, castDisplacement.magnitude ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return hits; } protected override int InternalBoxCast(Vector3 center, Vector3 size, Vector3 castDisplacement, Quaternion orientation, int layerMask, bool ignoreTriggers, bool allowOverlaps) { var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; _contactFilter.layerMask = layerMask; var hits = Physics2D.BoxCast( center, size, orientation.eulerAngles.z, Vector3.Normalize(castDisplacement), _contactFilter, _hitsBuffer, castDisplacement.magnitude ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return hits; } #endregion #region Overlaps protected override int InternalOverlapSphere(Vector3 center, float radius, int layerMask, bool ignoreTriggers) { var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; _contactFilter.layerMask = layerMask; var overlaps = Physics2D.OverlapCircle( center, radius, _contactFilter, _overlapsBuffer ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return overlaps; } protected override int InternalOverlapCapsule(Vector3 bottom, Vector3 top, float radius, int layerMask, bool ignoreTriggers) { Vector3 bottomToTop = top - bottom; Vector3 center = bottom + 0.5f * bottomToTop; Vector2 size = new Vector2(2f * radius, bottomToTop.magnitude + 2f * radius); float castAngle = Vector2.SignedAngle(bottomToTop, Vector2.up); var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; _contactFilter.layerMask = layerMask; var overlaps = Physics2D.OverlapCapsule( center, size, CapsuleDirection2D.Vertical, castAngle, _contactFilter, _overlapsBuffer ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return overlaps; } protected override int InternalOverlapBox(Vector3 center, Vector3 size, Quaternion orientation, int layerMask, bool ignoreTriggers) { var qht = Physics2D.queriesHitTriggers; var qsic = Physics2D.queriesStartInColliders; Physics2D.queriesHitTriggers = !ignoreTriggers; Physics2D.queriesStartInColliders = true; var angle = orientation.eulerAngles.z; _contactFilter.layerMask = layerMask; var overlaps = Physics2D.OverlapBox( center, size, angle, _contactFilter, _overlapsBuffer ); Physics2D.queriesHitTriggers = qht; Physics2D.queriesStartInColliders = qsic; return overlaps; } #endregion protected override int FilterOverlaps(int overlaps, LayerMask ignoredLayerMask, HitFilterDelegate hitFilter) { int filteredOverlaps = overlaps; for (int i = 0; i < overlaps; i++) { var hitCollider = _overlapsBuffer[i]; if (hitCollider.transform.IsChildOf(transform)) { filteredOverlaps--; continue; } if (hitFilter != null) { if (!hitFilter.Invoke(hitCollider.transform)) { filteredOverlaps--; continue; } } // Tell the physics engine to ignore this collider if it belongs to the "ignoredLayerMask" mask. if (CustomUtilities.BelongsToLayerMask(hitCollider.gameObject.layer, ignoredLayerMask)) IgnoreCollision(hitCollider, true); } return filteredOverlaps; } protected override void GetClosestHit(out HitInfo hitInfo, int hits, Vector3 castDisplacement, in HitInfoFilter filter, bool allowOverlaps, HitFilterDelegate hitFilter) { var closestRaycastHit = new RaycastHit2D(); closestRaycastHit.distance = Mathf.Infinity; var hit = false; HitsBuffer.Clear(); for (int i = 0; i < hits; i++) { var raycastHit = _hitsBuffer[i]; if (!PerformHitCheck(ref raycastHit, in filter, allowOverlaps, hitFilter)) continue; hit = true; var thisHitInfo = new HitInfo(ref raycastHit, Vector3.Normalize(castDisplacement)); HitsBuffer.Add(thisHitInfo); if (raycastHit.distance < closestRaycastHit.distance) { closestRaycastHit = raycastHit; } } if (hit) hitInfo = new HitInfo(ref closestRaycastHit, Vector3.Normalize(castDisplacement)); else hitInfo = new HitInfo(); } protected override List GetAllHits(int hits, Vector3 castDisplacement, in HitInfoFilter filter, bool allowOverlaps, HitFilterDelegate hitFilter) { HitsBuffer.Clear(); for (int i = 0; i < hits; i++) { var raycastHit = _hitsBuffer[i]; if (!PerformHitCheck(ref raycastHit, in filter, allowOverlaps, hitFilter)) continue; var thisHitInfo = new HitInfo(ref raycastHit, Vector3.Normalize(castDisplacement)); HitsBuffer.Add(thisHitInfo); } return HitsBuffer; } bool PerformHitCheck(ref RaycastHit2D raycastHit, in HitInfoFilter filter, bool allowOverlaps, HitFilterDelegate hitFilter) { var raycastHitDistance = raycastHit.distance; var raycastHitCollider = raycastHit.collider; if (raycastHitCollider.transform.IsChildOf(transform)) return false; if (hitFilter != null) if (!hitFilter.Invoke(raycastHitCollider.transform)) return false; if (!allowOverlaps && raycastHitDistance == 0) return false; if (raycastHitDistance < filter.minimumDistance || raycastHitDistance > filter.maximumDistance) return false; if (filter.ignoreRigidbodies && raycastHitCollider.attachedRigidbody != null) return false; return true; } public override bool CheckCollisionsWith(GameObject gameObject) { if (!gameObject.TryGetComponent(out Collider2D collider)) return false; return !Physics2D.GetIgnoreCollision(Collider, collider); } } }