A* Pathfinding Project (Unity A*寻路插件) 使用教程

摘要:
Unity4.6兴许版本号都已经内置了寻路AI了。之前的文章有介绍Unity3d寻路功能介绍及项目演示然而两年来项目中一直使用的是A*Pathfinding这个插件的。在创建一个GameObject,改名为A*。调整宽高,覆盖整个Plane。黄色框中的HeightTesting是用来让寻路节点与Ground进行检測的。首先在Player这个Capsule上加入Seeker组件。Seeker生成路径成功后。

Unity4.6 兴许版本号都已经内置了寻路AI了。之前的文章有介绍

Unity3d 寻路功能 介绍及项目演示

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

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

我也一直都没有去看……

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

以下是动态图,借助 A* 插件,编写非常少的代码就能够做到寻路。

A* Pathfinding Project (Unity A*寻路插件) 使用教程第1张

1、创建场景

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


A* Pathfinding Project (Unity A*寻路插件) 使用教程第2张

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

A* Pathfinding Project (Unity A*寻路插件) 使用教程第3张

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

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

A* Pathfinding Project (Unity A*寻路插件) 使用教程第4张

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

A* Pathfinding Project (Unity A*寻路插件) 使用教程第5张

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

3、生成寻路网格

选中 A* ,在Inspector 中,展开 。

查看以下的面板。

A* Pathfinding Project (Unity A*寻路插件) 使用教程第6张

如图中,

黑色箭头所指是宽高。这里的宽高。是指格子的数量。

这里用到的就是 A* 的格子寻路。

调整宽高,覆盖整个Plane。

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

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

A* Pathfinding Project (Unity A*寻路插件) 使用教程第7张

继续设置。

红框中的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 到相应的位置,就完毕了寻路。

以下是完整代码:

using UnityEngine;
using System.Collections;
using Pathfinding;

public class AStarPlayer : MonoBehaviour 
{
    //目标位置;
     Vector3 targetPosition;

    Seeker seeker;
    CharacterController characterController;

    //计算出来的路线;
     Path path;

    //移动速度;
     float playerMoveSpeed = 10f;

    //当前点
    int currentWayPoint = 0;

    bool stopMove = true;

    //Player中心点;
    float playerCenterY = 1.0f;


	// Use this for initialization
	void Start () 
    {
        seeker = GetComponent<Seeker>();

        playerCenterY = transform.localPosition.y;
	}

    //寻路结束;
    public void OnPathComplete(Path p)
    {
        Debug.Log("OnPathComplete error = "+p.error);

        if (!p.error)
        {
            currentWayPoint = 0;
            path = p;
            stopMove = false;
        }

        for (int index = 0; index < path.vectorPath.Count; index++)
        {
            Debug.Log("path.vectorPath["+index+"]="+path.vectorPath[index]);
        }
    }
	
	// Update is called once per frame
	void Update () 
    {
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            if (!Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100))
            {
                return;
            }
            if (!hit.transform)
            {
                return;
            }
            targetPosition = hit.point;// new Vector3(hit.point.x, transform.localPosition.y, hit.point.z);

            Debug.Log("targetPosition=" + targetPosition);

            seeker.StartPath(transform.position, targetPosition,OnPathComplete);
        }
	}

    void FixedUpdate()
    {
        if (path == null || stopMove)
        {
            return;
        }

        //依据Player当前位置和 下一个寻路点的位置,计算方向;
        Vector3 currentWayPointV = new Vector3(path.vectorPath[currentWayPoint].x, path.vectorPath[currentWayPoint].y + playerCenterY, path.vectorPath[currentWayPoint].z);
        Vector3 dir = (currentWayPointV - transform.position).normalized;

        //计算这一帧要朝着 dir方向 移动多少距离;
        dir *= playerMoveSpeed * Time.fixedDeltaTime;

        //计算加上这一帧的位移,是不是会超过下一个节点;
        float offset = Vector3.Distance(transform.localPosition, currentWayPointV);

        if (offset < 0.1f)
        {
            transform.localPosition = currentWayPointV;

            currentWayPoint++;

            if (currentWayPoint == path.vectorPath.Count)
            {
                stopMove = true;

                currentWayPoint = 0;
                path = null;
            }
        }
        else
        {
            if (dir.magnitude > offset)
            {
                Vector3 tmpV3 = dir * (offset / dir.magnitude);
                dir = tmpV3;

                currentWayPoint++;

                if (currentWayPoint == path.vectorPath.Count)
                {
                    stopMove = true;

                    currentWayPoint = 0;
                    path = null;
                }
            }
            transform.localPosition += dir;
        }
    }
}

至此简单的寻路了。

在A* 的Example 中,有非常多个样例。

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

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

using UnityEngine;
using System.Collections;
using Pathfinding.RVO;

namespace Pathfinding
{
    [RequireComponent(typeof(Seeker))]
    [RequireComponent(typeof(CharacterController))]
    public class PlayerAI : AIPath
    {
        /** Minimum velocity for moving */
        public float sleepVelocity = 0.4F;

        /** Speed relative to velocity with which to play animations */
        public float animationSpeed = 0.2F;

        /** Effect which will be instantiated when end of path is reached.
         * see OnTargetReached */
        public GameObject endOfPathEffect;

        public new void Start()
        {
            //Call Start in base script (AIPath)
            base.Start();
        }

        /** Point for the last spawn of #endOfPathEffect */
        protected Vector3 lastTarget;

        public override void OnTargetReached()
        {
            if (endOfPathEffect != null && Vector3.Distance(tr.position, lastTarget) > 1)
            {
                GameObject.Instantiate(endOfPathEffect, tr.position, tr.rotation);
                lastTarget = tr.position;
            }
        }

        public override Vector3 GetFeetPosition()
        {
            return tr.position;
        }

        protected new void Update()
        {

            if (Input.GetMouseButtonDown(0))
            {
                RaycastHit hit;
                if (!Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100))
                {
                    return;
                }
                if (!hit.transform)
                {
                    return;
                }
                target.localPosition = hit.point;
            }

            //Get velocity in world-space
            Vector3 velocity;
            if (canMove)
            {
                //Calculate desired velocity
                Vector3 dir = CalculateVelocity(GetFeetPosition());

                //Rotate towards targetDirection (filled in by CalculateVelocity)
                RotateTowards(targetDirection);

                dir.y = 0;
                if (dir.sqrMagnitude > sleepVelocity * sleepVelocity)
                {
                    //If the velocity is large enough, move
                }
                else
                {
                    //Otherwise, just stand still (this ensures gravity is applied)
                    dir = Vector3.zero;
                }

                if (this.rvoController != null)
                {
                    rvoController.Move(dir);
                    velocity = rvoController.velocity;
                }
                else
                    if (navController != null)
                    {
#if FALSE
					navController.SimpleMove (GetFeetPosition(), dir);
#endif
                        velocity = Vector3.zero;
                    }
                    else if (controller != null)
                    {
                        controller.SimpleMove(dir);
                        velocity = controller.velocity;
                    }
                    else
                    {
                        Debug.LogWarning("No NavmeshController or CharacterController attached to GameObject");
                        velocity = Vector3.zero;
                    }
            }
            else
            {
                velocity = Vector3.zero;
            }
        }
    }
}

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

两种不同的脚本都能够实现寻路效果。

演示样例项目打包下载:

http://pan.baidu.com/s/1hsm6YNi


免责声明:文章转载自《A* Pathfinding Project (Unity A*寻路插件) 使用教程》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇oauth2bootstrap基本标签总结2下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

基于智能网卡(Smart Nic)的Open vSwitch卸载方案简介

SmartNic技术的初衷是以比普通CPU低得多的成本支持各种虚拟化功能,如sriov、overlay/decap和卸载一些vSwitch处理逻辑。目前,业界还没有完美的SmartNic解决方案来解决传统的vSwitch性能瓶颈,每种解决方案的实施方式也各不相同。没有统一的解决方案。图1.不同SmartNic架构的比较。2.基于SmartNic的OVS卸载方...

ABB机器人功能程序(FUNC)

功能程序的应用范围非常广泛。熟练的人员可以根据不同的需求创建相应的功能程序。函数程序的固定格式是FUNC,返回结束。在ABB的学习中,许多学生对功能程序几乎一无所知,即使他们真的在使用它。在学习ABB的过程中,我遇到了几个用例,所以我总结了它们以加深我的理解。...

移动端媒体查询的一些尺寸参考

device-width是设备实际的宽度,不会随着屏幕的旋转而改变,因此并不适合开发响应式网站。比如iphone5s的屏幕分辨率宽为640,由于retina显示策略,当scale设置为1的时候,对应的media中取到的width为320,当scale设置为0.5的时候,width为640,而device-width始终是320。总结1.device-widt...

转:(WIN)S04-CH01 PCIE XDMA开发环境搭建以及环路测试

XDMAIP使用部分教程分LINUX篇和WINDOWS篇两个部分。通过实战,面向应用,提供给大家XILINXFPGAPCIE应用解决方案。本教程以MK7160FA作为样机测试。这是一款高性价比的FPGA开发板。而M_AXI_LITE挂载的BRAM是需要进行BAR空间操作,所以地址设置为0x80000000...

部署springboot+vue项目文档(若依ruoyi项目部署步骤)

1: 部署Linux+nginx部署背景代码1.1因为我使用了idea工具进行开发,所以终端中的mvnclean包生成了相应的jar包。这个jar包可以在相应文件所在目录的目标中找到。linux服务器需要加载redis和nginx。redis存储缓存数据,nginx用于代理前端和后端服务。打包vue项目并将dist文件复制到tomcat的webapps目录中...

zabbix监控华为交换机

xmlversion=“1.0”encoding=“UTF-8”?...