using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using UnityEngine.EventSystems;
namespace Pathfinding.Examples {
/// Helper script in the example scene 'Turn Based'
[HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_turn_based_manager.php")]
public class TurnBasedManager : MonoBehaviour {
TurnBasedAI selected;
public float movementSpeed;
public GameObject nodePrefab;
public LayerMask layerMask;
List possibleMoves = new List();
EventSystem eventSystem;
public State state = State.SelectUnit;
public enum State {
SelectUnit,
SelectTarget,
Move
}
void Awake () {
eventSystem = FindObjectOfType();
}
void Update () {
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// Ignore any input while the mouse is over a UI element
if (eventSystem.IsPointerOverGameObject()) {
return;
}
if (state == State.SelectTarget) {
HandleButtonUnderRay(ray);
}
if (state == State.SelectUnit || state == State.SelectTarget) {
if (Input.GetKeyDown(KeyCode.Mouse0)) {
var unitUnderMouse = GetByRay(ray);
if (unitUnderMouse != null) {
Select(unitUnderMouse);
DestroyPossibleMoves();
GeneratePossibleMoves(selected);
state = State.SelectTarget;
}
}
}
}
// TODO: Move to separate class
void HandleButtonUnderRay (Ray ray) {
var button = GetByRay(ray);
if (button != null && Input.GetKeyDown(KeyCode.Mouse0)) {
button.OnClick();
DestroyPossibleMoves();
state = State.Move;
StartCoroutine(MoveToNode(selected, button.node));
}
}
T GetByRay(Ray ray) where T : class {
RaycastHit hit;
if (Physics.Raycast(ray, out hit, float.PositiveInfinity, layerMask)) {
return hit.transform.GetComponentInParent();
}
return null;
}
void Select (TurnBasedAI unit) {
selected = unit;
}
IEnumerator MoveToNode (TurnBasedAI unit, GraphNode node) {
var path = ABPath.Construct(unit.transform.position, (Vector3)node.position);
path.traversalProvider = unit.traversalProvider;
// Schedule the path for calculation
AstarPath.StartPath(path);
// Wait for the path calculation to complete
yield return StartCoroutine(path.WaitForPath());
if (path.error) {
// Not obvious what to do here, but show the possible moves again
// and let the player choose another target node
// Likely a node was blocked between the possible moves being
// generated and the player choosing which node to move to
Debug.LogError("Path failed:\n" + path.errorLog);
state = State.SelectTarget;
GeneratePossibleMoves(selected);
yield break;
}
// Set the target node so other scripts know which
// node is the end point in the path
unit.targetNode = path.path[path.path.Count - 1];
yield return StartCoroutine(MoveAlongPath(unit, path, movementSpeed));
unit.blocker.BlockAtCurrentPosition();
// Select a new unit to move
state = State.SelectUnit;
}
/// Interpolates the unit along the path
static IEnumerator MoveAlongPath (TurnBasedAI unit, ABPath path, float speed) {
if (path.error || path.vectorPath.Count == 0)
throw new System.ArgumentException("Cannot follow an empty path");
// Very simple movement, just interpolate using a catmull rom spline
float distanceAlongSegment = 0;
for (int i = 0; i < path.vectorPath.Count - 1; i++) {
var p0 = path.vectorPath[Mathf.Max(i-1, 0)];
// Start of current segment
var p1 = path.vectorPath[i];
// End of current segment
var p2 = path.vectorPath[i+1];
var p3 = path.vectorPath[Mathf.Min(i+2, path.vectorPath.Count-1)];
var segmentLength = Vector3.Distance(p1, p2);
while (distanceAlongSegment < segmentLength) {
var interpolatedPoint = AstarSplines.CatmullRom(p0, p1, p2, p3, distanceAlongSegment / segmentLength);
unit.transform.position = interpolatedPoint;
yield return null;
distanceAlongSegment += Time.deltaTime * speed;
}
distanceAlongSegment -= segmentLength;
}
unit.transform.position = path.vectorPath[path.vectorPath.Count - 1];
}
void DestroyPossibleMoves () {
foreach (var go in possibleMoves) {
GameObject.Destroy(go);
}
possibleMoves.Clear();
}
void GeneratePossibleMoves (TurnBasedAI unit) {
var path = ConstantPath.Construct(unit.transform.position, unit.movementPoints * 1000 + 1);
path.traversalProvider = unit.traversalProvider;
// Schedule the path for calculation
AstarPath.StartPath(path);
// Force the path request to complete immediately
// This assumes the graph is small enough that
// this will not cause any lag
path.BlockUntilCalculated();
foreach (var node in path.allNodes) {
if (node != path.startNode) {
// Create a new node prefab to indicate a node that can be reached
// NOTE: If you are going to use this in a real game, you might want to
// use an object pool to avoid instantiating new GameObjects all the time
var go = GameObject.Instantiate(nodePrefab, (Vector3)node.position, Quaternion.identity) as GameObject;
possibleMoves.Add(go);
go.GetComponent().node = node;
}
}
}
}
}