using UnityEngine;
using System.Collections.Generic;
using Pathfinding;
using Pathfinding.RVO;
///
/// Example movement script for using RVO.
///
/// Primarily intended for the example scenes.
/// You can use the AIPath or RichAI movement scripts in your own projects.
///
/// See:
/// See:
/// See:
///
[RequireComponent(typeof(RVOController))]
[RequireComponent(typeof(Seeker))]
[HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_examples_1_1_r_v_o_example_agent.php")]
public class RVOAgentWanderer : MonoBehaviour {
public float repathRate = 1;
private float nextRepath = 0;
public Vector3 target;
public bool canSearchAgain = true;
public bool reachedEndOfPath = false;
public bool waiting;
private RVOController controller;
public float maxSpeed = 10;
public float rotationSpeed = 5;
Path path = null;
public List vectorPath;
int wp;
public float moveNextDist = 1;
public float slowdownDistance = 1;
public LayerMask groundMask;
Seeker seeker;
MeshRenderer[] rends;
public void Awake () {
seeker = GetComponent();
controller = GetComponent();
}
/// Set the point to move to
public void SetTarget (Vector3 target)
{
reachedEndOfPath = false;
this.target = target;
RecalculatePath();
}
/// Animate the change of color
public void SetColor (Color color) {
if (rends == null) rends = GetComponentsInChildren();
foreach (MeshRenderer rend in rends) {
Color current = rend.material.GetColor("_TintColor");
AnimationCurve curveR = AnimationCurve.Linear(0, current.r, 1, color.r);
AnimationCurve curveG = AnimationCurve.Linear(0, current.g, 1, color.g);
AnimationCurve curveB = AnimationCurve.Linear(0, current.b, 1, color.b);
AnimationClip clip = new AnimationClip();
#if !(UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8)
// Needed to make Unity5 happy
clip.legacy = true;
#endif
clip.SetCurve("", typeof(Material), "_TintColor.r", curveR);
clip.SetCurve("", typeof(Material), "_TintColor.g", curveG);
clip.SetCurve("", typeof(Material), "_TintColor.b", curveB);
Animation anim = rend.gameObject.GetComponent();
if (anim == null) {
anim = rend.gameObject.AddComponent();
}
clip.wrapMode = WrapMode.Once;
anim.AddClip(clip, "ColorAnim");
anim.Play("ColorAnim");
}
}
public void RecalculatePath () {
print(transform.name+" RecalculatePath");
canSearchAgain = false;
nextRepath = Time.time+repathRate*(Random.value+0.5f);
seeker.StartPath(transform.position, target, OnPathComplete);
}
public void OnPathComplete (Path _p) {
ABPath p = _p as ABPath;
canSearchAgain = true;
if (path != null) path.Release(this);
path = p;
p.Claim(this);
if (p.error) {
wp = 0;
vectorPath = null;
return;
}
Vector3 p1 = p.originalStartPoint;
Vector3 p2 = transform.position;
p1.y = p2.y;
float d = (p2-p1).magnitude;
wp = 0;
vectorPath = p.vectorPath;
Vector3 waypoint;
if (moveNextDist > 0) {
for (float t = 0; t <= d; t += moveNextDist*0.6f) {
wp--;
Vector3 pos = p1 + (p2-p1)*t;
do {
wp++;
waypoint = vectorPath[wp];
} while (controller.To2D(pos - waypoint).sqrMagnitude < moveNextDist*moveNextDist && wp != vectorPath.Count-1);
}
}
}
public void Update ()
{
if (waiting) return;
if (Time.time >= nextRepath && canSearchAgain)
{
print(transform.name +" CONDITION");
//agentClose = true;
RecalculatePath();
}
Vector3 pos = transform.position;
if (vectorPath != null && vectorPath.Count != 0) {
while ((controller.To2D(pos - vectorPath[wp]).sqrMagnitude < moveNextDist*moveNextDist && wp != vectorPath.Count-1) || wp == 0) {
wp++;
}
// Current path segment goes from vectorPath[wp-1] to vectorPath[wp]
// We want to find the point on that segment that is 'moveNextDist' from our current position.
// This can be visualized as finding the intersection of a circle with radius 'moveNextDist'
// centered at our current position with that segment.
var p1 = vectorPath[wp-1];
var p2 = vectorPath[wp];
// Calculate the intersection with the circle. This involves some math.
var t = VectorMath.LineCircleIntersectionFactor(controller.To2D(transform.position), controller.To2D(p1), controller.To2D(p2), moveNextDist);
// Clamp to a point on the segment
t = Mathf.Clamp01(t);
Vector3 waypoint = Vector3.Lerp(p1, p2, t);
waypoint= new Vector3(waypoint.x, 2.4f, waypoint.z);
// Calculate distance to the end of the path
float remainingDistance = controller.To2D(waypoint - pos).magnitude + controller.To2D(waypoint - p2).magnitude;
for (int i = wp; i < vectorPath.Count - 1; i++) remainingDistance += controller.To2D(vectorPath[i+1] - vectorPath[i]).magnitude;
// Set the target to a point in the direction of the current waypoint at a distance
// equal to the remaining distance along the path. Since the rvo agent assumes that
// it should stop when it reaches the target point, this will produce good avoidance
// behavior near the end of the path. When not close to the end point it will act just
// as being commanded to move in a particular direction, not toward a particular point
var rvoTarget = (waypoint - pos).normalized * remainingDistance + pos;
// When within [slowdownDistance] units from the target, use a progressively lower speed
var desiredSpeed = Mathf.Clamp01(remainingDistance / slowdownDistance) * maxSpeed;
Debug.DrawLine(transform.position, waypoint, Color.red);
controller.SetTarget(rvoTarget, desiredSpeed, maxSpeed);
if (Utils.MyApprox(remainingDistance,0,0.05f))
{
reachedEndOfPath = true;
waiting = true;
}
} else {
// Stand still
controller.SetTarget(pos, maxSpeed, maxSpeed);
}
// Get a processed movement delta from the rvo controller and move the character.
// This is based on information from earlier frames.
var movementDelta = controller.CalculateMovementDelta(Time.deltaTime);
pos += movementDelta;
// Rotate the character if the velocity is not extremely small
if (Time.deltaTime > 0 && movementDelta.magnitude / Time.deltaTime > 0.01f) {
var rot = transform.rotation;
var targetRot = Quaternion.LookRotation(movementDelta, controller.To3D(Vector2.zero, 1));
if (controller.movementPlane == MovementPlane.XY) {
targetRot = targetRot * Quaternion.Euler(-90, 180, 0);
}
transform.rotation = Quaternion.Slerp(rot, targetRot, Time.deltaTime * rotationSpeed);
}
if (controller.movementPlane == MovementPlane.XZ) {
RaycastHit hit;
if (Physics.Raycast(pos + Vector3.up, Vector3.down, out hit, 2, groundMask)) {
pos.y = hit.point.y;
}
}
transform.position = pos;
}
}