A* Pathfinding Project (Unity A*寻路插件) 使用教程
发布日期:2021-06-30 19:39:22 浏览次数:2 分类:技术文章

本文共 5740 字,大约阅读时间需要 19 分钟。

转载注明出处:

Unity4.6 后续版本都已经内置了寻路AI了,之前的文章有介绍

然而两年来项目中一直使用的是 A* Pathfinding 这个插件的,所以抽时间来写下这个插件的简单使用。

根据游戏的类型,使用到的插件功能可能会不一样,我这里只介绍最简单的,也是使用的最多的简单寻路。复杂的如跟随、动态,都有对应的例子来学习。

我也一直都没有去看……

转自http://blog.csdn/huutu http://www.thisisgame.com.cn

下面是动态图,借助 A* 插件,编写很少的代码就可以做到寻路。

1、创建场景

在场景中添加一些Cube 作为障碍物 Obstacles,添加一个 Capsule 作为Player,然后添加一个Plane 作为地面,再添加一个Plane,作为斜坡。


在创建一个GameObject,改名为 A* ,添加A Star Path (Path finder) 组件。

2、编辑场景,指定障碍物

A* 插件中,是根据 Layer 来判断障碍物的,所以我们要把 作为障碍物的 Cubes 都设置到 Obstacle 这一个Layer。

然后给我们的地板,设置Layer 为 Ground ,两块地板都是

转自http://blog.csdn/huutu http://www.thisisgame.com.cn

3、生成寻路网格

选中 A* ,在Inspector 中,展开 。查看下面的面板。

如图中,

黑色箭头所指是宽高,这里的宽高,是指格子的数量。这里用到的就是 A* 的格子寻路。

调整宽高,覆盖整个Plane。

红色箭头所指,是左上、右上、左下、右下、中心 四个点,选中其中一个点,就可以调整这个点的位置。

选中中心,点击蓝色箭头所指的 Snap Size,会根据中心的位置来自动对齐。

继续设置。

红框中的Collision Testing,是生成 禁止通过 格子的。

因为我们的 Cubes 是障碍物,所以在 Mask 中选择 Cubes 所在的Layer - Obstacles。

黄色框中的Height Testing 是用来 让寻路节点 与 Ground 进行检测的,比如要爬坡的时候就需要检测高度。

设置完成后,点击Scan,就会生成寻路网格。

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

4、编写寻路 AI 代码

生成寻路网格之后,我们在代码中就可以使用 A* 来进行寻路了。

首先在 Player 这个 Capsule 上添加Seeker 组件。

然后新建脚本 AStarPlayer.cs 作为测试代码。

在代码中,首先我们从 屏幕发射射线,来定位目标位置。

然后使用 Seeker 来开始生成最短路径。

Seeker生成路径成功后,会把每一个节点的位置保存在 List中。

我们按照顺序读取 List 中的位置,位移Player 到对应的位置,就完成了寻路。

下面是完整代码:

[html]   
 
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using Pathfinding;  
  4.   
  5. public class AStarPlayer : MonoBehaviour   
  6. {  
  7.     //目标位置;  
  8.      Vector3 targetPosition;  
  9.   
  10.     Seeker seeker;  
  11.     CharacterController characterController;  
  12.   
  13.     //计算出来的路线;  
  14.      Path path;  
  15.   
  16.     //移动速度;  
  17.      float playerMoveSpeed = 10f;  
  18.   
  19.     //当前点  
  20.     int currentWayPoint = 0;  
  21.   
  22.     bool stopMove = true;  
  23.   
  24.     //Player中心点;  
  25.     float playerCenterY = 1.0f;  
  26.   
  27.   
  28.     // Use this for initialization  
  29.     void Start ()   
  30.     {  
  31.         seeker = GetComponent<Seeker>();  
  32.   
  33.         playerCenterY = transform.localPosition.y;  
  34.     }  
  35.   
  36.     //寻路结束;  
  37.     public void OnPathComplete(Path p)  
  38.     {  
  39.         Debug.Log("OnPathComplete error = "+p.error);  
  40.   
  41.         if (!p.error)  
  42.         {  
  43.             currentWayPoint = 0;  
  44.             path = p;  
  45.             stopMove = false;  
  46.         }  
  47.   
  48.         for (int index = 0; index < path.vectorPath.Count; index++)  
  49.         {  
  50.             Debug.Log("path.vectorPath["+index+"]="+path.vectorPath[index]);  
  51.         }  
  52.     }  
  53.       
  54.     // Update is called once per frame  
  55.     void Update ()   
  56.     {  
  57.         if (Input.GetMouseButtonDown(0))  
  58.         {  
  59.             RaycastHit hit;  
  60.             if (!Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100))  
  61.             {  
  62.                 return;  
  63.             }  
  64.             if (!hit.transform)  
  65.             {  
  66.                 return;  
  67.             }  
  68.             targetPosition = hit.point;// new Vector3(hit.point.x, transform.localPosition.y, hit.point.z);  
  69.   
  70.             Debug.Log("targetPosition=" + targetPosition);  
  71.   
  72.             seeker.StartPath(transform.position, targetPosition,OnPathComplete);  
  73.         }  
  74.     }  
  75.   
  76.     void FixedUpdate()  
  77.     {  
  78.         if (path == null || stopMove)  
  79.         {  
  80.             return;  
  81.         }  
  82.   
  83.         //根据Player当前位置和 下一个寻路点的位置,计算方向;  
  84.         Vector3 currentWayPointV = new Vector3(path.vectorPath[currentWayPoint].x, path.vectorPath[currentWayPoint].y + playerCenterY, path.vectorPath[currentWayPoint].z);  
  85.         Vector3 dir = (currentWayPointV - transform.position).normalized;  
  86.   
  87.         //计算这一帧要朝着 dir方向 移动多少距离;  
  88.         dir *= playerMoveSpeed * Time.fixedDeltaTime;  
  89.   
  90.         //计算加上这一帧的位移,是不是会超过下一个节点;  
  91.         float offset = Vector3.Distance(transform.localPosition, currentWayPointV);  
  92.   
  93.         if (offset < 0.1f)  
  94.         {  
  95.             transform.localPosition = currentWayPointV;  
  96.   
  97.             currentWayPoint++;  
  98.   
  99.             if (currentWayPoint == path.vectorPath.Count)  
  100.             {  
  101.                 stopMove = true;  
  102.   
  103.                 currentWayPoint = 0;  
  104.                 path = null;  
  105.             }  
  106.         }  
  107.         else  
  108.         {  
  109.             if (dir.magnitude > offset)  
  110.             {  
  111.                 Vector3 tmpV3 = dir * (offset / dir.magnitude);  
  112.                 dir = tmpV3;  
  113.   
  114.                 currentWayPoint++;  
  115.   
  116.                 if (currentWayPoint == path.vectorPath.Count)  
  117.                 {  
  118.                     stopMove = true;  
  119.   
  120.                     currentWayPoint = 0;  
  121.                     path = null;  
  122.                 }  
  123.             }  
  124.             transform.localPosition += dir;  
  125.         }  
  126.     }  
  127. }  

至此简单的寻路了。

在A* 的Example 中,有很多个例子。

最简单的寻路脚本写法是 直接继承 AIPath 。

下面新建一个 脚本 PlayerAI.cs 继承 AIPath 来作为测试

[html]   
 
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using Pathfinding.RVO;  
  4.   
  5. namespace Pathfinding  
  6. {  
  7.     [RequireComponent(typeof(Seeker))]  
  8.     [RequireComponent(typeof(CharacterController))]  
  9.     public class PlayerAI : AIPath  
  10.     {  
  11.         /** Minimum velocity for moving */  
  12.         public float sleepVelocity = 0.4F;  
  13.   
  14.         /** Speed relative to velocity with which to play animations */  
  15.         public float animationSpeed = 0.2F;  
  16.   
  17.         /** Effect which will be instantiated when end of path is reached.  
  18.          * \see OnTargetReached */  
  19.         public GameObject endOfPathEffect;  
  20.   
  21.         public new void Start()  
  22.         {  
  23.             //Call Start in base script (AIPath)  
  24.             base.Start();  
  25.         }  
  26.   
  27.         /** Point for the last spawn of #endOfPathEffect */  
  28.         protected Vector3 lastTarget;  
  29.   
  30.         public override void OnTargetReached()  
  31.         {  
  32.             if (endOfPathEffect != null && Vector3.Distance(tr.position, lastTarget) > 1)  
  33.             {  
  34.                 GameObject.Instantiate(endOfPathEffect, tr.position, tr.rotation);  
  35.                 lastTarget = tr.position;  
  36.             }  
  37.         }  
  38.   
  39.         public override Vector3 GetFeetPosition()  
  40.         {  
  41.             return tr.position;  
  42.         }  
  43.   
  44.         protected new void Update()  
  45.         {  
  46.   
  47.             if (Input.GetMouseButtonDown(0))  
  48.             {  
  49.                 RaycastHit hit;  
  50.                 if (!Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100))  
  51.                 {  
  52.                     return;  
  53.                 }  
  54.                 if (!hit.transform)  
  55.                 {  
  56.                     return;  
  57.                 }  
  58.                 target.localPosition = hit.point;  
  59.             }  
  60.   
  61.             //Get velocity in world-space  
  62.             Vector3 velocity;  
  63.             if (canMove)  
  64.             {  
  65.                 //Calculate desired velocity  
  66.                 Vector3 dir = CalculateVelocity(GetFeetPosition());  
  67.   
  68.                 //Rotate towards targetDirection (filled in by CalculateVelocity)  
  69.                 RotateTowards(targetDirection);  
  70.   
  71.                 dir.y = 0;  
  72.                 if (dir.sqrMagnitude > sleepVelocity * sleepVelocity)  
  73.                 {  
  74.                     //If the velocity is large enough, move  
  75.                 }  
  76.                 else  
  77.                 {  
  78.                     //Otherwise, just stand still (this ensures gravity is applied)  
  79.                     dir = Vector3.zero;  
  80.                 }  
  81.   
  82.                 if (this.rvoController != null)  
  83.                 {  
  84.                     rvoController.Move(dir);  
  85.                     velocity = rvoController.velocity;  
  86.                 }  
  87.                 else  
  88.                     if (navController != null)  
  89.                     {  
  90. #if FALSE  
  91.                     navController.SimpleMove (GetFeetPosition(), dir);  
  92. #endif  
  93.                         velocity = Vector3.zero;  
  94.                     }  
  95.                     else if (controller != null)  
  96.                     {  
  97.                         controller.SimpleMove(dir);  
  98.                         velocity = controller.velocity;  
  99.                     }  
  100.                     else  
  101.                     {  
  102.                         Debug.LogWarning("No NavmeshController or CharacterController attached to GameObject");  
  103.                         velocity = Vector3.zero;  
  104.                     }  
  105.             }  
  106.             else  
  107.             {  
  108.                 velocity = Vector3.zero;  
  109.             }  
  110.         }  
  111.     }  
  112. }  

代码量少,但是不如自己写的直观。

两种不同的脚本都可以实现寻路效果。

示例项目打包下载:

[html]   
 
  1. http://pan.baidu.com/s/1hsm6YNi  

转载地址:https://linxinfa.blog.csdn.net/article/details/71080462 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:bash学习笔记
下一篇:python的代码缩进

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月28日 00时50分26秒