summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs
diff options
context:
space:
mode:
authorchai <215380520@qq.com>2024-05-23 10:08:29 +0800
committerchai <215380520@qq.com>2024-05-23 10:08:29 +0800
commit8722a9920c1f6119bf6e769cba270e63097f8e25 (patch)
tree2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs
parent3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff)
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs252
1 files changed, 252 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs
new file mode 100644
index 0000000..b94de4f
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Misc/GraphModifier.cs
@@ -0,0 +1,252 @@
+using UnityEngine;
+using System.Collections.Generic;
+using Pathfinding.Util;
+
+namespace Pathfinding {
+ /// <summary>
+ /// GraphModifier is used for modifying graphs or processing graph data based on events.
+ /// This class is a simple container for a number of events.
+ ///
+ /// \borderlessimage{graph_events.png}
+ ///
+ /// Warning: Some events will be called both in play mode <b>and in editor mode</b> (at least the scan events).
+ /// So make sure your code handles both cases well. You may choose to ignore editor events.
+ /// See: Application.IsPlaying
+ ///
+ /// Warning: Events may be received before Awake and OnEnable has been called on the component. This is because
+ /// graphs are typically scanned during Awake on the AstarPath component, which may happen before Awake on the graph modifier itself.
+ /// </summary>
+ [ExecuteInEditMode]
+ public abstract class GraphModifier : VersionedMonoBehaviour {
+ /// <summary>All active graph modifiers</summary>
+ private static GraphModifier root;
+
+ private GraphModifier prev;
+ private GraphModifier next;
+
+ /// <summary>Unique persistent ID for this component, used for serialization</summary>
+ [SerializeField]
+ [HideInInspector]
+ protected ulong uniqueID;
+
+ /// <summary>Maps persistent IDs to the component that uses it</summary>
+ protected static Dictionary<ulong, GraphModifier> usedIDs = new Dictionary<ulong, GraphModifier>();
+
+ protected static List<T> GetModifiersOfType<T>() where T : GraphModifier {
+ var current = root;
+ var result = new List<T>();
+
+ while (current != null) {
+ var cast = current as T;
+ if (cast != null) result.Add(cast);
+ current = current.next;
+ }
+ return result;
+ }
+
+ public static void FindAllModifiers () {
+ var allModifiers = UnityCompatibility.FindObjectsByTypeSorted<GraphModifier>();
+
+ for (int i = 0; i < allModifiers.Length; i++) {
+ if (allModifiers[i].enabled) {
+ if (allModifiers[i].next == null) {
+ // The modifier is not yet registered. Presumably it is enabled,
+ // but unity hasn't had time to call OnEnable yet.
+ // Disabling it and enabling it will force unity to call OnEnable immediately.
+ // We don't want to call it ourselves, because then Unity won't know that it has been called,
+ // which could cause issues for lifecycle management.
+ // For example, if we called OnEnable manually (before Unity did), and then the object was destroyed
+ // before Unity had a chance to call OnEnable, then Unity would not call OnDisable.
+ allModifiers[i].enabled = false;
+ allModifiers[i].enabled = true;
+ }
+ }
+ }
+ }
+
+ /// <summary>GraphModifier event type</summary>
+ public enum EventType {
+ PostScan = 1 << 0,
+ PreScan = 1 << 1,
+ LatePostScan = 1 << 2,
+ PreUpdate = 1 << 3,
+ PostUpdate = 1 << 4,
+ PostCacheLoad = 1 << 5,
+ PostUpdateBeforeAreaRecalculation = 1 << 6,
+ PostGraphLoad = 1 << 7,
+ }
+
+ /// <summary>Triggers an event for all active graph modifiers</summary>
+ public static void TriggerEvent (GraphModifier.EventType type) {
+ if (!Application.isPlaying) {
+ FindAllModifiers();
+ }
+
+ try {
+ GraphModifier c = root;
+ switch (type) {
+ case EventType.PreScan:
+ while (c != null) { c.OnPreScan(); c = c.next; }
+ break;
+ case EventType.PostScan:
+ while (c != null) { c.OnPostScan(); c = c.next; }
+ break;
+ case EventType.LatePostScan:
+ while (c != null) { c.OnLatePostScan(); c = c.next; }
+ break;
+ case EventType.PreUpdate:
+ while (c != null) { c.OnGraphsPreUpdate(); c = c.next; }
+ break;
+ case EventType.PostUpdate:
+ while (c != null) { c.OnGraphsPostUpdate(); c = c.next; }
+ break;
+ case EventType.PostUpdateBeforeAreaRecalculation:
+ while (c != null) { c.OnGraphsPostUpdateBeforeAreaRecalculation(); c = c.next; }
+ break;
+ case EventType.PostCacheLoad:
+ while (c != null) { c.OnPostCacheLoad(); c = c.next; }
+ break;
+ case EventType.PostGraphLoad:
+ while (c != null) { c.OnPostGraphLoad(); c = c.next; }
+ break;
+ }
+ } catch (System.Exception e) {
+ Debug.LogException(e);
+ }
+ }
+
+ /// <summary>Adds this modifier to list of active modifiers</summary>
+ protected virtual void OnEnable () {
+ RemoveFromLinkedList();
+ AddToLinkedList();
+ ConfigureUniqueID();
+ }
+
+ /// <summary>Removes this modifier from list of active modifiers</summary>
+ protected virtual void OnDisable () {
+ RemoveFromLinkedList();
+ }
+
+ protected override void Awake () {
+ base.Awake();
+ ConfigureUniqueID();
+ }
+
+ void ConfigureUniqueID () {
+ // Check if any other object is using the same uniqueID
+ // In that case this object may have been duplicated
+ GraphModifier usedBy;
+
+ if (usedIDs.TryGetValue(uniqueID, out usedBy) && usedBy != this) {
+ Reset();
+ }
+
+ usedIDs[uniqueID] = this;
+ }
+
+ void AddToLinkedList () {
+ if (root == null) {
+ root = this;
+ } else {
+ next = root;
+ root.prev = this;
+ root = this;
+ }
+ }
+
+ void RemoveFromLinkedList () {
+ if (root == this) {
+ root = next;
+ if (root != null) root.prev = null;
+ } else {
+ if (prev != null) prev.next = next;
+ if (next != null) next.prev = prev;
+ }
+ prev = null;
+ next = null;
+ }
+
+ protected virtual void OnDestroy () {
+ usedIDs.Remove(uniqueID);
+ }
+
+ /// <summary>
+ /// Called right after all graphs have been scanned.
+ ///
+ /// Note: Area information (see <see cref="Pathfinding.HierarchicalGraph)"/> may not be up to date when this event is sent.
+ /// This means some methods like <see cref="Pathfinding.PathUtilities.IsPathPossible"/> may return incorrect results.
+ /// Use <see cref="OnLatePostScan"/> if you need that info to be up to date.
+ ///
+ /// See: OnLatePostScan
+ /// </summary>
+ public virtual void OnPostScan () {}
+
+ /// <summary>
+ /// Called right before graphs are going to be scanned.
+ ///
+ /// See: OnLatePostScan
+ /// </summary>
+ public virtual void OnPreScan () {}
+
+ /// <summary>
+ /// Called at the end of the scanning procedure.
+ /// This is the absolute last thing done by Scan.
+ /// </summary>
+ public virtual void OnLatePostScan () {}
+
+ /// <summary>
+ /// Called after cached graphs have been loaded.
+ /// When using cached startup, this event is analogous to OnLatePostScan and implementing scripts
+ /// should do roughly the same thing for both events.
+ /// </summary>
+ public virtual void OnPostCacheLoad () {}
+
+ /// <summary>
+ /// Called after a graph has been deserialized and loaded.
+ /// Note: The graph may not have had any valid node data, it might just contain the graph settings.
+ ///
+ /// This will be called often outside of play mode. Make sure to check Application.isPlaying if appropriate.
+ /// </summary>
+ public virtual void OnPostGraphLoad () {}
+
+ /// <summary>Called before graphs are updated using GraphUpdateObjects</summary>
+ public virtual void OnGraphsPreUpdate () {}
+
+ /// <summary>
+ /// Called after graphs have been updated using GraphUpdateObjects or navmesh cutting.
+ ///
+ /// This is among other times called after graphs have been scanned, updated using GraphUpdateObjects, navmesh cuts, or GraphUpdateScene components.
+ ///
+ /// Area recalculations (see <see cref="Pathfinding.HierarchicalGraph"/>) have been done at this stage so things like PathUtilities.IsPathPossible will work.
+ ///
+ /// Use <see cref="OnGraphsPostUpdateBeforeAreaRecalculation"/> instead if you are modifying the graph in any way, especially connections and walkability.
+ /// This is because if you do this then area recalculations
+ /// </summary>
+ public virtual void OnGraphsPostUpdate () {}
+
+ /// <summary>
+ /// Called after graphs have been updated.
+ ///
+ /// This is among other times called after graphs have been scanned, updated using GraphUpdateObjects, navmesh cuts, or GraphUpdateScene components.
+ ///
+ /// Note: Area information (see <see cref="Pathfinding.HierarchicalGraph)"/> may not be up to date when this event is sent.
+ /// This means some methods like <see cref="Pathfinding.PathUtilities.IsPathPossible"/> may return incorrect results.
+ /// Use <see cref="OnLatePostScan"/> if you need that info to be up to date.
+ ///
+ /// Use this if you are modifying any graph connections or walkability.
+ ///
+ /// See: <see cref="OnGraphsPostUpdate"/>
+ /// </summary>
+ public virtual void OnGraphsPostUpdateBeforeAreaRecalculation () {}
+
+ protected override void Reset () {
+ base.Reset();
+ // Create a new random 64 bit value (62 bit actually because we skip negative numbers, but that's still enough by a huge margin)
+ var rnd1 = (ulong)Random.Range(0, int.MaxValue);
+ var rnd2 = ((ulong)Random.Range(0, int.MaxValue) << 32);
+
+ uniqueID = rnd1 | rnd2;
+ usedIDs[uniqueID] = this;
+ }
+ }
+}