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>();
DestroyImmediate(mousePoint.GetComponent<Collider>());
mousePoint.localScale = Vector3.one * 0.1f;
// 用于边缘效果展示的物体
highLightObj = GameObject.CreatePrimitive(PrimitiveType.Sphere).GetComponent<Transform>(); print("highLightObj=" + highLightObj);
DestroyImmediate(highLightObj.GetComponent<Collider>());
highLightObj.GetComponent<MeshRenderer>().material = Resources.Load<Material>("MT_Silhouette");
highLightObj.gameObject.SetActive(false);
//EditorApplication.update += Update0;
}
引用文本
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_HighLinght");
// 显示选中效果
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)
{
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();
}
Vector3 GetMousePositionOnWorld()
{
Vector3 mousePos = Input.mousePosition;
// Z 值不能为零,Z 值为零该方法返回值为相机的世界坐标
// 鼠标坐标值转换为世界坐标时,该方法返回值的 Z 值为:相机的 Z 值加上下面一行代码的赋值
// 例如:相机 Z 值为-10,经过下面一行代码赋值后,该方法返回值的 Z 值为 0
mousePos.z = 10;
return Camera.main.ScreenToWorldPoint(mousePos);
}
【最后想说】
底下有源码资源包,起初,我想过没有运行前也能从Game视图选择物体,虽然EditorApplication.update支持刷新,但是射线的相关功能好像在主线程中才能使用(运行unity后),所以没有运行选取没有想到更好的解决方案,如果有更好的意见,可以留言我,评论我都会一一查看的。
案例源码下载:Game视图选择物体unitypackage