{"title":"using Skillx.Utils;\nusing UnityEngine;\nusing UnityEngine.UI;\n\nnamespace NoviceTutorial\n{\n ///

\n /// 实现镂空效果的Mask组件\n /// \n public class TutorialHollowOutMask : MaskableGraphic, ICanvasRaycastFilter\n {\n [SerializeField] private RectTransform highLightTarget;\n\n private Vector3 mTargetMin = Vector3.zero;\n private Vector3 mTargetMax = Vector3.zero;\n\n private bool mCanRefresh = true;\n private Transform mCacheTrans = null;\n\n /// \n /// 设置高亮区域的位置大小\n /// \n /// \n public void SetTarget(RectTransform target)\n {\n if (highLightTarget == null)\n {\n return;\n }\n\n highLightTarget.position = target.position;\n highLightTarget.sizeDelta = target.sizeDelta;\n \n mCanRefresh = true;\n RefreshView();\n }\n\n private void SetTarget(Vector3 tarMin, Vector3 tarMax)\n {\n if (tarMin == mTargetMin && tarMax == mTargetMax)\n return;\n mTargetMin = tarMin;\n mTargetMax = tarMax;\n SetAllDirty();\n }\n\n private void RefreshView()\n {\n if (!mCanRefresh)\n {\n return;\n }\n \n mCanRefresh = false;\n\n if (null == highLightTarget)\n {\n SetTarget(Vector3.zero, Vector3.zero);\n }\n else\n {\n Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(mCacheTrans, highLightTarget);\n SetTarget(bounds.min, bounds.max);\n }\n }\n\n protected override void OnPopulateMesh(VertexHelper vh)\n {\n if (mTargetMin == Vector3.zero && mTargetMax == Vector3.zero)\n {\n base.OnPopulateMesh(vh);\n return;\n }\n\n vh.Clear();\n\n // 填充顶点\n UIVertex vert = UIVertex.simpleVert;\n vert.color = color;\n\n Vector2 selfPiovt = rectTransform.pivot;\n Rect selfRect = rectTransform.rect;\n float outerLx = -selfPiovt.x * selfRect.width;\n float outerBy = -selfPiovt.y * selfRect.height;\n float outerRx = (1 - selfPiovt.x) * selfRect.width;\n float outerTy = (1 - selfPiovt.y) * selfRect.height;\n // 0 - Outer:LT\n vert.position = new Vector3(outerLx, outerTy);\n vh.AddVert(vert);\n // 1 - Outer:RT\n vert.position = new Vector3(outerRx, outerTy);\n vh.AddVert(vert);\n // 2 - Outer:RB\n vert.position = new Vector3(outerRx, outerBy);\n vh.AddVert(vert);\n // 3 - Outer:LB\n vert.position = new Vector3(outerLx, outerBy);\n vh.AddVert(vert);\n\n // 4 - Inner:LT\n vert.position = new Vector3(mTargetMin.x, mTargetMax.y);\n vh.AddVert(vert);\n // 5 - Inner:RT\n vert.position = new Vector3(mTargetMax.x, mTargetMax.y);\n vh.AddVert(vert);\n // 6 - Inner:RB\n vert.position = new Vector3(mTargetMax.x, mTargetMin.y);\n vh.AddVert(vert);\n // 7 - Inner:LB\n vert.position = new Vector3(mTargetMin.x, mTargetMin.y);\n vh.AddVert(vert);\n\n // 设定三角形\n vh.AddTriangle(4, 0, 1);\n vh.AddTriangle(4, 1, 5);\n vh.AddTriangle(5, 1, 2);\n vh.AddTriangle(5, 2, 6);\n vh.AddTriangle(6, 2, 3);\n vh.AddTriangle(6, 3, 7);\n vh.AddTriangle(7, 3, 0);\n vh.AddTriangle(7, 0, 4);\n }\n\n bool ICanvasRaycastFilter.IsRaycastLocationValid(Vector2 screenPos, Camera eventCamera)\n {\n if (highLightTarget == null)\n {\n return true;\n }\n \n // 将目标对象范围内的事件镂空(使事件可以穿透)\n var isClickHighLightArea = RectTransformUtility.RectangleContainsScreenPoint(highLightTarget, screenPos, eventCamera);\n \n //点击到高亮区域时,射线可以穿透\n if (!isClickHighLightArea)\n {\n //判断下方是否有引导窗口,如果有的话,通知引导进行下一步\n var uiTutorialRoot = GoHelper.FindGameObject(transform.parent.gameObject, "TutorialUIRoot");\n var uiTutorial = uiTutorialRoot.GetComponentInChildren();\n if (uiTutorial != null)\n {\n uiTutorial.Next();\n }\n }\n\n return !isClickHighLightArea;\n }\n\n protected override void Awake()\n {\n base.Awake();\n mCacheTrans = GetComponent();\n }\n\n void Update()\n {\n mCanRefresh = true;\n RefreshView();\n }\n }\n}\n挂上后, 场景里EventSystem有报错,来自t Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/GraphicRaycaster.cs:332)\n\n解释下为啥,并举个例子告诉我如何重写IsRaycastLocationValid内容:错误来自GraphicRaycaster.cs的332行,这个错误是因为TutorialHollowOutMask组件实现了ICanvasRaycastFilter接口,而ICanvasRaycastFilter接口中的IsRaycastLocationValid方法与GraphicRaycaster中的Raycast方法有冲突。\n\n在Unity中,GraphicRaycaster是用来处理UI元素的射线检测和事件响应的。它的Raycast方法会调用每个UI元素的IsRaycastLocationValid方法来判断射线是否在UI元素的可点击区域内。而TutorialHollowOutMask的IsRaycastLocationValid方法返回的是高亮区域外的位置是否有效,与GraphicRaycaster中的逻辑冲突。\n\n要解决这个问题,可以通过重写GraphicRaycaster的Raycast方法来处理TutorialHollowOutMask的点击事件。以下是一个示例代码:\n\ncsharp\nusing UnityEngine;\nusing UnityEngine.UI;\n\nnamespace NoviceTutorial\n{\n public class TutorialHollowOutRaycaster : GraphicRaycaster\n {\n public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)\n {\n // 获取场景中所有的TutorialHollowOutMask组件\n TutorialHollowOutMask[] masks = FindObjectsOfType<TutorialHollowOutMask>();\n\n // 遍历每个TutorialHollowOutMask组件\n foreach (TutorialHollowOutMask mask in masks)\n {\n // 如果点击的位置在高亮区域内,则跳过该TutorialHollowOutMask组件\n if (RectTransformUtility.RectangleContainsScreenPoint(mask.HighLightTarget, eventData.position, eventCamera))\n {\n continue;\n }\n\n // 将点击事件交给TutorialHollowOutMask处理\n mask.ProcessRaycast(eventData, resultAppendList);\n }\n }\n }\n}\n\n\n在上述代码中,我们创建了一个名为TutorialHollowOutRaycaster的类,继承自GraphicRaycaster。在Raycast方法中,我们获取场景中所有的TutorialHollowOutMask组件,并遍历每个组件。如果点击的位置在高亮区域内,则跳过该组件;否则,将点击事件交给TutorialHollowOutMask的ProcessRaycast方法处理。\n\n要使用这个TutorialHollowOutRaycaster,需要将它挂载到场景中的EventSystem对象上的GraphicRaycaster组件上。然后,将场景中的所有TutorialHollowOutMask组件的Canvas属性的Raycaster Type设置为None,这样它们就不会被默认的GraphicRaycaster处理。\n\n请注意,这只是一个示例代码,可能需要根据具体项目的需求进行修改和适配。

Unity UI 镂空遮罩组件:TutorialHollowOutMask 实现与优化

原文地址: https://www.cveoy.top/t/topic/qeSZ 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录