UE 不修改原始碼實現遮罩控制元件 (Mask Widget)

当麻發表於2024-06-23

本文內容的參考和靈感來自以下連結

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

相關文章