学习中遇到一些坑,记录一下,因为RectTransform坐标转换第一次遇到真的头疼
1,首先要理解RectTransform是Transform的子类,而所有的UI组件,在代码中获取的transform.position实际上都是rectTransform.anchoredPosition,也就是说,新建一个空物体,加入Image、Text等组件后,这个物体只有在Canvas下,才能显示出来,它的Transform组件自动被替换为RectTransform。
2, rectTransform.anchoredPosition是该UI物体的Pivot对应它的锚点的坐标,如果锚点为一个点,就比较好理解,如果锚点是父物体Panel或者canvas的四个角,那还牵涉到unity内部的一些计算,具体这里不深入研究。
3, 那么如果我们需要一个一个UI的世界坐标,来进行一些操作,应该怎么做?
假设:(1) 目标ui在canvas下,canvas的模式为camera,camera为专门的UIcamera。此时UI和canvas画布是可以在scene中看到的,作为游戏物体的一部分出现。
(2)我们还有一个maincamera来拍摄渲染整个游戏场景
(3) 我们需要一个linerenderer来从ui指向场景中某个物体。
那么场景中的物体坐标很容易得到,我们需要的就是UI的坐标。
第一步:将UI的position转换为屏幕坐标
unity提供了一个方法RectTransformUtility.WorldToScreenPoint(Camera camera,Vector3 point),我们将UI的transform.position也就是实际上的rectTransform.anchoredPosition传进point中,将UIcamera传进camera中,就可以得到这个ui的屏幕坐标。
RectTransformUtility.WorldToScreenPoint(UIcamera,transform.position)
第二步,将屏幕坐标转换为世界坐标
这时我们所选用的摄像机就由UIcamera换成Camera.main,因为canvas在任何位置都不影响UIcamera渲染它,而我们游戏世界是由maincamera来渲染的,所以此时摄像机应该使用mainCamera。
unity同样提供了一个方法Camera.ScreenToWorldPoint(Vector3 point)来处理这个问题,需要注意的是,之前RectTransformUtility.WorldToScreenPoint方法的返回值是一个Vector2,而这个方法的入参是vector3,需要处理一下Z轴,否则返回值永远是一个(0,0,0)的三维向量。
//主相机到2D界面的距离
float distance = Mathf.Abs(GameObject.Find("BackGroundCanvas").GetComponent<Canvas>().planeDistance) ;
// return GameObject.Find("UICamera").GetComponent<Camera>().ScreenToWorldPoint(new Vector3(mousPos.x, mousPos.y, distance));
return camera.ScreenToWorldPoint(new Vector3(mousPos.x, mousPos.y, distance));
此处我的项目是一个2D项目,暂时的Z轴使用背景层canvas的planedistance,方法外部再进行-1,以保证生成的linerenderer在背景层上显示。
第二个坑: UI预制体创建时如果没有指定父物体为canvas,就会导致位置、大小出现问题
因为canvas的camera模式在渲染时是进行了缩放的:如果没有缩放,那么比如canvas的planedistance设定为100,那么在摄像机中看到的canvas就会非常小了。如果没有指定父物体为canvas或其子物体,就会导致创建出来的UI物体巨大,位置也因为LocalPosition等变换问题出现奇怪的偏移
总结:折腾了一天才处理明白,我的处理方法应该也不是最好的,因为感觉堆2D项目中Z轴、DEPTH的理解还不够清晰。
版权归原作者 NerfmePlz 所有, 如有侵权,请联系我们删除。