RectTransformUtility的一些工具函数使用心得

忙里偷闲整理一下RectTransformUtility这个工具类里的几个函数使用方法~

·RectTransformUtility.CalculateRelativeRectTransformBounds(root, child)
这个函数两个参数root , child都是transform,返回一个bounds。
一般用于计算child物体如果在root作为父节点的时候,这个child的bounds,之前我一直没用到bounds,都是直接把他的返回结果bounds.center拿出来当坐标用,这样就实现了两个transform在毫不相关的节点下,获取一个在另一个下的相对位置,但是他返回一个bounds肯定也是有他的道理的,直接上源码:

public static Bounds CalculateRelativeRectTransformBounds (Transform root, Transform child)
{
	RectTransform[] componentsInChildren = child.GetComponentsInChildren<RectTransform> (includeInactive: false);
	if (componentsInChildren.Length != 0) {
		Vector3 vector = new Vector3 (float.MaxValue, float.MaxValue, float.MaxValue);
		Vector3 vector2 = new Vector3 (float.MinValue, float.MinValue, float.MinValue);
		Matrix4x4 worldToLocalMatrix = root.worldToLocalMatrix;
		int i = 0;
		for (int num = componentsInChildren.Length; i < num; i++) {
			componentsInChildren [i].GetWorldCorners (s_Corners);
			for (int j = 0; j < 4; j++) {
				Vector3 lhs = worldToLocalMatrix.MultiplyPoint3x4 (s_Corners [j]);
				vector = Vector3.Min (lhs, vector);
				vector2 = Vector3.Max (lhs, vector2);
			}
		}
		Bounds result = new Bounds (vector, Vector3.zero);
		result.Encapsulate (vector2);
		return result;
	}
	return new Bounds (Vector3.zero, Vector3.zero);
}

可以看出,坐标转换部分的原理是
1.先把父级物体root的世界转local矩阵获取到
2.然后把目标物体child(以及其他的子物体)用getworldCorner获取到他的世界坐标
3.在用第二步的世界坐标乘以第一步的矩阵,这样就可以获得相对的local坐标了

其实还是挺简单的,就是一个local转world再转local的过程。
但是他还做了操作就是把child的所有子物体都取出来,然后每一个都进行这样的操作,然后把获得的结果做一个矩形的“累加” 就是bounds的encapsulate操作,这种我是不太理解这样做在操作中会有什么样的实际应用场景呢,有大佬知道的希望可以解答下~ 不胜感激!
亲测即使是不同摄像机照射下的物体也可适用哦 ,毕竟是通过世界坐标转换的

RectTransformUtility.ScreenPointToWorldPointInRectangle

关于这个函数,我查了网上的说法 大多只有这么一段:
“将屏幕空间上的点转换为位于给定RectTransform平面上的世界空间中的位置。cam参数是与屏幕点相关的相机。对于Canvas设置为“Screen Space - Overlay mode”模式的情况,cam参数应该为null。”
看着不是很懂,于是自己做个简单实验:
1.搭建一个新场景
2.建立一个canvas,设置为camera模式,并且建立一个camera拖进来
3.随便加一个UI, 我加了一个Image
4.建一个脚本,写个测试代码:

public class ScreenToRayTest : MonoBehaviour
{
    public Camera cam;
    public RectTransform rectT;
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            DoClick(Input.mousePosition);
        }
    }
    void DoClick(Vector2 mousePos)
    {
       bool res= RectTransformUtility.ScreenPointToWorldPointInRectangle(rectT, mousePos, cam, out Vector3 worldPos);
       Debug.LogError("结果:"+res);
    }
}

我这里简单用鼠标点击的坐标作为屏幕坐标传进去,然后用场景中的image作为recttransform参数,我预想的结果是 ,我鼠标点击到Image图片上,他会返回true,点到外边,会返回false。
在这里插入图片描述
结果是无论我点没点到白色Image,都会返回true。不甚理解,看源码:

public static bool ScreenPointToWorldPointInRectangle (RectTransform rect, Vector2 screenPoint, Camera cam, out Vector3 worldPoint)
{
	worldPoint = Vector2.zero;
	Ray ray = ScreenPointToRay (cam, screenPoint);
	if (!new Plane (rect.rotation * Vector3.back, rect.position).Raycast (ray, out var enter)) {
		return false;
	}
	worldPoint = ray.GetPoint (enter);
	return true;
}

非常简短的几句代码,我们看出他首先是
1.new出了一个射线,从输入的屏幕坐标出发,以摄像机的角度为方向
2.重点来了,用这条射线去碰撞什么呢,去碰撞一个新建立的平面。
这个平面是如何建立的捏? 我们看是new plane(rect.rotationVector3.back,rect.postion)*
使用了一个法线加一个点的这种构造函数来构建平面,注意这个法线的获取:“rect.rotation * vector3.back”这是一个四元数乘以向量,这种东西里面具体计算方式我看的头疼,后悔之前上大学的时候没好好学这方面,矩阵相关的知识太差,哈哈,反正就是知道意思就是把vector3.back(冲着屏幕的方向)做一个rect.rotation的旋转,这样就能得出一个和rect一样旋转的法线了。 所以这样就获取了你参数rect所在的平面,注意他这个plane不是我们在unity右键就可以新建的那个plane,这个是可以无限延伸的真正意义上的平面,是一个结构体。所以我之前无论点哪里都会碰撞到,那么这个函数什么时候返回false呢?没错就是你射线和平面平行的时候,于是乎我又做了个实验,把image放倒,绕X轴旋转90,让他和视线平行:

然而结果出乎意料 :有时候是false 有时候是true
还是不合理呀,我想了一下,原来是我的摄像机是透视模式,透视模式视线方向是沿着视锥体的,不是平行的,换成正交模式,结果全是false。
总结 :这个函数就是检测从屏幕坐标的一个点沿着摄像机视线方向,然后去和参数里的rect所在平面去做碰撞检测,碰到了返回true,并且第四个out参数就是碰撞点的世界坐标,没碰到就是false。

ScreenPointToWorldPointInRectangle

这个就简单多了 就是 ScreenPointToWorldPointInRectangle后边又加一步呗,直接上源码:

public static bool ScreenPointToLocalPointInRectangle (RectTransform rect, Vector2 screenPoint, Camera cam, out Vector2 localPoint)
{
	localPoint = Vector2.zero;
	if (ScreenPointToWorldPointInRectangle (rect, screenPoint, cam, out var worldPoint)) {
		localPoint = rect.InverseTransformPoint (worldPoint);
		return true;
	}
	return false;
}

可以看出就是在获取到世界坐标之后再通过 rect.InverseTransformPoint转化成相对于rect的local坐标,我从这个函数的名字估计里面的实现就是用rect的转换矩阵求逆矩阵然后乘以坐标来计算。

以上三个比较不好理解的函数稍作记录,望君共勉~

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值