闲来无事又更新了一下在最下边-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];
}
}
}
}