[Like] Game视图选择Scene物体
【前言】

之前做很多项目的时候,在画面中突然出现的物体想要去选取调试,基本上都只能在scene去找,或者直接选择相机物体,在相机视野里双击物体查找,知道最近,在做一个项目时需要选择视野里的地砖查看视图,就想了却一下多年的心愿,写了这个插件。


配图:game视图选取地砖


【思路分析】

由于是Game视图选择物体,所以Game视图一般有两种选取方式:
一、使用射线检测碰撞体,也就意味着场景的物体身上必须有合适的碰撞体,然后根据检测信息知道物体选择物体。
二、UGUI有事件系统可以知道鼠标是否在UI上,这个接口或者EventTrigger需要勾选RaycastTarget,所以就猜想是否是射线检测,为此也写射线调试了下勾选了RaycastTarget的UI,好像是行不通,又或者是UI底层的射线和我们平时用的射线有所区别,由于UI没碰撞体的,所以可能是由屏幕位置来知道是否在UI上吧,反正我没看过UGUI源码不得而知。

综上所述,我们选择了第一种方式实现,这样的话索要选择的物体必须加碰撞体,如果场景中加碰撞体麻烦,建议在预制体加比较快。




【进入开发环节】

在开发过程中,我发现暂停后Update等生命周期函数会停止,于是乎我意识到,假设一个怪物在Game视图中突然出现很快消失,那么很不利于选择到该怪物,所以只能暂停停止生命周期,选择怪物,于是我舍弃了Update,改用了EditorApplication.update来替代Update相关功能,在暂停模式,这个方法便代替了Update执行动态选取。

1.Start方法生成Sphere(代替鼠标方便查看),再生成一个Sphere用于显示高亮效果,然后最最最重要的一点就是注册EditorApplication.update事件

private void Start()
{
print(\"Start\");
// 鼠标的屏幕坐标转成世界坐标的点
mousePoint = GameObject.CreatePrimitive(PrimitiveType.Sphere).GetComponent<Transform>();//创建一个Sphere物体
DestroyImmediate(mousePoint.GetComponent<Collider>());//去除掉碰撞体
mousePoint.localScale = Vector3.one * 0.1f;//设置尺寸

// 用于边缘效果展示的物体
highLightObj = GameObject.CreatePrimitive(PrimitiveType.Sphere).GetComponent<Transform>(); print(\"highLightObj=\" + highLightObj);//创建一个Sphere用于选中边缘描边
DestroyImmediate(highLightObj.GetComponent<Collider>());//同样去掉碰撞体
highLightObj.GetComponent<MeshRenderer>().material = Resources.Load<Material>(\"MT_Silhouette\");//更换成边缘描边材质球
highLightObj.gameObject.SetActive(false);//初始化关闭描边的物体
EditorApplication.update += Update0;//注册EditorApplication.update事件
}

2.Update0里面动态做射线检测,用于比对前后帧的物体来更换材质球,来显示到描边效果,最后在鼠标左键单击,来定位到选择的物体上。


void Update0()
{
print(\"update\");
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit raycastHit = new RaycastHit();
if (Physics.Raycast(ray out raycastHit))
{
//print(!nowObj);
if (!nowObj || nowObj.name != raycastHit.transform.name)//把上次检测到的物体和新检测的raycastHit的物体名比对,如果不是上一次的物体进入逻辑
{
if (nowObj)
{
// 换回原来的材质
nowObj.GetComponent<MeshRenderer>().material = oldMaterial;
}
// 射线当前检测到的物体
nowObj = raycastHit.transform.gameObject;
// 更换材质球
oldMaterial = raycastHit.transform.GetComponent<MeshRenderer>().sharedMaterial;
nowObj.GetComponent<MeshRenderer>().material = Resources.Load<Material>(\"MT_HighLingh\");
// 显示选中效果
highLightObj.position = raycastHit.transform.position;
highLightObj.rotation = raycastHit.transform.rotation;
highLightObj.localScale = raycastHit.transform.localScale;
highLightObj.GetComponent<MeshFilter>().mesh = raycastHit.collider.GetComponent<MeshFilter>().sharedMesh;
highLightObj.gameObject.SetActive(true);
print(nowObj);
}
if (Input.GetMouseButtonDown(0))
{
if (nowObj != null)//如果选取的对象存在,那么在scene视图定位到该物体
{
Selection.activeGameObject = nowObj;
SceneView.lastActiveSceneView.FrameSelected();
}
}
}
else
{
//print(\"射线未射到\");
if (highLightObj.gameObject.activeSelf)
{
//print(\"highLightObj处于激活\");
// 隐藏选中效果
highLightObj.gameObject.SetActive(false);
// 换回原来的材质
nowObj.GetComponent<MeshRenderer>().material = oldMaterial;
// 置空射线检测到的物体
nowObj = null;
}
}
Debug.DrawRay(Camera.main.transform.position GetMousePositionOnWorld() - Camera.main.transform.position Color.red);//将射线绘制出来,方便查看
mousePoint.position = GetMousePositionOnWorld();//将鼠标的屏幕坐标转换成世界坐标
}


【最后想说】

底下有源码资源包,起初,我想过没有运行前也能从Game视图选择物体,虽然EditorApplication.update支持刷新,但是射线的相关功能好像在主线程中才能使用(运行unity后),所以没有运行选取没有想到更好的解决方案,如果有更好的意见,可以留言我,评论我都会一一查看的。


案例源码:
码云下载Game视图选择物体unitypackage.txt (49 Bytes)
(下载次数: 9, 2021-1-23 09:58 上传)












  • 沙发 karsion
  • 2021-1-23 11:40:14
有点意思
{:107:}{:107:}
有点意思
  • 5# 璨辰
  • 2021-3-10 15:23:51

有点意思
案例中Game选择物体Scene视图定位,有点类似于Scene视图双击物体定位一样,除此之外,还做了选择的物体描边,还有一些编辑器功能
很实用的功能
好教程!学习了