using System.Collections; using System.Collections.Generic; using UnityEngine; namespace BNG { /// /// Keep track of all grabbables within this trigger /// public class GrabbablesInTrigger : MonoBehaviour { /// /// All grabbables in trigger that are considered valid /// public Dictionary NearbyGrabbables; /// /// All nearby Grabbables that are considered valid. I.e. Not being held, within range, etc. /// public Dictionary ValidGrabbables; /// /// The closest valid grabbable. If grab button is pressed this is the object that will be grabbed. /// public Grabbable ClosestGrabbable; /// /// All grabbables in trigger that are considered valid /// public Dictionary ValidRemoteGrabbables; /// /// Closest Valid Remote Grabbable may be highlighted /// public Grabbable ClosestRemoteGrabbable; /// /// Should we call events on grabbables /// public bool FireGrabbableEvents = true; // Cache these variables for GC private Grabbable _closest; private float _lastDistance; private float _thisDistance; private Dictionary _valids; private Dictionary _filtered; void Start() { NearbyGrabbables = new Dictionary(); ValidGrabbables = new Dictionary(); ValidRemoteGrabbables = new Dictionary(); } void Update() { // Sort Grabbales by Distance so we can use that information later if we need it updateClosestGrabbable(); updateClosestRemoteGrabbables(); } void updateClosestGrabbable() { // Remove any Grabbables that may have been destroyed, deactivated, etc. NearbyGrabbables = SanitizeGrabbables(NearbyGrabbables); // Find any grabbables that can potentially be picked up ValidGrabbables = GetValidGrabbables(NearbyGrabbables); // Assign closest grabbable ClosestGrabbable = GetClosestGrabbable(ValidGrabbables); } void updateClosestRemoteGrabbables() { // Assign closest remote grabbable ClosestRemoteGrabbable = GetClosestGrabbable(ValidRemoteGrabbables, true); // We can't have a closest remote grabbable if we are over a grabbable. // The closestGrabbable always takes precedent of the closestRemoteGrabbable if (ClosestGrabbable != null) { ClosestRemoteGrabbable = null; } } public virtual Grabbable GetClosestGrabbable(Dictionary grabbables, bool remoteOnly = false) { _closest = null; _lastDistance = 9999f; if(grabbables == null) { return null; } foreach (var kvp in grabbables) { if (kvp.Value == null || !kvp.Value.IsGrabbable()) { continue; } // Use Collider transform as position _thisDistance = Vector3.Distance(kvp.Value.transform.position, transform.position); if (_thisDistance < _lastDistance && kvp.Value.isActiveAndEnabled) { // Not a valid option if (remoteOnly && !kvp.Value.RemoteGrabbable) { continue; } // Not within remote grab range if (remoteOnly && _thisDistance > kvp.Value.RemoteGrabDistance) { continue; } // This is now our closest grabbable _lastDistance = _thisDistance; _closest = kvp.Value; } } return _closest; } public Dictionary GetValidGrabbables(Dictionary grabs) { _valids = new Dictionary(); if (grabs == null) { return _valids; } // Check for objects that need to be removed from RemoteGrabbables foreach (var kvp in grabs) { if (isValidGrabbable(kvp.Key, kvp.Value) && !_valids.ContainsKey(kvp.Key)) { _valids.Add(kvp.Key, kvp.Value); } } return _valids; } protected virtual bool isValidGrabbable(Collider col, Grabbable grab) { // Object has been deactivated. Remove it if (col == null || grab == null || !grab.isActiveAndEnabled || !col.enabled) { return false; } // Not considered grabbable any longer. May have been picked up, marked, etc. else if (!grab.IsGrabbable()) { return false; } // Snap Zone without an item isn't a valid grab. Want to skip this unless something is inside else if(grab.GetComponent() != null && grab.GetComponent().HeldItem == null) { return false; } // Position was manually set outside of break distance // No longer possible for it to be the closestGrabbable else if (grab == ClosestGrabbable) { if (grab.BreakDistance > 0 && Vector3.Distance(grab.transform.position, transform.position) > grab.BreakDistance) { return false; } } return true; } public virtual Dictionary SanitizeGrabbables(Dictionary grabs) { _filtered = new Dictionary(); if (grabs == null) { return _filtered; } foreach (var g in grabs) { if (g.Key != null && g.Key.enabled && g.Value.isActiveAndEnabled) { // If outside of distance then this collider may have been disabled / re-enabled. Scrub from Nearby if (g.Value.BreakDistance > 0 && Vector3.Distance(g.Key.transform.position, transform.position) > g.Value.BreakDistance) { continue; } // Collision check via raycast _filtered.Add(g.Key, g.Value); } } return _filtered; } public virtual void AddNearbyGrabbable(Collider col, Grabbable grabObject) { if(NearbyGrabbables == null) { NearbyGrabbables = new Dictionary(); } if (grabObject != null && !NearbyGrabbables.ContainsKey(col)) { NearbyGrabbables.Add(col, grabObject); } } public virtual void RemoveNearbyGrabbable(Collider col, Grabbable grabObject) { if (grabObject != null && NearbyGrabbables != null && NearbyGrabbables.ContainsKey(col)) { NearbyGrabbables.Remove(col); } } public virtual void RemoveNearbyGrabbable(Grabbable grabObject) { if (grabObject != null) { foreach (var x in NearbyGrabbables) { if (x.Value == grabObject) { NearbyGrabbables.Remove(x.Key); break; } } } } public virtual void AddValidRemoteGrabbable(Collider col, Grabbable grabObject) { // Sanity check if(col == null || grabObject == null) { return; } // Ensure our collection has been initialized if (ValidRemoteGrabbables == null) { ValidRemoteGrabbables = new Dictionary(); } try { if (grabObject != null && grabObject.RemoteGrabbable && col != null && !ValidRemoteGrabbables.ContainsKey(col)) { ValidRemoteGrabbables.Add(col, grabObject); } } catch(System.Exception e) { Debug.Log("Could not add Collider " + col.transform.name + " " + e.Message); } } public virtual void RemoveValidRemoteGrabbable(Collider col, Grabbable grabObject) { if (grabObject != null && ValidRemoteGrabbables != null && ValidRemoteGrabbables.ContainsKey(col)) { ValidRemoteGrabbables.Remove(col); } } void OnTriggerEnter(Collider other) { // Check for standard Grabbables first Grabbable g = other.GetComponent(); if (g != null) { AddNearbyGrabbable(other, g); return; } // Check for Child Grabbables that reference a parent GrabbableChild gc = other.GetComponent(); if (gc != null && gc.ParentGrabbable != null) { AddNearbyGrabbable(other, gc.ParentGrabbable); return; } } void OnTriggerExit(Collider other) { Grabbable g = other.GetComponent(); if (g != null) { RemoveNearbyGrabbable(other, g); return; } GrabbableChild gc = other.GetComponent(); if (gc != null) { RemoveNearbyGrabbable(other, gc.ParentGrabbable); return; } } } }