Unity 物体框选以及功能扩展

Unity 物体框选以及功能扩展


OK 老规矩,直接上图片上代码:

组件搭载:
DrawRectangle_ZH 脚本需要搭载到 Camera下。
GestureRecognition_ZH 脚本 看需求自己搭载。

屏幕线框绘制以及物体选择

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

/// <summary>
/// 屏幕线框绘制以及物体选择
/// </summary>

public class DrawRectangle_ZH : MonoBehaviour
{
    //线框颜色
    [Header("线框颜色")]
    public Color _RectColor = Color.green;
    [Header("选中材质")]
    public Material _SelectingMaterial;
    [Header("取消材质")]
    public Material _CancelMaterial;

    //单例
    public static DrawRectangle_ZH _DrawRectangle;

    //记下鼠标按下位置
    private Vector3 _MouseStart = Vector3.zero;

    //画线的材质 不设定系统会用当前材质画线 结果不可控
    private Material _RectMat;

    //是否开始画线标志
    private bool _IsDrawRectangle = false;

    //场景角色数组
    public List<GameObject> _Characters;

    //当前选择数组
    private List<GameObject> _NewListGam = new List<GameObject>();

    private void Awake()
    {
        _DrawRectangle = this;
        //场景特定物体数组存入
        GameobjectAddList();
    }

    void Start()
    {
        //生成画线的材质
        _RectMat = new Material(Shader.Find("UI/Default"));
        //GameObject(游戏对象)没有显示在层次结构中,没有保存到场景中,也没有被Resources.UnloadUnusedAssets卸载。
        _RectMat.hideFlags = HideFlags.HideAndDontSave;
        //Gameobject(游戏对象)没有显示在层次结构中,没有保存到场景中,也没有被Resources.UnloadUnusedAssets卸载
        _RectMat.shader.hideFlags = HideFlags.HideAndDontSave;
    }


    void Update()
    {
        //按下鼠标左键
        if (Input.GetMouseButtonDown(0))
        {
            //如果鼠标左键按下 设置开始画线标志
            _IsDrawRectangle = true;
            //记录按下位置
            _MouseStart = Input.mousePosition;
        }
        //如果鼠标左键放开 结束画线
        else if (Input.GetMouseButtonUp(0))
        {
            _IsDrawRectangle = false;
        }

        if (Input.GetMouseButton(0))
        {
            //手势识别
            switch (GestureRecognition_ZH._GestureRecognition._GestureStateBe)
            {
                case GestureRecognition_ZH.GestureState.Null:

                    //扩展

                    break;

                case GestureRecognition_ZH.GestureState.Up:

                    //扩展

                    break;

                case GestureRecognition_ZH.GestureState.Down:

                    //取消选中方法
                    Disselecting();

                    break;

                case GestureRecognition_ZH.GestureState.Lift:

                    //扩展

                    break;

                case GestureRecognition_ZH.GestureState.Right:

                    //扩展

                    break;

                default:
                    break;
            }
        }
        if (Input.GetMouseButton(1))
        {
            //移动方法  
            MoveController(_NewListGam);

        }
    }

    /// <summary>
    /// 在渲染后执行
    /// </summary>
    void OnPostRender()
    {
        //画线这种操作推荐在 OnPostRender()里进行 而不是直接放在Update,所以需要标志来开启
        if (_IsDrawRectangle)
        {
            //鼠标当前位置
            Vector3 _MouseEnd = Input.mousePosition;
            //保存摄像机变换矩阵
            GL.PushMatrix();

            if (!_RectMat)
                return;

            _RectMat.SetPass(0);
            //设置用屏幕坐标绘图
            GL.LoadPixelMatrix();
            GL.Begin(GL.QUADS);
            //设置颜色和透明度,方框内部透明
            GL.Color(new Color(_RectColor.r, _RectColor.g, _RectColor.b, 0.1f));
            GL.Vertex3(_MouseStart.x, _MouseStart.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseStart.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseEnd.y, 0);
            GL.Vertex3(_MouseStart.x, _MouseEnd.y, 0);
            GL.End();
            GL.Begin(GL.LINES);
            //设置方框的边框颜色 边框不透明
            GL.Color(_RectColor);
            GL.Vertex3(_MouseStart.x, _MouseStart.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseStart.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseStart.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseEnd.y, 0);
            GL.Vertex3(_MouseEnd.x, _MouseEnd.y, 0);
            GL.Vertex3(_MouseStart.x, _MouseEnd.y, 0);
            GL.Vertex3(_MouseStart.x, _MouseEnd.y, 0);
            GL.Vertex3(_MouseStart.x, _MouseStart.y, 0);
            GL.End();
            //恢复摄像机投影矩阵
            GL.PopMatrix();

            //物体判断方法
            CheckSelection(_MouseStart, _MouseEnd);
        }
    }

    /// <summary>
    /// 线框绘制范围物体判断方法
    /// </summary>
    /// <param 开始位置="_StartPosition">  </param>
    /// <param 结束位置="_EndPosition">  </param>
    void CheckSelection(Vector3 _StartPosition, Vector3 _EndPosition)
    {
        Vector3 p1 = Vector3.zero;
        Vector3 p2 = Vector3.zero;
        //这些判断是用来确保p1的xy坐标小于p2的xy坐标,因为画的框不见得就是左下到右上这个方向的
        if (_StartPosition.x > _EndPosition.x)
        {
            p1.x = _EndPosition.x;
            p2.x = _StartPosition.x;
        }
        else
        {
            p1.x = _StartPosition.x;
            p2.x = _EndPosition.x;
        }
        if (_StartPosition.y > _EndPosition.y)
        {
            p1.y = _EndPosition.y;
            p2.y = _StartPosition.y;
        }
        else
        {
            p1.y = _StartPosition.y;
            p2.y = _EndPosition.y;
        }
        foreach (GameObject _Obj in _Characters)
        {
            //把可选择的对象保存在characters数组里
            //把对象的position转换成屏幕坐标
            Vector3 location = GetComponent<Camera>().WorldToScreenPoint(_Obj.transform.position);
            if (location.x < p1.x || location.x > p2.x || location.y < p1.y || location.y > p2.y
                //z方向就用摄像机的设定值,看不见的也不需要选择了
                || location.z < GetComponent<Camera>().nearClipPlane || location.z > GetComponent<Camera>().farClipPlane)
            {
                //上面的条件是筛选 不在选择范围内的对象,然后进行取消选择操作,比如把物体放到default层,就不显示轮廓线了
                //Disselecting(obj);
            }
            else
            {
                //否则就进行选中操作,比如把物体放到画轮廓线的层去
                Selecting(_Obj);
                //数组清空
                _NewListGam.Clear();
                //数组添加
                _NewListGam.AddRange(GameObject.FindGameObjectsWithTag("画线轮廓"));
            }
        }
    }

    /// <summary>
    /// 选中方法操作
    /// </summary>
    /// <param 被选中的物体="_Obj"></param>
    private void Selecting(GameObject _Obj)
    {
        //层级设置
        _Obj.layer = LayerMask.NameToLayer("画线轮廓");
        _Obj.tag = "画线轮廓";
        //材质赋予
        _Obj.GetComponent<MeshRenderer>().material = _SelectingMaterial;
    }

    /// <summary>
    /// 取消选中方法
    /// </summary>
    public void Disselecting()
    {
        foreach (var item in _Characters)
        {
            //层级设置
            item.layer = LayerMask.NameToLayer("Default");
            item.tag = "Default";
            //材质赋予
            item.GetComponent<MeshRenderer>().material = _CancelMaterial;
        }
    }

    /// <summary>
    /// 场景物体数组存入
    /// </summary>
    private void GameobjectAddList()
    {
        //查找特定数组存入当前控制数组
        _Characters.AddRange(GameObject.FindGameObjectsWithTag("Player"));
    }

    /// <summary>
    /// 物体移动方法
    /// </summary>
    /// <param 框选物体数组="_ObjList"></param>
    private void MoveController(List<GameObject> _ObjList)
    {
        //将鼠标的屏幕坐标转换到世界坐标内 ,深度值设为相机的最大深度,然后从相机发出射线到该点,中途如果有碰撞,将碰撞的物体设为选择
        Vector3 _MouseWorldPosition = Camera.main.ScreenToWorldPoint(new Vector3(_MouseStart.x, _MouseStart.y, Camera.main.farClipPlane));
        Ray ray = new Ray(Camera.main.transform.position, _MouseWorldPosition - Camera.main.transform.position);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
            //如果射线点击到的是地面的话
            if (hit.transform.tag == "地面")
            {
                //选择物体的位置就等于射线点击的位置
                for (int i = 0; i < _ObjList.Count; i++)
                {
                    //_ObjList[i].transform.position = hit.point;

                    //移动算法
                    MoveVrithmetic();
                }

            }
        }
    }

    /// <summary>
    /// 移动算法
    /// </summary>
    private void MoveVrithmetic()
    {
        //没写 哈哈哈
    }
}

手势识别

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

/// <summary>
/// 手势识别
/// </summary>

public class GestureRecognition_ZH : MonoBehaviour
{
    //鼠标第一次点击位置
    public Vector2 _MousePos;
    //位置枚举
    public GestureState _GestureStateBe;
    //最小动作距离
    private float _MinGestureDistance = 20.0f;
    //手势开启布尔
    private bool _IsInvaild;
    //单例
    public static GestureRecognition_ZH _GestureRecognition;

    void Update()
    {
        //手势方法
        GestureOnClick();
        _GestureRecognition = this;
    }

    //手势方法
    public void GestureOnClick()
    {
        //手势为空
        _GestureStateBe = GestureState.Null;

        if (Input.GetMouseButtonDown(0))
        {
            //第一次鼠标点击位置记录
            _MousePos = Input.mousePosition;
            //开启手势识别
            _IsInvaild = true;

        }
        if (Input.GetMouseButton(0))
        {
            //鼠标轨迹向量
            Vector2 _Dis = (Vector2)Input.mousePosition - _MousePos;
            //画线
            Debug.DrawLine(_MousePos, (Vector2)Input.mousePosition, Color.cyan);
            //判断当前 向量的长度 是否大于 最小动作距离
            if (_Dis.magnitude > _MinGestureDistance)
            {
                //判断在 空间 X轴 还是在 Y轴
                if (Mathf.Abs(_Dis.x) > Mathf.Abs(_Dis.y) && _IsInvaild)
                {
                    if (_Dis.x > 0)
                    {
                        //如果当前向量值 X 大于 0  就是 Right 状态
                        _GestureStateBe = GestureState.Right;
                    }
                    else if (_Dis.x < 0)
                    {
                        //如果当前向量值 X 小于 0  就是 Lift 状态
                        _GestureStateBe = GestureState.Lift;
                    }
                }
                //判断在 空间 X轴 还是在 Y轴
                else if (Mathf.Abs(_Dis.x) < Mathf.Abs(_Dis.y) && _IsInvaild)
                {
                    if (_Dis.y > 0)
                    {
                        //如果当前向量值 Y 大于 0  就是 Up 状态
                        _GestureStateBe = GestureState.Up;
                    }
                    else if (_Dis.y < 0)
                    {
                        //如果当前向量值 Y 小于 0  就是 Down 状态
                        _GestureStateBe = GestureState.Down;
                    }
                }

                CallEvent();
                //关闭手势识别
                _IsInvaild = false;
            }
        }
    }

    //呼叫事件
    public void CallEvent()
    {
        switch (_GestureStateBe)
        {
            case GestureState.Null:

                print("Null");

                // Null 方法调用(自己写)

                break;

            case GestureState.Up:

                print("Up");

                // Up 方法调用(自己写)

                break;

            case GestureState.Down:

                print("Down");
                // Down 方法调用(自己写)

                break;

            case GestureState.Lift:

                print("Lift");

                // Lift 方法调用(自己写)

                break;

            case GestureState.Right:

                print("Right");

                // Right 方法调用(自己写)

                break;

            default:
                break;
        }
    }

    //状态枚举
    public enum GestureState
    {
        Null,
        Up,
        Down,
        Lift,
        Right
    }
}
最终效果以及脚本搭载:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

暂时先这样吧,如果有时间的话就会更新,如果实在看不明白就留言,看到我会回复的。

路长远兮,与君共勉。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maddie_Mo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值