最终效果
先上Graph图和配置
注意Diameter是用来检测是否可以通过的直接
需要考虑AI对象的碰撞器大小,以免测试通过但是卡住过不去。
这是AI对象的组件
向鼠标单击右键的地方移动
由于使用的是移动而不是施加力,所以可能出现超过的情况
- 第一种处理办法是修改移动向量的长度使得当好落在目标点。但是这种办法会导致移动过程速度时快时慢,镜头移动不流畅
- 第二种办法是循环检测下一个目标点是否在当前移动后,已经越过了
可能被卡住,所以写了个随机方向移动(移动后重新寻路)
杂揉了更新动画的代码,不能拿来直接用,就参考一下吧
/// <summary>
/// 鼠标的世界坐标
/// </summary>
/// <returns></returns>
public static Vector2 MousePosInWorld()
{
return Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
/// <summary>
/// 随机方向向量
/// </summary>
/// <returns></returns>
public Vector2 RandomDirection()
{
float x = Random.Range(-1f, 1f);
float y = Mathf.Sqrt(1 - x * x);
if (Random.Range(0f, 1f) > 0.5f)
{
y *= -1;
}
return new Vector2(x, y);
}
/*
* Author : Jk_Chen
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class PlayerAI : MonoBehaviour
{
PlayerControler playerControler;
float MaxSpeed = 10; // 最大速度
float SpeedLimit = 0.01f; // 更新动画器的速度阈值
Vector2 target; // 目标位置
public bool enable; // 是否AI行动
Path path; // 路径
int nextP = 0; // 当前要去的点下标
Seeker seeker;
Rigidbody2D _rigidbody;
void Start()
{
enable = false;
playerControler = GetComponent<PlayerControler>();
_rigidbody = GetComponent<Rigidbody2D>();
seeker = GetComponent<Seeker>();
}
void FixedUpdate()
{
// 右键
if (Input.GetMouseButtonDown(1))
{
EndAI("新的寻路");
Vector2 vector = Helper.MousePosInWorld();
StartAI(vector);
}
// AI移动
if (enable)
{
// 卡住了往随机方向撤 timeLen 秒
if (resTime > 0)
{
resTime -= Time.deltaTime;
MoveRandom();
// 结束后重新确实路径
if (resTime <= 0)
{
SeekPath();
}
}
else
{
// 每隔20帧检查是否被卡住
if (Time.frameCount % 20 == 0)
prePos = transform.position;
MoveAI();
if (Time.frameCount % 20 == 19)
{
if (Vector2.Distance(prePos, transform.position) < 0.01f)
{
randomV = Helper.instance.RandomDirection();
Debug.Log(prePos + " " + transform.position);
resTime = timeLen;
}
}
}
}
}
Vector2 prePos = new Vector2(-1e9f, -1e9f);
float resTime;
Vector2 randomV;
float timeLen = 0.5f;
/// <summary>
/// 向下一个点移动
/// </summary>
void MoveAI()
{
if (path == null || !enable)
{
return;
}
// 获取向量
Vector2 vector = (path.vectorPath[nextP] - transform.position);
if (vector.magnitude > SpeedLimit)
{
Vector2 delta = Time.deltaTime * vector.normalized * MaxSpeed;
playerControler.UpdateAnimator(vector.normalized);
Vector2 pos = (Vector2)transform.position + delta;
_rigidbody.MovePosition(pos);
//由于使用的是移动而不是施加力,所以可能出现超过的情况
//第一种处理办法是修改移动向量的长度使得当好落在目标点
// 但是这种办法会导致移动过程速度时快时慢,镜头移动不流畅
// 第二种办法是循环检测下一个目标点是否在当前移动后,已经越过了
while (delta.magnitude > Vector2.Distance(transform.position, path.vectorPath[nextP]))
{
nextP++;
if (nextP == path.vectorPath.Count)
{
EndAI("已到达终点");
break;
}
}
}
else
{
nextP++;
if(nextP == path.vectorPath.Count)
{
EndAI("已到达终点");
}
playerControler.UpdateAnimator(vector);
}
}
/// <summary>
/// 向随机生成的方向移动
/// </summary>
void MoveRandom()
{
Vector2 delta = Time.deltaTime * randomV * MaxSpeed;
//playerControler.UpdateAnimator(delta.normalized);
Vector2 pos = (Vector2)transform.position + delta;
_rigidbody.MovePosition(pos);
}
/// <summary>
/// 开启AI模式
/// </summary>
/// <param name="pos"></param>
public void StartAI(Vector2 pos)
{
enable = true;
target = pos;
SeekPath();
}
/// <summary>
/// 结束AI
/// </summary>
public void EndAI(string info)
{
enable = false;
path = null;
Debug.Log(info);
}
/// <summary>
/// 向目标寻路
/// </summary>
/// <param name="pos"></param>
void SeekPath()
{
if (path != null && !seeker.IsDone())
{
return;
}
Debug.Log("Try " + transform.position + " to " + target);
seeker.StartPath(transform.position, target, OnPathComplete);
//if (seeker.IsDone())
//{
// EndAI("暂时无法到达终点");
//}
}
/// <summary>
/// 完成寻路
/// </summary>
/// <param name="p"></param>
void OnPathComplete(Path p)
{
if (!p.error)
{
Debug.Log("Success Seek");
path = p;
nextP = 0;
}
else
{
EndAI("");
}
}
}
去掉Debug