A* Pathfinding 项目地址: https://arongranberg.com/astar/
学习视频:Unity 2D AI自动寻路功能 [风农译制]_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
素材地址:2D Beginner: Tutorial Resources | 资源包 | Unity Asset Store
1.生成导航网格
首先制作一个瓦片地图,并且加上一个瓦片地图碰撞器
创建一个空物体,挂上pathfinder组件
创建一个网格图
勾选2d物理系统选项,将网格覆盖瓦片地图,之后选择瓦片地图所在的层。
在下方选择扫描,生成导航网格。
2.寻路脚本
给对象挂上seeker脚本,seeker脚本可以用来生成寻路路径。接着创建一个新脚本(EnemyContorller),用来操控对象自动寻路。
编写脚本EnemyContorller(这个脚本还不太完善,只是简单的示例)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class EnemyContorller : MonoBehaviour
{
public float enemyspeed;//敌人速度
public Transform target;//设置跟随目标
public float nextWaypointDistance = 3f;//敌人距离一个路标点多近的时候,需要向下一个点移动
public Animator animator;
Path path;//储存路径
int currentWayPoint = 0;//代表在当前路径上,正在朝哪一个路标点移动
bool reachedEndOfPath = false;//是否到达最后一个点
Rigidbody2D rigidbody2d;
Seeker seeker;
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
seeker = GetComponent<Seeker>();
enemyCurrentHealth = enemyMaxHealth;
InvokeRepeating("UpdatePath", 0, 0.5f);//第二个参数,多久第一次调用它,第三个参数,调用速率
}
private void UpdatePath()
{
if(seeker.IsDone())//如果没有在计算路径,进行下一次更新
seeker.StartPath(rigidbody2d.position, target.position, OnPathComplete);
//生成路径,第三个参数为生成路径完成后调用的函数
}
void FixedUpdate()
{
if (path == null)
return;//路径为空,退出
if (currentWayPoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
return;
}
else
{
reachedEndOfPath = false;
}//检查路径点是否走完
Vector2 direction = ((Vector2)path.vectorPath[currentWayPoint] - rigidbody2d.position).normalized;
//设置朝向
animator.SetFloat("Look X", direction.x);
animator.SetFloat("Look Y", direction.y);
Vector2 force = direction * enemyspeed * Time.deltaTime;//设置力
animator.SetFloat("Speed", force.magnitude);//播放行走动画
rigidbody2d.AddForce(force);//给敌人施加力
float distance = Vector2.Distance(rigidbody2d.position, path.vectorPath[currentWayPoint]);
//计算到下一个点的距离
if (distance < nextWaypointDistance)
{
currentWayPoint++;
}//小于,移动到下一个坐标点
}
private void OnPathComplete(Path p)
{
if(!p.error)
{
path = p;
currentWayPoint = 0;
}//若路径无错误,使用这个路径
}
}
3.完善代码
敌人改成了NPC。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class NPCContorller : MonoBehaviour
{
public float npcSpeed;//NPC速度
public Transform target;//设置跟随目标
public float nextWaypointDistance = 3f;//NPC距离一个路标点多近的时候,需要向下一个点移动
public float followDistance = 3f;//跟随距离
public Animator animator;
public int npcMaxHealth;
private int npcCurrentHealth;
public int npcHealth { get { return npcCurrentHealth; } }
Path path = null;//储存路径
int currentWayPoint = 0;//代表在当前路径上,正在朝哪一个路标点移动
bool reachedEndOfPath = false;//是否到达最后一个点
Rigidbody2D rigidbody2d;
Seeker seeker;
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
seeker = GetComponent<Seeker>();
npcCurrentHealth = npcMaxHealth;
InvokeRepeating("UpdatePath", 0, 0.5f);
}
//更新路径
private void UpdatePath()
{
float targetDistance = Vector2.Distance(rigidbody2d.position, target.position);
//到目标的距离
if (targetDistance <= followDistance)
//如果到目标的距离小于或等于跟随距离,停止更新路径
{
return;
}
else
{
if (seeker.IsDone())//如果没有在计算路径,进行下一次更新
CancelInvoke("NPCStop");//更新路径时,停止调用这个函数,避免动画错误
seeker.StartPath(rigidbody2d.position, target.position, OnPathComplete);
//生成路径,第三个参数为生成路径完成后调用的函数
}
}
void FixedUpdate()
{
if (path == null)
return;//路径为空,退出
if (currentWayPoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
Invoke("NPCStop", 0.3f);//0.3秒过后调用函数,使其到达跟随距离
return;//路径点走完,退出函数
}
else
{
reachedEndOfPath = false;
}//检查路径点是否走完
Vector2 direction = ((Vector2)path.vectorPath[currentWayPoint] - rigidbody2d.position).normalized;
//设置朝向
animator.SetFloat("Look X", direction.x);
animator.SetFloat("Look Y", direction.y);
Vector2 seepd = direction * npcSpeed * Time.deltaTime;//设置力
animator.SetFloat("Speed", seepd.magnitude);//播放走路动画
rigidbody2d.velocity = seepd;//给一个速度
float distance = Vector2.Distance(rigidbody2d.position, path.vectorPath[currentWayPoint]);
//计算到下一个点的距离
if (distance < nextWaypointDistance)
{
currentWayPoint++;
}//小于,移动到下一个坐标点
}
//检查路径是否正确
private void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWayPoint = 0;
if (path.pathID != 1)
currentWayPoint++;
//避免移动时生成新路径后,往新路径的0号路径点移动
}//若路径无错误,使用这个路径
}
//npc生命改变
public void NPCChangeHealth(int amount)
{
npcCurrentHealth = Mathf.Clamp(npcCurrentHealth + amount, 0, npcMaxHealth);
Debug.Log("NPC:" + npcCurrentHealth + '\\' + npcMaxHealth);
}
//靠近目标后停下
private void NPCStop()
{
rigidbody2d.velocity = Vector2.zero;
//设置速度为0
Vector2 direction = ((Vector2)target.position - rigidbody2d.position).normalized;
//朝向
animator.SetFloat("Speed", 0);
animator.SetFloat("Look X", direction.x);
animator.SetFloat("Look Y", direction.y);
//朝向动画
}
}
效果: