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变化

设置灯光动画

扫描二维码关注公众号,回复: 5160810 查看本文章

 

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和碰撞体

怪物管理器添加积分系统

猜你喜欢

转载自www.cnblogs.com/ciaowoo/p/10363049.html