网格导航综合练习

前言:在学习完网格导航之后,我们做一个涉及到网格导航的综合练习,涉及到前面学习的动画以及角色控制器等内容,废话不多说,开始吧


综合练习
1、路面设置障碍
2、鼠标点击路面,角色可以移动过去,同时播放动画,期间有障碍的话会躲过去,到达目的地之后角色会播放Idle的动画
3、WASD也可以控制角色的移动,同时要播放动画,不能穿过障碍,松开按键之后播放Idle的动画
4、按WASD可以终止点击鼠标后角色的自动寻路,按照WASD的控制去移动
提示:
1、射线
2、动画(Animator)
3、
自动寻路
自动寻路
终止寻路
重新开始寻路

场景搭建

  • 创建一个新的场景,命名为“网格导航练习”

  • 搭建地形

    • 新建一个Plane
    • Transform组件点击右上角的小齿轮>>Reset
    • Scale改为(5,1,5)
    • 给它一个材质


    • 再克隆4个Plane,调整合适的Position


  • 设置障碍物

    • 创建一个Cube,Transform组件Reset
    • 调整一个合适的Scale
    • 添加网格导航障碍组件(网格导航代理不能移动到此位置)
      • 选中Cube,Add Component>>Navigation>>Nav Mesh Obstacle


    • 再克隆3个Cube,调整合适的Position


  • 模型设置

    • 把模型拖入场景
    • 选中模型的导入设置文件,Model>>调整合适的Scale Factor(缩放因子)


    • 点击Apply


烘焙网格导航路径

  • 选中所有地形对象,在编辑器的菜单栏找到Window>>Navigation


  • Navigation面板>>Object>>Navigation Static勾选


  • Bake>>调整合适的值>>点击Bake


添加动画

  • 创建Avatar(映射关系)
    • 选中模型,Rig>>Animation Type:Humanoid,Avatar Definition:Create From This Model
    • 点击Apply
  • 给所有动画剪辑添加Avatar
    • 选中需要添加Avatar的动画剪辑
    • Rig>>Animation Type:Humanoid,Avatar Definition:Copy From Other Avatar
    • Source:刚刚创建的模型的Avatar
    • 点击Apply
  • 添加动画控制器

    • 双击打开,拖入Idle(原地不动)和Walk(走)的动画剪辑
      • Idle设为默认过渡线,从Idle设置过渡线到Walk,从Walk设置过渡线到Idle


      • Parameters>>添加>>Bool,重命名为“IsWalk”


    • 设置过渡条件
      • 选中Idle到Walk的过渡线,点击加号(添加过渡条件);找到IsWalk(参数变量名字),将值设为true;Has Exit Time(是否等待上一个动画剪辑播放完毕,再去播放下一个动画剪辑)不勾选


      • 同理:选中Walk到Idle的过渡线,点击加号;找到IsWalk,将值设为false;Has Exit Time不勾选
    • 设置循环
      • 分别选中Idle和Walk的导入文件,将Loop Time勾选,这样动画剪辑就是循环播放


  • 选中模型,Animator组件>>Controller:动画控制器


  • 取消应用根动作(不然动画剪辑自身也会有一个偏移量,会造成模型和角色控制器位置的不匹配)


添加一个空物体

  • 重命名为“Player”
  • 把Transform组件Reset
  • 把模型设为Player的子物体,Transform组件Reset

添加网格导航代理件

  • 选中Player,Add Component>>Navigation>>Nav Mesh Agent
  • 调整半径和高度,让其跟模型大小匹配


添加角色控制器

  • 选中Player,Add Component>>Physics>>Character Controller
  • 调整半径和高度,让其和网格导航代理相同
  • 调整Center的位置,让其和模型匹配


  • 圆柱体的是网格导航代理,胶囊体的是角色控制器


添加脚本

  • 选中Player,Add Component>>New Script
  • Name:PlayerMoveController
  • Create and Add



    脚本PlayerMoveController代码:

  • 这里只需要关注通过WSAD键控制Player移动以及通过鼠标点击地面控制Player移动即可
  • 其他代码有可能需要进行相应处理,新手的话建议先不看
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;//人工智能的命名空间

public class PlayerMoveController : MonoBehaviour {

    Vector3 Vec_Dir;
    float hor;
    float ver;

    public float MoveSpeed;
    public float RotaSpeed;
    public float JumpSpeed;
    public float MouseScrollSpeed;
    float G=0.5F;
    //角色控制器
    CharacterController CC;
    //动画控制器
    Animator m_Anima;

    //网格导航代理
    private NavMeshAgent mNMA;
    //用于得到射线检测信息
    RaycastHit hit;

    bool mIsWalk;
    bool mIsRun;
    Vector3 pos_mouse;
    float fov;
    Vector3 pos_cam;
    //public  Camera cam;
    void Start () {
        //得到自身的角色控制器
        CC= GetComponent<CharacterController>();
        //得到子物体的动画控制器
        m_Anima = GetComponentInChildren<Animator>();
        //cam = GetComponentInChildren<Camera>();

        //得到NavMeshAgent组件
        mNMA = GetComponent<NavMeshAgent>();
    }

    //判断角色控制器是否在地面上
    public bool IsGround()
    {
        //创建从角色控制器中心点向下的一条射线
        Ray ray = new Ray(CC.center,-Vector3.up);
        //返回射线检测是否碰到物体
        return Physics.Raycast(ray,CC.height/2);

    }
    private void FixedUpdate()
    {
        //角色控制器在地面上
        if (IsGround())
        {
            //按空格跳
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Vec_Dir.y = JumpSpeed;
                //跳的动画
                m_Anima.SetTrigger("Jump");
            }
        }


    }
    void Update () {
        //增量
        hor = Input.GetAxis("Horizontal");
        ver = Input.GetAxis("Vertical");

        #region 鼠标控制移动
        //点击鼠标左键
        if (Input.GetMouseButtonDown(0))
        {
            //得到鼠标位置
            pos_mouse = Input.mousePosition;
            //调用方法:玩家跟随鼠标位置
            PlayWithMousePoint(pos_mouse);

        }
        #endregion
        //运行时会自动判断,将标志位设为false;
        //使用同一个变量的时候一定要注意先后顺序
        #region 通过导航管理的期望速度控制动画标志位
        if (mNMA.desiredVelocity != Vector3.zero)
        {
            mIsWalk = true;
        }
        else
        {

            //刚进入游戏时
            mIsWalk = false;
        }
        #endregion
        #region 通过WSAD、Shift控制动画标志位以及导航
        //按下WSAD
        if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
        {
            mIsWalk = true;
            //关闭导航
            mNMA.isStopped = true;
        }
        //松开WSAD
        if (Input.GetKeyUp(KeyCode.W) || Input.GetKeyUp(KeyCode.S) || Input.GetKeyUp(KeyCode.A) || Input.GetKeyUp(KeyCode.D))
        {
            mIsWalk = false;
        }

        //按下Shift,速度乘以2
        if (Input.GetKey(KeyCode.LeftShift))
        {
            Vec_Dir *= 2;
            mIsRun = true;
        }
        //松开Shift
        if (Input.GetKeyUp(KeyCode.LeftShift))
        {
            mIsRun = false;
        }
        #endregion
        #region 通过Transform移动
        //移动,转动
        //transform.Translate(0, 0, ver * MoveSpeed * Time.deltaTime);
        //transform.Rotate(0, hor * RotaSpeed * Time.deltaTime, 0);
        #endregion

        #region 通过角色控制器移动
        //移动
        //世界坐标
        Vec_Dir = new Vector3(0, 0, ver*MoveSpeed*Time.deltaTime);
        //转化成自身坐标
        Vec_Dir = transform.TransformDirection(Vec_Dir);

        //重力
        Vec_Dir.y -= G;
        CC.Move(Vec_Dir);
        //转动
        transform.Rotate(0, hor * RotaSpeed * Time.deltaTime, 0);

       
        #endregion

       

        #region ZXCV技能
        //ZXCV技能
        if (Input.GetKeyDown(KeyCode.Z))
        {
            m_Anima.SetTrigger("Q");
        }
        if (Input.GetKeyDown(KeyCode.X))
        {
            m_Anima.SetTrigger("W");
        }
        if (Input.GetKeyDown(KeyCode.C))
        {
            m_Anima.SetTrigger("E");
        }
        if (Input.GetKeyDown(KeyCode.V))
        {
            m_Anima.SetTrigger("R");
        }
        #endregion



        //播放走、跑的动画
        m_Anima.SetBool("IsWalk",mIsWalk);
        //我在状态机中没有写参数IsRun,如果你写了可以把注释取消掉
        //m_Anima.SetBool("IsRun",mIsRun);

    }

    private void LateUpdate()
    {
        //鼠标滑轮调整摄像机远近
        fov = Camera.main.fieldOfView;
        fov+= Input.GetAxis("Mouse ScrollWheel")*MouseScrollSpeed;
        fov = Mathf.Clamp(fov,0.3f,1000);
        Camera.main.fieldOfView = fov;

        //pos_mouse= Camera.main.ScreenToViewportPoint(pos_mouse);
        //pos_mouse= Camera.main.ViewportToWorldPoint(pos_mouse);
       
        //限制摄像机位置范围
        pos_cam = Camera.main.transform.position;
        pos_cam.x= Mathf.Clamp(pos_cam.x, -100, 100);
        pos_cam.y = Mathf.Clamp(pos_cam.y,-100,100);
        Camera.main.transform.position = pos_cam;
        //到屏幕边缘移动摄像机
        if (pos_mouse.x <= 0) pos_cam.x--;
        if (pos_mouse.x >= Screen.width) pos_cam.x++;
        if (pos_mouse.y <= 0) pos_cam.y--;
        if (pos_mouse.y >= Screen.height) pos_cam.y++;
    }

    /// <summary>
    /// 玩家跟随鼠标移动
    /// </summary>
    /// <param name="pos_mouse">鼠标位置</param>
    void PlayWithMousePoint(Vector3 pos_mouse)
    {
        //将鼠标点击位置转换成射线
        Ray ray = Camera.main.ScreenPointToRay(pos_mouse);
        //如果射线碰到了游戏对象
        if (Physics.Raycast(ray, out hit))
        {
            //打开导航
            mNMA.isStopped = false;
            //将碰到的游戏对象的位置
            //赋给 网格导航代理的终点位置
            mNMA.destination = hit.point;
           
        } 
    }
}

设置脚本参数

  • 将脚本参数中的移动速度和角速度设置和网格导航代理中的移动速度和角速度一样


进行到这里,我们的题目就算是完成了,赶紧点开运行试着通过鼠标和键盘WSAD去移动Player吧


最后:网格导航综合练习的内容就到这里了,希望通过这次练习能够让你更加熟练的使用网格导航部分的学习内容,同时也要记得复习前面的动画部分内容,达到所谓“温故而知新”,下次再见~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341