newGUI 中如何判断鼠标在某个控件范围内?(AB两种方法)
A.通过数学计算
根据 Canvas 的 RenderMode 不同分为两种情况:
1、Screen Space - Overlay(2D游戏或普通APP)
public Rect GetSpaceRect(RectTransform rect)
{
Rect spaceRect = rect.rect;
Vector3 spacePos = rect.position;
spaceRect.x = spaceRect.x * rect.lossyScale.x + spacePos.x;
spaceRect.y = spaceRect.y * rect.lossyScale.y + spacePos.y;
spaceRect.width = spaceRect.width * rect.lossyScale.x;
spaceRect.height = spaceRect.height * rect.lossyScale.y;
return spaceRect;
}
Rect rt = GetSpaceRect( rawImage.rectTrasform)
if( rt.Contains(Input.mousePosition))
{
//dos omething
}
研究了下 Screen space - Overlay下的计算原理:
在overlay模式下,不需要uicamera,所有UIBehaviour子类直接使用一个构造好的 orthogonal projection matrix,
等同于一个UICamera,其Size为 Screen.Height * 0.5f ,Projection为Orthographic,Clipping Planes为正无穷和负无穷。
再加上CanvasScaler的计算,在公式上直接让所有对象的 world position 等于 screen position。
等同于一个UICamera,其Size为 Screen.Height * 0.5f ,Projection为Orthographic,Clipping Planes为正无穷和负无穷。
再加上CanvasScaler的计算,在公式上直接让所有对象的 world position 等于 screen position。
变换路径为:【ui space -> world space -> viewport space -> screen space】
2、Screen Space - Camera(绝大多数3D游戏或多uicamera的APP使用)
var mg = gameObject.GetComponent<MaskableGraphic> ();
var rectTransform = mg.rectTransform;
Camera uicamera = Camera.main; // 此处按需取uicamera,不一定是main.
Debug.Log ("in:" + RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Input.mousePosition, uicamera));
研究了下 RectangleContainsScreenPoint 原理:
1、具有 CanvasScaler脚本的Canvas的GameObject的position坐标是相对于UICamera 的 World Space Position(一般为0,0,0)。这个Canvas认为是【RootCanvas】
2、RootCanvas 的所有子对象,其localPosition为 UI Resolution 下的坐标(比如 1920*1080) , 称为 【UI Space Local Position】,这个变换由 CanvasScaler 计算得出,并通过修改 RootCanvas 的scale值来应用变换。
3、子对象的RectTransform.rect是 UI Spcae 下的 rect
4、变换路径是 【(uispace) ui local position to ui world position(级联计算 ui position)】-> 【uispace -> world space ref uicamera】-> 【world space - > viewport space -> screen space】
5、手动计算如下:
var mg = gameObject.GetComponent<MaskableGraphic> ();
var rect = mg.rectTransform.rect;
Camera uicamera = Camera.main;
Vector3 screen_to_ui_scale = mg.rectTransform.lossyScale;
float uix = rect.x + mg.rectTransform.localPosition.x;
float uicamera_world_x = uix * screen_to_ui_scale.x;
float uiy = rect.y + mg.rectTransform.localPosition.y;
float uicamera_world_y = uiy * screen_to_ui_scale.y;
float uixm = uix + rect.width;
float uicamera_world_w = uixm * screen_to_ui_scale.x;
float uiym = uiy + rect.height;
float uicamera_world_h = uiym * screen_to_ui_scale.y;
Vector2 min = uicamera.WorldToScreenPoint (new Vector3 (uicamera_world_x, uicamera_world_y));
Vector2 max = uicamera.WorldToScreenPoint (new Vector3 (uicamera_world_w, uicamera_world_h));
Rect rect_screen = new Rect (min, (max - min));
Debug.Log ("In:" + rect_screen.Contains (Input.mousePosition));
B.通过EventSystem
var trigger = gameObject.AddComponent<UnityEngine.EventSystems.EventTrigger> ();
var entry = new UnityEngine.EventSystems.EventTrigger.Entry ();
entry.eventID = UnityEngine.EventSystems.EventTriggerType.PointerEnter;
entry.callback.AddListener( (data) =>
{
Debug.Log ("Input.mousePosition:"+Input.mousePosition);
});
trigger.triggers.Add (entry);
EventSystem本质上也是做计算,而且还走了很多结构性代码,所以如果如果可以用数学解决的话,就不要用回调函数来解决.