using System; using System.Collections; using ModV1.Source; using Pathfinding; using UnityEngine; // KevinJ: Game specific faster version // There are thousands of structural objects, it's not worthwhile to call Update() on all of them considering they don't move most of the time // Disable Update() unless taking damage // This is per-collider public class DynamicGridObstacle2 : GraphModifier { Rigidbody rb; DamageUnityEvents damageUnitEvents; /// Collider to get bounds information from Collider coll; /// 2D Collider to get bounds information from Collider2D coll2D; /// Cached transform component Transform tr; /// The minimum change in world units along one of the axis of the bounding box of the collider to trigger a graph update public float updateErrorPosition = 1; // Rotational error, in degrees, to trigger a graph update //public float updateErrorRotation = 30; /// /// Time in seconds between bounding box checks. /// If AstarPath.batchGraphUpdates is enabled, it is not beneficial to have a checkTime much lower /// than AstarPath.graphUpdateBatchingInterval because that will just add extra unnecessary graph updates. /// /// In real time seconds (based on Time.realtimeSinceStartup). /// //public float checkTime = 0.2F; /// Bounds of the collider the last time the graphs were updated Bounds prevBounds; /// Rotation of the collider the last time the graphs were updated //Quaternion prevRotation; /// True if the collider was enabled last time the graphs were updated //bool prevEnabled; //float lastCheckTime = -9999; Bounds bounds { get { if (coll != null) { return coll.bounds; } else { var b = coll2D.bounds; // Make sure the bounding box stretches close to infinitely along the Z axis (which is the axis perpendicular to the 2D plane). // We don't want any change along the Z axis to make a difference. b.extents += new Vector3(0, 0, 10000); return b; } } } bool colliderEnabled { get { return coll != null ? coll.enabled : coll2D.enabled; } } protected override void Awake() { base.Awake(); tr = transform; //prevRotation = tr.rotation; /* // Make sure we update the graph as soon as we find that the collider is enabled // prevEnabled = false; if (AStarPathValid()) enabled = true; else enabled = false; */ Setup(); //DoUpdateGraphs2(); } // KevinJ: Move GetCollider to Start() protected virtual void Start() { rb = GetComponentInParent(); damageUnitEvents = GetComponentInParent(); if (damageUnitEvents == null) { enabled = false; return; } damageUnitEvents.onMajorHealthDamage.AddListener(OnDamage); damageUnitEvents.onMinorHealthDamage.AddListener(OnDamage); // DoUpdateGraphs2(); //DoUpdateGraphs(); } protected override void OnEnable() { base.OnEnable(); Setup(); DoUpdateGraphs(); } /// /// Revert graphs when disabled. /// When the DynamicObstacle is disabled or destroyed, a last graph update should be done to revert nodes to their original state /// protected override void OnDisable() { base.OnDisable(); //OnDisableOrDestroy(); DoUpdateGraphs(); } // KevinJ: protected override void OnDestroy() { base.OnDestroy(); // if (enabled == false) // OnDisableOrDestroy(); } // KevinJ: // protected void OnDisableOrDestroy() // { // DoUpdateGraphs2(); // } private void OnDamage(GameObject arg0, Vector3 arg1, Vector3 arg2) { // if (AStarPathValid()) // enabled = true; //DoUpdateGraphs2(); if (gameObject.activeInHierarchy) StartCoroutine(DoUpgradeGraphsWhileMoving()); } private void OnCollisionEnter(Collision collision) { // if (AStarPathValid()) // enabled = true; //DoUpdateGraphs2(); if (gameObject.activeInHierarchy) StartCoroutine(DoUpgradeGraphsWhileMoving()); } // KevinJ: Move GetCollider to Start() protected void Setup() { coll = GetComponent(); coll2D = GetComponent(); if (coll == null && coll2D == null) { return; } prevBounds = bounds; } public override void OnPostScan() { if (coll == null && coll2D == null) return; // In case the object was in the scene from the start and the graphs // were scanned then we ignore the first update since it is unnecessary. //prevEnabled = colliderEnabled; // if (colliderEnabled) // enabled = false; prevBounds = bounds; } bool AStarPathValid() { if (AstarPath.active == null || AstarPath.active.gameObject.activeInHierarchy == false || AstarPath.active.isScanning || AstarPath.active.graphs.Length == 0 || AstarPath.active.graphs[0].active == false || //Time.realtimeSinceStartup - lastCheckTime < checkTime || !Application.isPlaying) return false; return true; } /* protected virtual void Update() { if (coll == null && coll2D == null) { enabled = false; return; } if (rb != null && (rb.isKinematic || rb.IsSleeping())) { // There are thousands of structural objects, it's not worthwhile to call Update() on all of them considering they don't move most of the time // Not calling Update() is important even if the result is wrong and valid paths are blocked enabled = false; return; } if (AStarPathValid() == false) { return; } if (transform.position.y < 0.0f) { enabled = false; return; } lastCheckTime = Time.realtimeSinceStartup; if (colliderEnabled) { // The current bounds of the collider Bounds newBounds = bounds; var newRotation = tr.rotation; Vector3 minDiff = prevBounds.min - newBounds.min; Vector3 maxDiff = prevBounds.max - newBounds.max; var extents = newBounds.extents.magnitude; // This is the distance that a point furthest out on the bounding box // would have moved due to the changed rotation of the object //var errorFromRotation = extents * Quaternion.Angle(prevRotation, newRotation); // * Mathf.Deg2Rad; // if (!prevEnabled) // { // DoUpdateGraphs(); // enabled = false; // } // If the difference between the previous bounds and the new bounds is greater than some value, update the graphs //else if (minDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition || maxDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition) { // Update the graphs as soon as possible DoUpdateGraphs(); //enabled = false; } } // else // { // // Collider has just been disabled // if (prevEnabled) // { // DoUpdateGraphs(); // } // enabled = false; // } } */ /* void DoUpdateGraphs2() { if (AStarPathValid()) { //lastCheckTime = Time.realtimeSinceStartup; if (colliderEnabled) { // The current bounds of the collider Bounds newBounds = bounds; var newRotation = tr.rotation; Vector3 minDiff = prevBounds.min - newBounds.min; Vector3 maxDiff = prevBounds.max - newBounds.max; //var extents = newBounds.extents.magnitude; // This is the distance that a point furthest out on the bounding box // would have moved due to the changed rotation of the object //var errorFromRotation = extents * Quaternion.Angle(prevRotation, newRotation); // * Mathf.Deg2Rad; // if (!prevEnabled) // { // DoUpdateGraphs(); // enabled = false; // } // If the difference between the previous bounds and the new bounds is greater than some value, update the graphs //else if (minDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition || maxDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition) { // Update the graphs as soon as possible DoUpdateGraphs(); //enabled = false; } } else { DoUpdateGraphs(); } } } */ public IEnumerator DoUpgradeGraphsWhileMoving() { while (enabled && colliderEnabled && rb.isKinematic == false && rb.IsSleeping() == false) { /* // The current bounds of the collider Bounds newBounds = bounds; var newRotation = tr.rotation; Vector3 minDiff = prevBounds.min - newBounds.min; Vector3 maxDiff = prevBounds.max - newBounds.max; //var extents = newBounds.extents.magnitude; // This is the distance that a point furthest out on the bounding box // would have moved due to the changed rotation of the object //var errorFromRotation = extents * Quaternion.Angle(prevRotation, newRotation); // * Mathf.Deg2Rad; // if (!prevEnabled) // { // DoUpdateGraphs(); // enabled = false; // } // If the difference between the previous bounds and the new bounds is greater than some value, update the graphs //else if (minDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition || maxDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition) { // Update the graphs as soon as possible DoUpdateGraphs(); //enabled = false; } */ yield return null; } // The current bounds of the collider Bounds newBounds = bounds; var newRotation = tr.rotation; Vector3 minDiff = prevBounds.min - newBounds.min; Vector3 maxDiff = prevBounds.max - newBounds.max; if (minDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition || maxDiff.sqrMagnitude > updateErrorPosition * updateErrorPosition) DoUpdateGraphs(); } /// /// Update the graphs around this object. /// Note: The graphs will not be updated immediately since the pathfinding threads need to be paused first. /// If you want to guarantee that the graphs have been updated then call AstarPath.active.FlushGraphUpdates() /// after the call to this method. /// public virtual void DoUpdateGraphs() { if (AStarPathValid() == false) return; if (coll == null && coll2D == null) return; if (!colliderEnabled) { // If the collider is not enabled, then col.bounds will empty // so just update prevBounds AstarPath.active.UpdateGraphs(prevBounds); } else { Bounds newBounds = bounds; Bounds merged = newBounds; merged.Encapsulate(prevBounds); // Check what seems to be fastest, to update the union of prevBounds and newBounds in a single request // or to update them separately, the smallest volume is usually the fastest if (BoundsVolume(merged) < BoundsVolume(newBounds) + BoundsVolume(prevBounds)) { // Send an update request to update the nodes inside the 'merged' volume AstarPath.active.UpdateGraphs(merged); } else { // Send two update request to update the nodes inside the 'prevBounds' and 'newBounds' volumes AstarPath.active.UpdateGraphs(prevBounds); if (newBounds.extents != Vector3.zero) AstarPath.active.UpdateGraphs(newBounds); } #if ASTARDEBUG Debug.DrawLine(prevBounds.min, prevBounds.max, Color.yellow); Debug.DrawLine(newBounds.min, newBounds.max, Color.red); #endif prevBounds = newBounds; } //prevEnabled = colliderEnabled; //prevRotation = tr.rotation; // Set this here as well since the DoUpdateGraphs method can be called from other scripts //lastCheckTime = Time.realtimeSinceStartup; } /// Volume of a Bounds object. X*Y*Z static float BoundsVolume(Bounds b) { return System.Math.Abs(b.size.x * b.size.y * b.size.z); } }