6.选中士兵
6.1 知识点
选中士兵需求
- 选择框范围内选中的士兵要显示特效并记录到列表中
实现方法分析
射线检测+范围检测(使用这一种)
从选择框左上角和右下角发射射线到地面,记录相交的两点。通过这两点和范围检测生成盒型碰撞器进行检测。
世界坐标转屏幕坐标
把所有士兵的世界坐标转成屏幕坐标,判断是否在选择框屏幕坐标范围内。逻辑上没有问题,但是每个对象都要实时更新屏幕位置,而且要遍历所有对象,代码上稍显麻烦。
射线检测+位置判断
根据射线交互的两点的位置自己手写判断士兵位置是否在范围内。
把地面预制体层级改成 Ground
在 Controller 定义射线信息,开始点世界坐标位置,当前选择的士兵对象容器变量
// 射线检测获取到的信息
private RaycastHit hitInfo;
private Vector3 beginWorldPos;
// 选择到的士兵对象 存储在该容器
private List<SoldierObj> soldierObjs = new List<SoldierObj>();
把 Controller 在 Update 的逻辑封装成 SelSoldiers 选择士兵方法。鼠标按下时发出射线计算开始点世界坐标。鼠标抬起时清空上一次选择的士兵对象容器,且或者抬起时的世界坐标。通过两个坐标进行盒型范围检测,得到士兵脚本设置选择特效显示,选中的士兵脚本添加到选择的士兵对象容器。
void Update()
{
// 选择士兵逻辑处理
SelSoldiers();
}
/// <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;
// 清空选择
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);
for (int i = 0; i < colliders.Length; i++)
{
SoldierObj obj = colliders[i].GetComponent<SoldierObj>();
if (obj != null)
{
obj.SetSelSelf(true);
soldierObjs.Add(obj);
}
}
}
}
// 当鼠标左键处于按下状态时
// 就在这里面去处理 线段绘制的逻辑
if( isMouseDown)
{
// ...
}
}
6.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>();
void Start()
{
line = this.GetComponent<LineRenderer>();
}
void Update()
{
//选择士兵逻辑处理
SelSoldiers();
}
/// <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;
//清空选择
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);
for (int i = 0; i < colliders.Length; i++)
{
SoldierObj obj = colliders[i].GetComponent<SoldierObj>();
if (obj != null)
{
obj.SetSelSelf(true);
soldierObjs.Add(obj);
}
}
}
}
//当鼠标左键处于按下状态时
//就在这里面去处理 线段绘制的逻辑
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));
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com