Unity 框选多个3D物体

闲来无事又更新了一下在最下边-2023.2.23

今天研究了一下框选功能,这是个很老旧的功能了 ,网上有很多资料,不过看了几个发现都是旧版本的,新版本有些东西要改动一下才好使,就改了改,写个博客纪录一下,

还是大概说一下思路吧,

1.鼠标按下时记录鼠标点下的屏幕坐标,通过unity提供的Camera的方法 WorldToScreenPoint,将待选物体由世界坐标转屏幕坐标.当然反过来也是一样的

2.第二就是比较一个点是否在一个范围内 这个很好理解,比xy轴就行了

3.屏幕上要画线 刷出来拖动的范围,用的是GL划线,需要一个特定的shader 这个也是之前老版本的代码不支持的地方

先上效果图

然后上代码 

这个因为有OnPostRender方法,脚本需要挂相机上才框选范围可以生效;

这个因为有OnPostRender方法,脚本需要挂相机上才框选范围可以生效;

这个因为有OnPostRender方法,脚本需要挂相机上才框选范围可以生效

using UnityEngine;
 
public class DrawRect : MonoBehaviour {
 
    private bool drawing;
    private Material rectMat;
    private Color rectColor = Color.green;
    private Vector2 startPosition = Vector2.zero;
    private Vector2 endPosition = Vector2.zero;
 
    void Start()
    {
        rectMat = new Material(Shader.Find("Lines/Colored Blended"));
    }
 
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            drawing = true;
            // 设置开始位置
            startPosition = Input.mousePosition;   
            CubeManager.Instance.BeginDraw();
        }
        if (Input.GetMouseButtonUp(0))
        {
            drawing = false;
        }
      
    }
    
    // 该方法为系统函数,需将此脚本挂载到Camera下才能执行。
    void OnPostRender()
    {
        if (drawing)
        {
            GL.PushMatrix();
 
            if (!rectMat)
                return;
            endPosition = Input.mousePosition;      // 设置结束位置
            CubeManager.Instance.Drawing(startPosition, endPosition);
            rectMat.SetPass(0);
            GL.LoadPixelMatrix();
            GL.Begin(GL.QUADS);
            GL.Color(new Color(rectColor.r, rectColor.g, rectColor.b, 0.1f ));
            GL.Vertex3(startPosition.x, startPosition.y, 0);
            GL.Vertex3(endPosition.x, startPosition.y, 0);
            GL.Vertex3(endPosition.x, endPosition.y, 0);
            GL.Vertex3(startPosition.x, endPosition.y, 0);
            GL.End();
            GL.Begin(GL.LINES );
            GL.Color(rectColor );
            GL.Vertex3(startPosition.x, startPosition.y, 0);
            GL.Vertex3(endPosition.x, startPosition.y, 0);
            GL.Vertex3(endPosition.x, startPosition.y, 0);
            GL.Vertex3(endPosition.x, endPosition.y, 0);
            GL.Vertex3(endPosition.x, endPosition.y, 0);
            GL.Vertex3(startPosition.x, endPosition.y, 0);
            GL.Vertex3(startPosition.x, endPosition.y, 0);
            GL.Vertex3(startPosition.x, startPosition.y, 0);
            GL.End();
            GL.PopMatrix();
        }
    }
}

这个脚本随便挂一个地方 拖个盒子和两个材质球就好了

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public class CubeManager : MonoBehaviour {
    private static CubeManager instance;
    public static CubeManager Instance
    {
        get
        {
            return instance;
        }
    }
 
    public GameObject prefab;
    public int count = 10;
 
    public Material originMat;
    public Material outlineMat;
 
    private List<MeshRenderer> meshRendererList;
 
    void Awake()
    {
        instance = this;
    }
 
	// Use this for initialization
    void Start () {
        meshRendererList = new List<MeshRenderer>();
 
        // 创建物体
        for ( int i = 0; i < count; i++ )
	    {
            GameObject go = GameObject.Instantiate( prefab ) as GameObject;
            go.name = (i + 1).ToString();
            go.transform.parent = transform;
            go.transform.position = new Vector3( Random.Range( -10, 10 ), 0, Random.Range( -10, 10 ) );
            meshRendererList.Add( go.GetComponent<MeshRenderer>() );
	    }
        originMat = meshRendererList[0].material;
    }
 
    public void BeginDraw()
    {
        var item = meshRendererList.GetEnumerator();
        while (item.MoveNext())
        {
            item.Current.material = originMat;
        }
    }
    
    public void Drawing(Vector2 point1, Vector2 point2)
    {
        Vector3 p1 = Vector3.zero;
        Vector3 p2 = Vector3.zero;
 
        if (point1.x > point2.x)
        {
            p1.x = point2.x;
            p2.x = point1.x;
        }
        else
        {
            p1.x = point1.x;
            p2.x = point2.x;
        }
        if (point1.y > point2.y)
        {
            p1.y = point2.y;
            p2.y = point1.y;
        }
        else
        {
            p1.y = point1.y;
            p2.y = point2.y;
        }
 
        var item = meshRendererList.GetEnumerator();
        while (item.MoveNext())
        {
            Vector3 position = Camera.main.WorldToScreenPoint(item.Current.transform.position);
            if (position.x > p1.x && position.y > p1.y 
                && position.x < p2.x && position.y < p2.y )
            {
                item.Current.material = outlineMat;
            }
            else
            {
                item.Current.material = originMat;
            }
        }
    }
}

场景中截图

下边是GL的材质shader

Shader "Lines/Colored Blended"
{
    SubShader 
    {
        Pass 
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off Cull Off Fog { Mode Off }
            BindChannels
            {
              Bind "vertex", vertex Bind "color", color
            }
        }
    }
}

最后也把原来的博客连接发一下,不能那么无耻的就偷别人的代码哈哈修改前的原文

=======================================================================

更新:

刚好有个小需求要框选  拿上边的代码发现有些小问题,

1.版本不同 OnPostRender方法不一定生效,画线在OnGUI方法即可

2,也不用挂相机上了.上班的坐标貌似Y轴需要镜像一下  

先放效果图

需要注意的是, 如果物体多个材质 需要把字典里的material改为materials数组 取得时候也是取数组,

还有一点 ,导出的模型上的不是meshrender  而是 skinnedmeshrenderer之类的, 这时可以把meshrenderer换为 他们的父类  render  

最后一点,这个方法在场景内mesh数量众多的时候不适用,会卡顿, 

代码如下

using UnityEngine;
using System.Collections.Generic;

public class BoxSelectManager : MonoBehaviour
{
    public bool drawing;
    public Material rectMat;
    public Color rectColor;//框选的颜色
    private Vector2 startPosition = Vector2.zero;
    private Vector2 endPosition = Vector2.zero;

    public RectTransform rectImage;

    public GameObject prefab;//用来批量生成一些物体
    public int count = 10;

    public Material outlineMat;//选中后物体的材质

    private List<MeshRenderer> meshRendererList;
    private Dictionary<MeshRenderer,Material> meshMatList = new Dictionary<MeshRenderer, Material>();//随机给一些物体赋予材质

   
    public List<Material> mats = new List<Material>();
    private float height;
    // Use this for initialization
    void Start()
    {
        meshRendererList = new List<MeshRenderer>();
        rectMat = new Material(Shader.Find("Lines/Colored Blended"));
        height = Screen.height;
        // 创建物体
        for (int i = 0; i < count; i++)
        {
            GameObject go = Instantiate(prefab);
            go.name = (i + 1).ToString();
            go.transform.parent = transform;
            go.transform.position = new Vector3(Random.Range(-10, 10), 0, Random.Range(-10, 10));
            go.GetComponent<MeshRenderer>().material = mats[Random.Range(0, mats.Count-2)];
            meshRendererList.Add(go.GetComponent<MeshRenderer>());
        }
        for (int i = 0; i < meshRendererList.Count; i++)
        {
            meshMatList[meshRendererList[i]] = meshRendererList[i].material;
        }
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            drawing = true;
            // 设置开始位置
            startPosition = Input.mousePosition;
            BeginDraw();
        }
        if (Input.GetMouseButtonUp(0))
        {
            drawing = false;
        }
    }

    void OnGUI()
    {
        if (drawing)
        {
            GL.PushMatrix();
            endPosition = Input.mousePosition;      // 设置结束位置
            Drawing(startPosition, endPosition);
            rectMat.SetPass(0);
            GL.LoadPixelMatrix();
            GL.Begin(GL.QUADS);
            GL.Color(new Color(rectColor.r, rectColor.g, rectColor.b, 0.1f));
            GL.Vertex3(startPosition.x, height - startPosition.y, 0);
            GL.Vertex3(endPosition.x, height - startPosition.y, 0);
            GL.Vertex3(endPosition.x, height - endPosition.y, 0);
            GL.Vertex3(startPosition.x, height - endPosition.y, 0);
            GL.End();
            GL.Begin(GL.LINES);
            GL.Color(rectColor);
            GL.Vertex3(startPosition.x, height - startPosition.y, 0);
            GL.Vertex3(endPosition.x, height - startPosition.y, 0);//上方横线
            GL.Vertex3(endPosition.x, height - startPosition.y, 0);
            GL.Vertex3(endPosition.x, height - endPosition.y, 0);//右边竖线
            GL.Vertex3(endPosition.x, height - endPosition.y, 0);
            GL.Vertex3(startPosition.x, height - endPosition.y, 0);//下方横线
            GL.Vertex3(startPosition.x, height - endPosition.y, 0);
            GL.Vertex3(startPosition.x, height - startPosition.y, 0);//作坊竖线
            GL.End();
            GL.PopMatrix();

        }
    }

    public void BeginDraw()
    {
        var item = meshRendererList.GetEnumerator();
        while (item.MoveNext())
        {
            item.Current.material = meshMatList[item.Current];
        }
    }

    public void Drawing(Vector2 point1, Vector2 point2)
    {
        Vector3 p1 = Vector3.zero;
        Vector3 p2 = Vector3.zero;

        if (point1.x > point2.x)
        {
            p1.x = point2.x;
            p2.x = point1.x;
        }
        else
        {
            p1.x = point1.x;
            p2.x = point2.x;
        }
        if (point1.y > point2.y)
        {
            p1.y = point2.y;
            p2.y = point1.y;
        }
        else
        {
            p1.y = point1.y;
            p2.y = point2.y;
        }

        var item = meshRendererList.GetEnumerator();
        while (item.MoveNext())
        {
            Vector3 position = Camera.main.WorldToScreenPoint(item.Current.transform.position);
            if (position.x > p1.x && position.y > p1.y
                && position.x < p2.x && position.y < p2.y)
            {
                item.Current.material = outlineMat;
            }
            else
            {
                item.Current.material = meshMatList[item.Current];
            }
        }
    }
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值