zoukankan      html  css  js  c++  java
  • Unity3D学习笔记(十五):寻路系统

    动画生硬切换:animation.play();//极少使用,常用融合方法
    动画融合淡入:animation.CrossFade(“Idle”, 0.2f);//0.2f为与前一动画的融合百分比为20%
    枚举做状态机:
    AI系统 - 寻路系统:
    关键要素:
    1、寻路元件
    挂载寻路组件
    2、寻路平面(地形)
    地形设置成导航静态,进入预备役(以此为基准提取数据,可以修改删减,显示数据)

    打开Navigation窗口在Bake栏下点Bake

    烘焙数据:蓝色区域就是寻路平面,烘焙数据可以与显示数据分离

    设置障碍物:障碍物勾选Navigation Static,重新烘焙,障碍物被添加

    设置目标点,引用UnityEngine.AI,引用寻路系统

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;//引用UnityEngine.AI
    public enum EnemySta
    {
        Move,
        Idle,
        Death
    }
    public class EnemyControl : MonoBehaviour {
        public Animation anim;
        public EnemySta enemySta;
        //因为我们需要经常用到目标的状态,所以把TransForm改成了PlayerControl
        public PlayerControl moveTarget;//设置目标点
        public NavMeshAgent navMeshAgent;//引用寻路系统
        public MonsterManager manager;//怪物属于那个管理器
        private int enemyHealth = 30;//设置怪物生命值
        // Use this for initialization
        void Start () {
            enemySta = EnemySta.Move;
        }
           
           // Update is called once per frame
           void Update () {    
            if (Input.GetKeyDown(KeyCode.Alpha1))
            {
                enemySta = EnemySta.Move;
            }
            if (Input.GetKeyDown(KeyCode.Alpha2))
            {
                enemySta = EnemySta.Idle;
            }
            if (Input.GetKeyDown(KeyCode.Alpha3))
            {
                enemySta = EnemySta.Death;
            }
            Action();
        }
        void Action()
        {
            switch (enemySta)
            {
                case EnemySta.Move:
                    Move();
                    break;
                case EnemySta.Idle:
                    Idle();
                    break;
                case EnemySta.Death:
                    Death();
                    break;
            }
        }
        void Move()
        {
            anim.CrossFade("Move", 0.2f);
            navMeshAgent.SetDestination(moveTarget.transform.position);//让对应组件调用一个SetDastination(目标点)方法
            //如果怪物的目标死了
            if (moveTarget.playSta == PlaySta.Death)
            {
                //寻路停止
                navMeshAgent.isStopped = true;
                //状态切换到闲置
                enemySta = EnemySta.Idle;
            }
        }
        void Idle()
        {
            anim.CrossFade("Idle", 0.2f);
        }
        public void Damage()
        {
            //受伤减血
            enemyHealth -= 10;
            //Debug.Log("怪物生命"+enemyHealth);
            //如果血到了0
            if (enemyHealth <= 0)
            {
                //状态变成死亡状态
                enemySta = EnemySta.Death;//怪物死亡:停止寻路,失效碰撞体(防止实体造成伤害)
                navMeshAgent.isStopped = true;
                gameObject.GetComponent<MeshCollider>().enabled = false;
                //gameObject.tag = "Untagged";
            }
        }
        void Death()
        {
            anim.CrossFade("Death", 0.2f);
        }
        public void DeathEvent()
        {
            //在管理器列表中移除自己
            manager.monsterList.Remove(this);
            //销毁自己
            Destroy(this.gameObject);
        }
    }

    Max Slope:坡度设置
    Step Height:高度设置,低于高度可忽略
    Speed:移动速度
    Angular Speed:转向速度
    Acceleration:启动速度,加速度
    摄像机跟随
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class CameraFollow : MonoBehaviour {
        public Transform target;//目标点
        public Vector3 distance;//目标点的距离,和目标点保持一定距离
        public Vector3 lookAtOffSet;//看向点与目标点的偏移量
        private Vector3 targetPos;
        // Use this for initialization
        void Start () {
            distance = transform.position - target.position;
        }
           
           // Update is called once per frame
           void Update () {
          
        }
        private void LateUpdate()
        {
            //目标点加距离 = 摄像机的目标位置
            targetPos = target.position + distance;
            //向目标位置移动,每次百分之10
            transform.position = Vector3.Lerp(transform.position, targetPos, 0.1F);
            //看向目标点
            transform.LookAt(target.position + lookAtOffSet);
        }
    }
    lookAtOffSet:设置摄像机看向点的偏移量(点),避免人物总是在画面中央
    射击功能:
    核心功能(射线,碰撞,调用掉血方法)+交互功能(射线闪光特效,音效)
    空物体Fire
    ----音效Audio
    ----动画
    ----射线
    ----粒子特效
    --------子物体:Spotlight聚光灯,开闭,range变化

    设置灯光动画

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public enum PlaySta
    {
        Move,
        Idle,
        Death
    }
    public class PlayerControl : MonoBehaviour
    {
        public Animation anim;
        public PlaySta playSta;
        // Use this for initialization
        void Start()
        {
            playSta = PlaySta.Move;
        }
        // Update is called once per frame
        void Update()
        {
            float h = Input.GetAxis("Horizontal");
            float v = Input.GetAxis("Vertical");
            moveDir.x = h;
            moveDir.z = v;
            cd += Time.deltaTime;
            Action();
        }
        void Action()
        {
            switch (playSta)
            {
                case PlaySta.Move:
                    Move();
                    Attack();
                    Rot();
                    break;
                case PlaySta.Idle:
                    Idle();
                    Attack();
                    Rot();
                    break;
                case PlaySta.Death:
                    Death();
                    break;
            }
        }
        Vector3 moveDir;
        Vector3 lookPoint;
        RaycastHit hit;
        public float moveSpeed = 3;
        void Move()
        {
            anim.Play("Move");
            transform.position += moveDir * moveSpeed * Time.deltaTime;
            if (moveDir.magnitude < 0.01f)
            {
                playSta = PlaySta.Idle;
            }
        }
        //把转向功能提炼出来
        void Rot()
        {
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100, 1 << 8))
            {
                lookPoint = hit.point;
                lookPoint.y = transform.position.y;
                transform.LookAt(lookPoint);
            }
        }
        void Idle()
        {
            anim.Play("Idle");
            if (moveDir.magnitude >= 0.01f)
            {
                playSta = PlaySta.Move;
            }
        }
        float cd;
        public void Damage()
        {
            cd = 0;//无敌时间,受伤间隔
            playSta = PlaySta.Death;
        }
        void Death()
        {
            anim.Play("Death");
        }
        //触发器判定死亡
        //public void OnTriggerEnter(Collider other)
        //{
        //    if (other.tag == "Monster")
        //    {
        //        if (cd > 0.5f)
        //        {
        //            Damage();
        //        }
        //    }
        //}
        //改良版:碰撞器判定死亡
        public void OnCollisionEnter(Collision other)
        {
            if (other.gameObject.tag == "Monster")
                if (cd > 0.5f)
                {
                    Damage();
                }
        }
        public AudioSource fireAudio;
        public Animation fireAnim;
        public Transform firePos;
        public LineRenderer lineRenderer;
        private RaycastHit fireHit;
        void Attack()
        {
            if (Input.GetMouseButtonDown(0))
            {
                fireAudio.Play();
                fireAnim.Stop();//Stop避免前一动画未执行完,后一动画不会执行
                fireAnim.Play();
                if (Physics.Raycast(new Ray(firePos.position,firePos.forward),out fireHit,100))
                {
                    //如果我打中的目标是怪物
                    if (fireHit.collider.tag == "Monster")
                    {
                        //这个目标受伤
                        fireHit.collider.GetComponent<EnemyControl>().Damage();
                    }
                    SetFirePos(fireHit.point);
                   
                }
                else
                {
                    SetFirePos(firePos.transform.position + firePos.forward * 100);
                }
            }
        }
        void SetFirePos(Vector3 hitPos)
        {
            lineRenderer.SetPosition(0, firePos.position);
            lineRenderer.SetPosition(1, hitPos);
        }
        //还原射线到枪口位置
        public void ReSetFirePos()
        {
            lineRenderer.SetPosition(0, firePos.position);
            lineRenderer.SetPosition(1, firePos.position);
        }
    }

    ----射线

    positions默认世界位置
    Element 0:枪口位置
    Element 1:目标点位置
    Materials射线材质:LineRenderMaterial
    width射线宽度:
    SetPosition方法:0,1参数是element0,element1
    射线关联:
    枪口位置,绑定在骨骼的武器位上
    如果没有,就放在正前方上0.15,0.3,0.7
    重置射线:
    设置在动画的第10帧,调用消失方法

    新建脚本FireEvent,调用父级Player的脚本

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class FireEvent : MonoBehaviour {
        public PlayerControl player;
           // Use this for initialization
           void Start () {
                  
           }
           
           // Update is called once per frame
           void Update () {
                  
           }
        public void Fire()
        {
            player.ReSetFirePos();
        }
    }

    射线宽度调整:添加关键帧,调整曲线,效果先细后粗

    怪物管理器;
    添加空物体和脚本类,生成点
    给怪物管理器添加预制体的引用
    注意:对象在场景中和类在资源中的区别

    怪物属于那个管理器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    public class MonsterManager : MonoBehaviour {
        List<Transform> birthPoint;
        public GameObject monsterPreGo;
        public List<EnemyControl> monsterList;
        public int MaxMonsterCount = 3;//最大怪物数
        public PlayerControl player;
        // Use this for initialization
        void Start () {
            birthPoint = new List<Transform>();
            monsterList = new List<EnemyControl>();
            for (int i = 0; i < transform.childCount; i++)3
            {
                birthPoint.Add(transform.GetChild(i));
            }
        }
           
           // Update is called once per frame
           void Update () {
        }
        private void FixedUpdate()
        {
            CheckMonster();
        }
        void CreateMonster()
        {
            //实例化一个怪物
            GameObject go = Instantiate<GameObject>(monsterPreGo);
            //怪物的位置设置任意出生点
            go.transform.position = birthPoint[Random.Range(0,birthPoint.Count)].position;
            //取得怪物脚本
            EnemyControl monster = go.GetComponent<EnemyControl>();
            //把怪物放到管理器列表里
            monsterList.Add(monster);
            //指定怪物的目标
            monster.moveTarget = player;
            //指定怪物的管理者
            monster.manager = this;
        }
        void CheckMonster()
        {   
            if (monsterList.Count < MaxMonsterCount)
            {
                CreateMonster();
            }
        }
    }
    角色穿墙问题:
    Drag=1防止被墙弹飞,XZ轴解锁

    怪物死亡:移出列表,摧毁游戏物体,放在死亡动画最后一帧调用

    public void DeathEvent()
    {
       //在管理器列表中移除自己
       manager.monsterList.Remove(this);
       //销毁自己
       Destroy(this.gameObject);
    }

    更换场景,重新烘焙,并添加floor和碰撞体

    怪物管理器添加积分系统

  • 相关阅读:
    Unity《ATD》塔防RPG类3D游戏架构设计(一)
    计算机网络基础笔记 运输层协议UDP/TCP
    空间划分的数据结构(四叉树/八叉树/BVH树/BSP树/k-d树)
    游戏设计模式——内存池管理
    Unity 用ml-agents机器学习造个游戏AI吧(2) (深度强化学习入门DEMO)
    博客部署设计和构建
    教你如何把浏览器变为浏览“神器”
    珍藏多年的学习资料300G+,赶紧免费领取,从此离大神更进一步(文末有彩蛋)
    图解一致性哈希算法,全网(小区局域网)最通俗易懂
    SpringBoot整合Mail发送邮件&发送模板邮件
  • 原文地址:https://www.cnblogs.com/vuciao/p/10363049.html
Copyright © 2011-2022 走看看