6.选中士兵

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

×

喜欢就点赞,疼爱就打赏