UE 不修改源码实现遮罩控件 (Mask Widget)

本文内容的参考和灵感来自以下链接

GitHub - inkiu0/MaskWidget: UE4 MaskWidget

GitHub - JanSeliv/CustomShapeButton: Open-source plugin that allows to make buttons of any shape in UE5.4

在 UE 有一个很麻烦的地方,Slate 事件不是按照堆叠顺序传递的,就会形成以下现象

上图是使用 CustomShapeButton 和 Button 相叠,形成的一个使用场景,遮罩按钮显示在普通按钮上面,虽然遮罩按钮没有处理鼠标事件,但是鼠标事件也不会传递给普通按钮


 我们调试一下看看怎么回事

我们新建一个 SWidget ,然后在 UI 处理事件的函数断点,例如 OnMouseButtonDown 函数

这是 UI 的布局

查看堆栈数据,序号 47 是我们的 MainWidget ,但它后面只有两个 Widget 一个是 Canvas Panel 另一个是 MaskWidget (新建的 SWidget 类型),这里可以得出一个结论,但鼠标在 MaskWidget 上进行点击时,被 MaskWidget 遮住的 Button 是不会进入分发事件的队列的(或者说同级的 Widget 是不会进入另一个同级 Widget 的 Slate 事件队列,具体原因可以看图中函数的源码 FSlateApplication::ProcessMouseButtonDownEvent)

那么下面将开始在不修改源码的情况下,实现遮罩这一需求


我的核心思路是在 Tick 的时候根据鼠标所在的像素数据来更改 MaskWidget 的 Visibility 变量(Visibility 不是 Visible 时是不会进入发送事件的队列的)

首先一点,如何在 Tick 时获得鼠标位置,我们去溯源 MouseEvent 这个参数的形成过程

我们在这里可以发现 SlateApplication 有一个 CurrentCursorPosition 的变量可以读取,它就是当前鼠标位置


 那么如何判断鼠标是否在 Widget 内呢,我们参考一下 UE 在获得当前鼠标 Hover 的 Widgets 时是怎么做的,具体是 FHittestGrid::GetHitIndexFromCellIndex 函数

我们从 FHittestGrid 复制一些局部函数过来

然后构成这个函数的使用条件,我们在 OnPaint 缓存 FHittestGrid 和 FGeometry 

这样就可以在 Tick 中判断鼠标是否在 Widget 内


 接下里实现一下遮罩材质,随便连一下(Texture 是从 inkiu0 的 MaskWidget 拿的)


 下一步就是渲染材质到尺寸和 MaskWidget 一样的 RenderTaget 上,当我们将鼠标位置转换到 Widget 的本地位置,鼠标位置 Hover 的像素等于 RenderTarget 在该位置的像素(此处参考了 CustomShapeButton 的思路)

那到这里差不多就实现了

看看效果

(实现并不完善,后续实际应用遇到什么问题再完善吧😂)

全部代码在以下链接

代码浏览 - UE-DM-Tool

posted @ 2024-06-23 20:55  当麻  阅读(76)  评论(0编辑  收藏  举报