TurnBasedManager.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Pathfinding;
  5. using UnityEngine.EventSystems;
  6. namespace Pathfinding.Examples {
  7. /// <summary>Helper script in the example scene 'Turn Based'</summary>
  8. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_turn_based_manager.php")]
  9. public class TurnBasedManager : MonoBehaviour {
  10. TurnBasedAI selected;
  11. public float movementSpeed;
  12. public GameObject nodePrefab;
  13. public LayerMask layerMask;
  14. List<GameObject> possibleMoves = new List<GameObject>();
  15. EventSystem eventSystem;
  16. public State state = State.SelectUnit;
  17. public enum State {
  18. SelectUnit,
  19. SelectTarget,
  20. Move
  21. }
  22. void Awake () {
  23. eventSystem = FindObjectOfType<EventSystem>();
  24. }
  25. void Update () {
  26. var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  27. // Ignore any input while the mouse is over a UI element
  28. if (eventSystem.IsPointerOverGameObject()) {
  29. return;
  30. }
  31. if (state == State.SelectTarget) {
  32. HandleButtonUnderRay(ray);
  33. }
  34. if (state == State.SelectUnit || state == State.SelectTarget) {
  35. if (Input.GetKeyDown(KeyCode.Mouse0)) {
  36. var unitUnderMouse = GetByRay<TurnBasedAI>(ray);
  37. if (unitUnderMouse != null) {
  38. Select(unitUnderMouse);
  39. DestroyPossibleMoves();
  40. GeneratePossibleMoves(selected);
  41. state = State.SelectTarget;
  42. }
  43. }
  44. }
  45. }
  46. // TODO: Move to separate class
  47. void HandleButtonUnderRay (Ray ray) {
  48. var button = GetByRay<Astar3DButton>(ray);
  49. if (button != null && Input.GetKeyDown(KeyCode.Mouse0)) {
  50. button.OnClick();
  51. DestroyPossibleMoves();
  52. state = State.Move;
  53. StartCoroutine(MoveToNode(selected, button.node));
  54. }
  55. }
  56. T GetByRay<T>(Ray ray) where T : class {
  57. RaycastHit hit;
  58. if (Physics.Raycast(ray, out hit, float.PositiveInfinity, layerMask)) {
  59. return hit.transform.GetComponentInParent<T>();
  60. }
  61. return null;
  62. }
  63. void Select (TurnBasedAI unit) {
  64. selected = unit;
  65. }
  66. IEnumerator MoveToNode (TurnBasedAI unit, GraphNode node) {
  67. var path = ABPath.Construct(unit.transform.position, (Vector3)node.position);
  68. path.traversalProvider = unit.traversalProvider;
  69. // Schedule the path for calculation
  70. AstarPath.StartPath(path);
  71. // Wait for the path calculation to complete
  72. yield return StartCoroutine(path.WaitForPath());
  73. if (path.error) {
  74. // Not obvious what to do here, but show the possible moves again
  75. // and let the player choose another target node
  76. // Likely a node was blocked between the possible moves being
  77. // generated and the player choosing which node to move to
  78. Debug.LogError("Path failed:\n" + path.errorLog);
  79. state = State.SelectTarget;
  80. GeneratePossibleMoves(selected);
  81. yield break;
  82. }
  83. // Set the target node so other scripts know which
  84. // node is the end point in the path
  85. unit.targetNode = path.path[path.path.Count - 1];
  86. yield return StartCoroutine(MoveAlongPath(unit, path, movementSpeed));
  87. unit.blocker.BlockAtCurrentPosition();
  88. // Select a new unit to move
  89. state = State.SelectUnit;
  90. }
  91. /// <summary>Interpolates the unit along the path</summary>
  92. static IEnumerator MoveAlongPath (TurnBasedAI unit, ABPath path, float speed) {
  93. if (path.error || path.vectorPath.Count == 0)
  94. throw new System.ArgumentException("Cannot follow an empty path");
  95. // Very simple movement, just interpolate using a catmull rom spline
  96. float distanceAlongSegment = 0;
  97. for (int i = 0; i < path.vectorPath.Count - 1; i++) {
  98. var p0 = path.vectorPath[Mathf.Max(i-1, 0)];
  99. // Start of current segment
  100. var p1 = path.vectorPath[i];
  101. // End of current segment
  102. var p2 = path.vectorPath[i+1];
  103. var p3 = path.vectorPath[Mathf.Min(i+2, path.vectorPath.Count-1)];
  104. var segmentLength = Vector3.Distance(p1, p2);
  105. while (distanceAlongSegment < segmentLength) {
  106. var interpolatedPoint = AstarSplines.CatmullRom(p0, p1, p2, p3, distanceAlongSegment / segmentLength);
  107. unit.transform.position = interpolatedPoint;
  108. yield return null;
  109. distanceAlongSegment += Time.deltaTime * speed;
  110. }
  111. distanceAlongSegment -= segmentLength;
  112. }
  113. unit.transform.position = path.vectorPath[path.vectorPath.Count - 1];
  114. }
  115. void DestroyPossibleMoves () {
  116. foreach (var go in possibleMoves) {
  117. GameObject.Destroy(go);
  118. }
  119. possibleMoves.Clear();
  120. }
  121. void GeneratePossibleMoves (TurnBasedAI unit) {
  122. var path = ConstantPath.Construct(unit.transform.position, unit.movementPoints * 1000 + 1);
  123. path.traversalProvider = unit.traversalProvider;
  124. // Schedule the path for calculation
  125. AstarPath.StartPath(path);
  126. // Force the path request to complete immediately
  127. // This assumes the graph is small enough that
  128. // this will not cause any lag
  129. path.BlockUntilCalculated();
  130. foreach (var node in path.allNodes) {
  131. if (node != path.startNode) {
  132. // Create a new node prefab to indicate a node that can be reached
  133. // NOTE: If you are going to use this in a real game, you might want to
  134. // use an object pool to avoid instantiating new GameObjects all the time
  135. var go = GameObject.Instantiate(nodePrefab, (Vector3)node.position, Quaternion.identity) as GameObject;
  136. possibleMoves.Add(go);
  137. go.GetComponent<Astar3DButton>().node = node;
  138. }
  139. }
  140. }
  141. }
  142. }