利用A* Pathfinding项目在unity中实现自动寻路
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); //朝向动画 } }
效果: