moba类游戏开发日志(九)Monster自动寻路/动态修改target优先级

这次的文章里又出现了一个新东西,自动寻路,这个东西怎么实现呢?

在unity中有一个组件叫nav mesh agent,照字面意思来看就是网格导航代理,照我自己的理解简单点说就是挂上了这玩意儿的那个东西能够自动且平滑得移动到我们给他的那个target的position,前提是区域平(曲)面已经经过bake

经过了bake之后的区域在scene中会变成蓝色,具体bake的范围这个主要和max slope有关系,bake之后蓝色的区域即为导航角色可以通过的区域

我还没有看过navigation具体实现的源码,但是吧从他这个nav mesh agent的字面上我估计他就是将地图网格化,也就是变成一块块的,抽象成一个邻接矩阵,然后bake的过程就是用dijkstra或者优化了估价函数调整了loss系数的A*去预处理点集的distance关系集

上面都是臆测,有空还是看看源码比较好

接着我们回到寻路-修改target优先级这一目标之上

nav mesh agent组件中它自带了priority参数,无论从控制逻辑还是UI中获取/修改这个参数都可以达到修改target优先级这一目的

至于什么时候去修改呢,这个还是那句老话,看需求

打个比方这边我的需求是monster在导航过程中距离箭塔2个单位便进入attack state,但是如果在导航途中monster周围的5个单位之内出现了我们控制的主角便将主角的优先级提升至箭塔之前,这个需求我自己还是通过碰撞检测来实现的,具体操作还是在 OnTriggerEnter 里写

最近学习客户端及u3d的一些知识最大的感受就是可以根据自己的需求自由的调整代码了,而不是拘于api的重复调用,主要还是因为这三五天时间里熟悉了常用的一些component及其parameter吧

到现在越发的觉得一段好的代码不应像算法竞赛中一样我们大家在满足需求的前提下拼命去压行。一段可读性好、架构优秀且不必因为后来添加的需求而重构的源码才是好代码

最后挂下自己写的soldier类的代码吧,部分功能耦合性略高,有空接着改吧,到时候就不更新到博文了,愿意赏脸看一眼的欢迎来我的github主页

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SmartSoldier : MonoBehaviour
{
    private UnityEngine.AI.NavMeshAgent nav;
    private Animator ani;
    private float distance;
    public Transform target;

    public Transform[] towers;

    private Health HP;

    private List<Transform> enemyList = new List<Transform>();


    // Start is called before the first frame update
    void Start()
    {
        HP = GetComponent<Health>();
        nav = GetComponent<UnityEngine.AI.NavMeshAgent>();
        ani = GetComponent<Animator>();
        target = GetTarget();
        // print(target.position.x);
        // print(target.position.y);
        // print(target.position.z);
    }

    // Update is called once per frame
    void Update()
    {
        SoldierMove();
    }

    void OnTriggerEnter(Collider col) 
    {
        if(col.gameObject.tag == "player")
        {
            this.enemyList.Add(col.transform);
            Transform temp = this.enemyList[0];
            if(target == null || target != temp)
            {
                target = temp;
            }
        }
    }

    void OnTriggerExit(Collider col) 
    {
        if(this.enemyList.Contains(col.transform))
        {
            this.enemyList.Remove(col.transform);
            target = GetTarget();
        }    
    }
    void SoldierMove()
    {
        if(target == null)
        {
            target = GetTarget();
            return ;
        }
        // print(target.position.x);
        // print(target.position.y);
        // print(target.position.z);
        nav.SetDestination(target.position);
        distance = Vector3.Distance(transform.position, target.position);
        if(distance > 2) ani.Play("ghoul_run");
        // print(distance);
        if(distance > 2)
        {
            nav.speed = 3.5f;
        }
        else 
        {
            // print("233333333333");
            nav.speed = 0;
            Vector3 tarPos = target.position;
            Vector3 lookPos = new Vector3(tarPos.x,transform.position.y,tarPos.z);
            transform.LookAt(lookPos);
            // ani.SetBool("isATK",true);
            ani.Play("ghoul_atk");
        }
    }


    public void Attack()
    {
        // print("999999999999999");
        // target = GetTarget();
        if(target == null)
        {
            // print("66666666");
            target = GetTarget();
            return ;
        }
        // var name = target.GetComponent<Health>().name;
        // print("name");
        Health heath = target.GetComponent<Health>();
        float damage = Random.Range(0.01f,0.02f);
        heath.TakeDamage(damage);
        if(heath.hp.Value <= 0)
        {
            // if()
            Destroy(target.gameObject);

        }
    }
    public void TakeDamage(float damage)
    {
        HP.hp.Value -= damage;
        if(HP.hp.Value <= 0)
        {
            Destroy(gameObject);
        }
    }

    Transform GetTarget()
    {
        for(int i = 0;i < towers.Length;i++)
        {
            if(towers[i] != null) 
            {
            // print(tower.position.x);
            // print(tower.position.y);
            // print(tower.position.z);
                return towers[i];
            }
        }
        return null;
    }
}