summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/ExampleScripts/Interactable.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/ExampleScenes/ExampleScripts/Interactable.cs
parent3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff)
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/ExampleScripts/Interactable.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/ExampleScripts/Interactable.cs323
1 files changed, 323 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/ExampleScripts/Interactable.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/ExampleScripts/Interactable.cs
new file mode 100644
index 0000000..03697d1
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/ExampleScripts/Interactable.cs
@@ -0,0 +1,323 @@
+using System.Collections;
+using System.Collections.Generic;
+using Pathfinding.ECS;
+using UnityEngine;
+
+namespace Pathfinding.Examples {
+ /// <summary>
+ /// Example script for handling interactable objects in the example scenes.
+ ///
+ /// It implements a very simple and lightweight state machine.
+ ///
+ /// Note: This is an example script intended for the A* Pathfinding Project's example scenes.
+ /// If you need a proper state machine for your game, you may be better served by other state machine solutions on the Unity Asset Store.
+ ///
+ /// It works by keeping a linear list of states, each with an associated action.
+ /// When an agent iteracts with this object, it immediately does the first action in the list.
+ /// Once that action is done, it will do the next action and so on.
+ ///
+ /// Some actions may cancel the whole interaction. For example the MoveTo action will cancel the interaction if the agent
+ /// suddenly had its destination to something else. Presumably because the agent was interrupted by something.
+ ///
+ /// If this component is added to the same GameObject as a <see cref="NodeLink2"/> component, the interactable will automatically trigger when the agent traverses the link.
+ /// Some components behave differently when used during an off-mesh link component.
+ /// For example the <see cref="MoveToAction"/> will move the agent without taking the navmesh into account (becoming a thin wrapper for <see cref="AgentOffMeshLinkTraversalContext.MoveTowards"/>).
+ /// </summary>
+ [HelpURL("https://arongranberg.com/astar/documentation/stable/interactable.html")]
+ public class Interactable : VersionedMonoBehaviour, IOffMeshLinkHandler, IOffMeshLinkStateMachine {
+ public enum CoroutineAction {
+ Tick,
+ Cancel,
+ }
+
+ [System.Serializable]
+ public abstract class InteractableAction {
+ public virtual IEnumerator<CoroutineAction> Execute (IAstarAI ai) {
+ return Execute();
+ }
+
+#if MODULE_ENTITIES
+ public virtual IEnumerator<CoroutineAction> Execute (Pathfinding.ECS.AgentOffMeshLinkTraversalContext context) {
+ return Execute();
+ }
+#endif
+
+ public virtual IEnumerator<CoroutineAction> Execute () {
+ throw new System.NotImplementedException("This action has no implementation");
+ }
+ }
+
+
+ [System.Serializable]
+ public class AnimatorPlay : InteractableAction {
+ public string stateName;
+ public float normalizedTime = 0;
+ public Animator animator;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ animator.Play(stateName, -1, normalizedTime);
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class AnimatorSetBoolAction : InteractableAction {
+ public string propertyName;
+ public bool value;
+ public Animator animator;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ animator.SetBool(propertyName, value);
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class ActivateParticleSystem : InteractableAction {
+ public ParticleSystem particleSystem;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ particleSystem.Play();
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class DelayAction : InteractableAction {
+ public float delay;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ float time = Time.time + delay;
+ while (Time.time < time) yield return CoroutineAction.Tick;
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class SetObjectActiveAction : InteractableAction {
+ public GameObject target;
+ public bool active;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ target.SetActive(active);
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class InstantiatePrefab : InteractableAction {
+ public GameObject prefab;
+ public Transform position;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ if (prefab != null && position != null) {
+ GameObject.Instantiate(prefab, position.position, position.rotation);
+ }
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class CallFunction : InteractableAction {
+ public UnityEngine.Events.UnityEvent function;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ function.Invoke();
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class TeleportAgentAction : InteractableAction {
+ public Transform destination;
+
+ public override IEnumerator<CoroutineAction> Execute (IAstarAI ai) {
+ ai.Teleport(destination.position);
+ yield break;
+ }
+
+#if MODULE_ENTITIES
+ public override IEnumerator<CoroutineAction> Execute (AgentOffMeshLinkTraversalContext context) {
+ context.Teleport(destination.position);
+ yield break;
+ }
+#endif
+ }
+
+ [System.Serializable]
+ public class TeleportAgentOnLinkAction : InteractableAction {
+ public enum Destination {
+ /// <summary>The side of the link that the agent starts traversing it from</summary>
+ RelativeStartOfLink,
+ /// <summary>The side of the link that is opposite the one the agent starts traversing it from</summary>
+ RelativeEndOfLink,
+ }
+
+ public Destination destination = Destination.RelativeEndOfLink;
+
+ public override IEnumerator<CoroutineAction> Execute() => throw new System.NotImplementedException("This action only works for agents traversing off-mesh links.");
+
+#if MODULE_ENTITIES
+ public override IEnumerator<CoroutineAction> Execute (AgentOffMeshLinkTraversalContext context) {
+ context.Teleport(destination == Destination.RelativeStartOfLink ? context.link.relativeStart : context.link.relativeEnd);
+ yield break;
+ }
+#endif
+ }
+
+ [System.Serializable]
+ public class SetTransformAction : InteractableAction {
+ public Transform transform;
+ public Transform source;
+ public bool setPosition = true;
+ public bool setRotation;
+ public bool setScale;
+
+ public override IEnumerator<CoroutineAction> Execute () {
+ if (setPosition) transform.position = source.position;
+ if (setRotation) transform.rotation = source.rotation;
+ if (setScale) transform.localScale = source.localScale;
+ yield break;
+ }
+ }
+
+ [System.Serializable]
+ public class MoveToAction : InteractableAction {
+ public Transform destination;
+ public bool useRotation;
+ public bool waitUntilReached;
+
+ public override IEnumerator<CoroutineAction> Execute (IAstarAI ai) {
+ var dest = destination.position;
+#if MODULE_ENTITIES
+ if (useRotation && ai is FollowerEntity follower) {
+ follower.SetDestination(dest, destination.rotation * Vector3.forward);
+ } else
+#endif
+ {
+ if (useRotation) Debug.LogError("useRotation is only supported for FollowerEntity agents", ai as MonoBehaviour);
+ ai.destination = dest;
+ }
+
+ if (waitUntilReached) {
+ if (ai is AIBase || ai is AILerp) {
+ // Only the FollowerEntity component is good enough to set the reachedDestination property to false immediately.
+ // The other movement scripts need to wait for the new path to be available, which is somewhat annoying.
+ ai.SearchPath();
+ while (ai.pathPending) yield return CoroutineAction.Tick;
+ }
+
+ while (!ai.reachedDestination) {
+ if (ai.destination != dest) {
+ // Something else must have changed the destination
+ yield return CoroutineAction.Cancel;
+ }
+ if (ai.reachedEndOfPath) {
+ // We have reached the end of the path, but not the destination
+ // This must mean that we cannot get any closer
+ // TODO: More accurate 'cannot move forwards' check
+ yield return CoroutineAction.Cancel;
+ }
+ yield return CoroutineAction.Tick;
+ }
+ }
+ }
+
+#if MODULE_ENTITIES
+ public override IEnumerator<CoroutineAction> Execute (AgentOffMeshLinkTraversalContext context) {
+ while (!context.MoveTowards(destination.position, destination.rotation, true, true).reached) {
+ yield return CoroutineAction.Tick;
+ }
+ yield break;
+ }
+#endif
+ }
+
+ [System.Serializable]
+ public class InteractAction : InteractableAction {
+ public Interactable interactable;
+
+ public override IEnumerator<CoroutineAction> Execute (IAstarAI ai) {
+ var it = interactable.InteractCoroutine(ai);
+ while (it.MoveNext()) {
+ yield return it.Current;
+ }
+ }
+ }
+
+ [SerializeReference]
+ public List<InteractableAction> actions;
+
+ public void Interact (IAstarAI ai) {
+ StartCoroutine(InteractCoroutine(ai));
+ }
+
+#if MODULE_ENTITIES
+ IOffMeshLinkStateMachine IOffMeshLinkHandler.GetOffMeshLinkStateMachine(AgentOffMeshLinkTraversalContext context) => this;
+
+ IEnumerable IOffMeshLinkStateMachine.OnTraverseOffMeshLink (AgentOffMeshLinkTraversalContext context) {
+ var it = InteractCoroutine(context);
+ while (it.MoveNext()) {
+ yield return null;
+ }
+ }
+
+ public IEnumerator<CoroutineAction> InteractCoroutine (Pathfinding.ECS.AgentOffMeshLinkTraversalContext context) {
+ if (actions.Count == 0) {
+ Debug.LogWarning("No actions have been set up for this interactable", this);
+ yield break;
+ }
+
+ var actionIndex = 0;
+ while (actionIndex < actions.Count) {
+ var action = actions[actionIndex];
+ if (action == null) {
+ actionIndex++;
+ continue;
+ }
+
+ var enumerator = action.Execute(context);
+ while (enumerator.MoveNext()) {
+ yield return enumerator.Current;
+ if (enumerator.Current == CoroutineAction.Cancel) yield break;
+ }
+
+ actionIndex++;
+ }
+ }
+#endif
+
+ public IEnumerator<CoroutineAction> InteractCoroutine (IAstarAI ai) {
+ if (actions.Count == 0) {
+ Debug.LogWarning("No actions have been set up for this interactable", this);
+ yield break;
+ }
+
+ var actionIndex = 0;
+ while (actionIndex < actions.Count) {
+ var action = actions[actionIndex];
+ if (action == null) {
+ actionIndex++;
+ continue;
+ }
+
+ var enumerator = action.Execute(ai);
+ while (enumerator.MoveNext()) {
+ yield return enumerator.Current;
+ if (enumerator.Current == CoroutineAction.Cancel) yield break;
+ }
+
+ actionIndex++;
+ }
+ }
+
+ void OnEnable () {
+ // Allow the interactable to be triggered by an agent traversing an off-mesh link
+ if (TryGetComponent<NodeLink2>(out var link)) link.onTraverseOffMeshLink = this;
+ }
+
+ void OnDisable () {
+ if (TryGetComponent<NodeLink2>(out var link) && link.onTraverseOffMeshLink == (IOffMeshLinkHandler)this) link.onTraverseOffMeshLink = null;
+ }
+ }
+}