9.阵型布局优化

9.阵型布局优化


9.1 知识点

定义士兵类型枚举。注意前排英雄枚举值越小,后排英雄枚举值越大。

public enum SoldierType
{
    Hero,       //英雄
    Warrior,    //战士
    Archer,     //猎人
    Magician,   //魔法师
    Loong,      //龙
}

在士兵对象脚本定义士兵类型变量,在外部修改并覆盖到预制体

public class SoldierObj : MonoBehaviour
{
    //士兵的类型
    public SoldierType type;
    // ...
}


在SelSoldiers选择士兵方法中抬起鼠标时对士兵列表进行排序。枚举值越小就越前面。因为前面的返回的是阵型前排的位置

/// <summary>
/// 选择士兵方法
/// </summary>
private void SelSoldiers()
{
    if (Input.GetMouseButtonDown(0))
    {
        // ...
    }
    else if(Input.GetMouseButtonUp(0))
    {
        // ...

        //我们根据兵种的类型进行排序
        //对应类型值晓得 放到前面 否则 放到后面
        soldierObjs.Sort((a, b)=> {
            if (a.type < b.type)
                return -1;
            else if (a.type == b.type)
                return 0;
            else
                return 1;
        });
    }
    // ...
}

9.2 知识点代码

SoldierObj

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

public enum SoldierType
{
    Hero,       //英雄
    Warrior,    //战士
    Archer,     //猎人
    Magician,   //魔法师
    Loong,      //龙
}

public class SoldierObj : MonoBehaviour
{
    //动画的切换
    private Animator animator;
    //移动方法
    private NavMeshAgent agent;
    //设置是否处于选中状态
    private GameObject footEffect;

    //士兵的类型
    public SoldierType type;
    
    void Start()
    {
        animator = this.GetComponentInChildren<Animator>();
        agent = this.GetComponent<NavMeshAgent>();
        footEffect = this.transform.Find("FootEffect").gameObject;

        SetSelSelf(false);
    }
    
    void Update()
    {
        //根据当前的移动速度 决定动画时 待机 还是移动
        animator.SetBool("IsMove", agent.velocity.magnitude > 0);
    }

    //移动方法 传入目标点即可
    public void Move(Vector3 pos)
    {
        agent.SetDestination(pos);
    }

    /// <summary>
    /// 设置自己是否被选中 决定光圈是否显示
    /// </summary>
    /// <param name="isSel"></param>
    public void SetSelSelf(bool isSel)
    {
        footEffect.SetActive(isSel);
    }
}

Controller

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

public class Controller : MonoBehaviour
{
    //鼠标左键是否按下
    private bool isMouseDown = false;
    
    //获取LineRenderer组件 用于绘制线段
    private LineRenderer line;
    
    //屏幕上的四个点 用于绘制矩形
    //鼠标按下时 记录的当前鼠标位置
    private Vector3 leftUpPoint;
    private Vector3 rightUpPoint;
    private Vector3 leftDownPoint;
    private Vector3 rightDownPoint;

    //射线检测获取到的信息
    private RaycastHit hitInfo;
    private Vector3 beginWorldPos;
    //选择到的士兵对象 存储在该容器
    private List<SoldierObj> soldierObjs = new List<SoldierObj>();

    //上一次鼠标点击的位置
    private Vector3 frontPos = Vector3.zero;

    //士兵之间的间隔距离
    private float soldierOffset = 3;
    
    void Start()
    {
        line = this.GetComponent<LineRenderer>();
    }
    
    void Update()
    {
        //选择士兵逻辑处理
        SelSoldiers();

        //控制士兵移动
        ControlSoldiersMove();
    }

    /// <summary>
    /// 选择士兵方法
    /// </summary>
    private void SelSoldiers()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //记录鼠标当前位置
            leftUpPoint = Input.mousePosition;
            isMouseDown = true;
            
            //通过射线检测 得到地面上的点 之后 用于 范围检测
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo, 1000, 1 << LayerMask.NameToLayer("Ground")))
                beginWorldPos = hitInfo.point;
        }
        else if(Input.GetMouseButtonUp(0))
        {
            isMouseDown = false;
            //将线段的点设置为0个 就不会去绘制
            line.positionCount = 0;

            //每一次重新选择士兵时  我们都把记录的上一个位置清空
            frontPos = Vector3.zero;

            //清空选择
            for (int i = 0; i < soldierObjs.Count; i++)
                soldierObjs[i].SetSelSelf(false);
            soldierObjs.Clear();

            //选择当前的对象
            //通过射线检测 得到地面上的另一个点 用于 范围检测
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo, 1000, 1 << LayerMask.NameToLayer("Ground")))
            {
                //范围检测用的中心点 (由于 摄像机 朝向 是世界坐标系正方向 没有旋转 所以我们可以这样做)
                Vector3 center = new Vector3((hitInfo.point.x + beginWorldPos.x) / 2, 1, (hitInfo.point.z + beginWorldPos.z) / 2);
                
                //范围检测 盒装的 长宽高的一半
                Vector3 halfExtents = new Vector3(Mathf.Abs(hitInfo.point.x - beginWorldPos.x) / 2, 1, Mathf.Abs(hitInfo.point.z - beginWorldPos.z) / 2);
                
                //得到 这个盒装范围内的 所有碰撞器
                Collider[] colliders = Physics.OverlapBox(center, halfExtents);
                //soldierObjs.Count < 12 是 设置的 选择上限 最多只能选择12个士兵
                for (int i = 0; i < colliders.Length && soldierObjs.Count < 12; i++)
                {
                    SoldierObj obj = colliders[i].GetComponent<SoldierObj>();
                    if (obj != null)
                    {
                        obj.SetSelSelf(true);
                        soldierObjs.Add(obj);
                    }
                }

                //我们根据兵种的类型进行排序
                //对应类型值晓得 放到前面 否则 放到后面
                soldierObjs.Sort((a, b)=> {
                    if (a.type < b.type)
                        return -1;
                    else if (a.type == b.type)
                        return 0;
                    else
                        return 1;
                });
            }
        }

        //当鼠标左键处于按下状态时
        //就在这里面去处理 线段绘制的逻辑
        if( isMouseDown)
        {
            //注意:目前我们获取的位置 是屏幕坐标系的位置
            //设置屏幕上的4个点
            leftUpPoint.z = 5;
            
            rightDownPoint = Input.mousePosition;
            rightDownPoint.z = 5;

            rightUpPoint.x = rightDownPoint.x;
            rightUpPoint.y = leftUpPoint.y;
            rightUpPoint.z = 5;

            leftDownPoint.x = leftUpPoint.x;
            leftDownPoint.y = rightDownPoint.y;
            leftDownPoint.z = 5;

            //设置线段画线的世界坐标系的点
            line.positionCount = 4;
            line.SetPosition(0, Camera.main.ScreenToWorldPoint(leftUpPoint));
            line.SetPosition(1, Camera.main.ScreenToWorldPoint(rightUpPoint));
            line.SetPosition(2, Camera.main.ScreenToWorldPoint(rightDownPoint));
            line.SetPosition(3, Camera.main.ScreenToWorldPoint(leftDownPoint));
        }
    }

    private void ControlSoldiersMove()
    {
        if(Input.GetMouseButtonDown(1))
        {
            //没有士兵不移动
            if (soldierObjs.Count == 0)
                return;

            //获取目标点 通过射线检测
            if( Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo, 1000, 1 << LayerMask.NameToLayer("Ground")) )
            {
                //通过目标点 计算出 真正的 阵型目标点
                //计算目标点
                List<Vector3> targetsPos = GetTargetPos(hitInfo.point);
                //命令士兵朝向各自的目标点 移动
                for (int i = 0; i < soldierObjs.Count; i++)
                    soldierObjs[i].Move(targetsPos[i]);
            }
        }
    }

    /// <summary>
    /// 根据鼠标点击的目标点 计算出 阵型的其它点位
    /// </summary>
    /// <param name="targetPos"></param>
    /// <returns></returns>
    private List<Vector3> GetTargetPos(Vector3 targetPos)
    {
        //需要计算目标点 的 面朝向和 右朝向
        Vector3 nowForward = Vector3.zero;
        Vector3 nowRigth = Vector3.zero;

        //是一批士兵 上一次已经移动过一次了 有上一次的位置
        if(frontPos != Vector3.zero)
            nowForward = (targetPos - frontPos).normalized;//有上一次的点 就直接计算
        else//没有上一次的点 就用第一个士兵的位置 作为上一次的点来计算
            nowForward = (targetPos - soldierObjs[0].transform.position).normalized;
        //根据面朝向 得到右朝向 旋转y轴 90度
        nowRigth = Quaternion.Euler(0, 90, 0) * nowForward;

        List<Vector3> targetsPos = new List<Vector3>();

        switch (soldierObjs.Count)
        {
            case 1:
                targetsPos.Add(targetPos);
                break;
            case 2:
                targetsPos.Add(targetPos + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowRigth * soldierOffset / 2);
                break;
            case 3:
                targetsPos.Add(targetPos);
                targetsPos.Add(targetPos + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowRigth * soldierOffset);
                break;
            case 4:
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2 - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2 + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2 - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2 + nowRigth * soldierOffset / 2);
                break;
            case 5:
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2 - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2 + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2 - nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2 + nowRigth * soldierOffset);
                break;
            case 6:
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2 - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset / 2 + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2 - nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2 + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset / 2);
                break;
            case 7:
                targetsPos.Add(targetPos + nowForward * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowRigth * soldierOffset);
                targetsPos.Add(targetPos);
                targetsPos.Add(targetPos - nowForward * soldierOffset);
                break;
            case 8:
                targetsPos.Add(targetPos + nowForward * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowRigth * soldierOffset);
                targetsPos.Add(targetPos);
                targetsPos.Add(targetPos - nowForward * soldierOffset - nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset + nowRigth * soldierOffset);
                break;
            case 9:
                targetsPos.Add(targetPos + nowForward * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowRigth * soldierOffset);
                targetsPos.Add(targetPos + nowRigth * soldierOffset);
                targetsPos.Add(targetPos);
                targetsPos.Add(targetPos - nowForward * soldierOffset - nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset + nowRigth * soldierOffset);
                targetsPos.Add(targetPos - nowForward * soldierOffset);
                break;
            case 10:
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowForward * soldierOffset - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowForward * soldierOffset + nowRigth * soldierOffset * 1.5f);
                break;
            case 11:
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowForward * soldierOffset - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowForward * soldierOffset + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowForward * soldierOffset);
                break;
            case 12:
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowForward * soldierOffset - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos + nowForward * soldierOffset + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos + nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowForward * soldierOffset - nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowForward * soldierOffset + nowRigth * soldierOffset * 1.5f);
                targetsPos.Add(targetPos - nowForward * soldierOffset - nowRigth * soldierOffset / 2);
                targetsPos.Add(targetPos - nowForward * soldierOffset + nowRigth * soldierOffset / 2);
                break;
        }

        //计算完毕后  记录当前次的位置 
        frontPos = targetPos;

        return targetsPos;
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏