10.寻路优化
10.1 知识点
目前存在的问题:当大角度翻转移动时,士兵会进行穿插。希望的效果是士兵后阵变前阵,尽量不要穿插。
解决思路:判断当前方向向量和士兵朝向的夹角,如果夹角大于60度,就要对士兵列表重新排序。排序规则不仅要依据兵种,还要计算各个士兵和目标值的距离,距离近的放在前面。
修改控制士兵移动函数。判断新朝向和老朝向夹角是否大于60度。是的话对士兵列表重新排序,优先计算兵种,再计算士兵和目标点的距离。
/// <summary>
/// 控制士兵移动
/// </summary>
private void ControlSoldiersMove()
{
if(Input.GetMouseButtonDown(1))
{
//...
//获取目标点 通过射线检测
if( Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo, 1000, 1 << LayerMask.NameToLayer("Ground")) )
{
//判断队伍新朝向和队伍老朝向之间的夹角
//新朝向:(hitInfo.point - soldierObjs[0].transform.position).normalized
//老朝向:soldierObjs[0].transform.forward 我们把之前的第一个士兵的面朝向作为了 阵型老朝向
//两个朝向之间的夹角大于60度 我们就对士兵列表重新进行排序
if( Vector3.Angle((hitInfo.point - soldierObjs[0].transform.position).normalized, soldierObjs[0].transform.forward) > 60 )
{
//满足条件后 重新进行士兵的排序
//优先 兵种排序
//其次 才是 距离排序
soldierObjs.Sort((a, b) => {
//优先兵种排序
if (a.type < b.type)
return -1;
else if (a.type == b.type)
{
//只有兵种相同时 才会以离目标点距离进行排序
if (Vector3.Distance(a.transform.position, hitInfo.point) <=
Vector3.Distance(b.transform.position, hitInfo.point))
return -1;
else
return 1;
}
else
return 1;
});
}
//...
}
}
}
10.2 知识点代码
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));
}
}
/// <summary>
/// 控制士兵移动
/// </summary>
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")) )
{
//判断队伍新朝向和队伍老朝向之间的夹角
//新朝向:(hitInfo.point - soldierObjs[0].transform.position).normalized
//老朝向:soldierObjs[0].transform.forward 我们把之前的第一个士兵的面朝向作为了 阵型老朝向
//两个朝向之间的夹角大于60度 我们就对士兵列表重新进行排序
if( Vector3.Angle((hitInfo.point - soldierObjs[0].transform.position).normalized, soldierObjs[0].transform.forward) > 60 )
{
//满足条件后 重新进行士兵的排序
//优先 兵种排序
//其次 才是 距离排序
soldierObjs.Sort((a, b) => {
//优先兵种排序
if (a.type < b.type)
return -1;
else if (a.type == b.type)
{
//只有兵种相同时 才会以离目标点距离进行排序
if (Vector3.Distance(a.transform.position, hitInfo.point) <=
Vector3.Distance(b.transform.position, hitInfo.point))
return -1;
else
return 1;
}
else
return 1;
});
}
//通过目标点 计算出 真正的 阵型目标点
//计算目标点
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