zoukankan      html  css  js  c++  java
  • A* Pathfinding Project (Unity A*寻路插件) 使用教程

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

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

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

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

    我也一直都没有去看……

    转自http://blog.csdn.net/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.net/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 到相应的位置,就完毕了寻路。


    以下是完整代码:

    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








  • 相关阅读:
    java socket知识点
    java io流缓冲理解
    如何理解java回电话
    它们的定义Adapterg在etView( )正在使用View.setTag()与不同的是不使用。
    打造你自己ajax上传图片
    POJ 1166 The Clocks
    spring-framework-3.2.4.RELEASE 综合hibernate-release-4.3.5.Final一个错误Caused by: java.lang.NoClassDefFound
    [Android]Volley源代码分析(店)应用
    金融脱媒砸进?
    音乐家演奏乐器
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/7156587.html
Copyright © 2011-2022 走看看