日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

if 组件是否存在_UE4 UMG简介+Slate组件问题排查

發布時間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 if 组件是否存在_UE4 UMG简介+Slate组件问题排查 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Slate 組件問題排查總結簡介
首先是一個工作中遇到的BUG: 用slua添加子節點到父節點上的時候,第二次打開無法顯示對應的子節點Widget。對應Lua代碼如下

  • local comboBox = ui_manager.ShowUI(ui_manager.UI_Config.ui_coupon_combobox,2,price,buyUIInfo.shopInfo.id)
  • if comboBox then
  • self:AttachChildWindow("ScaleBox_Coupon",comboBox)
  • end

  • 因為我們引入了UI對象的內存池概念,很自然而然的想到了緩存住的控件是不是釋放有問題。檢查slua的release代碼之后發現沒有任何問題。基于ScaleBox 只能有1個子物體這一個機制,在猜測BUG原因的時候,嘗試用CanvasPanel替換ScaleBox組件。發現果然能解決這個問題。
    但是也帶來了2個新問題:
    1、ScaleBox子物體在第一次關閉和第二次打開的時候經歷了什么?
    2、為什么CanvasPanel就可以而ScaleBox不可以?
    為了解決以上疑問,開啟我們的排查過程。
    為了能使大家更好的了解這個流程,先對Slate組件創建銷毀流程進行介紹。Slate組件創建銷毀流程
    1、將我們日常使用的一個組件,ScaleBox拆出來看(如下圖):有如下的對應關系。里面包含了 UScaleBox,UScaleBoxSlot,SscaleBox 。


    2、創建過程


    3、銷毀過程

    問題排查
    針對ScaleBox,進入斷點調試。
    在第一次關閉界面的時候,發現UscaleBox Release 有正常跑到(下面代碼部分)。

  • void UScaleBox::ReleaseSlateResources(bool bReleaseChildren)
  • {
  • Super::ReleaseSlateResources(bReleaseChildren);
  • MyScaleBox.Reset();
  • }
  • 但是在第二次打開的時候,發現沒有進入RebuildWidget(Swidget的實例化部分,也就是下面代碼部分)

  • TSharedRef<SWidget> UScaleBox::RebuildWidget()
  • {
  • MyScaleBox = SNew(SScaleBox)
  • .SingleLayoutPass(bSingleLayoutPass);
  • if ( GetChildrenCount() > 0 )
  • {
  • CastChecked<UScaleBoxSlot>(GetContentSlot())->BuildSlot(MyScaleBox.ToSharedRef());
  • }
  • return MyScaleBox.ToSharedRef();
  • }

  • 這是為什么呢? 原來,在UWidget的BUILD過程中,有一個查找共享指針MyWidget的操作。這個指針雖然被UscaleBox Reset了一次。但是在ScaleBox 第二次打開時候,這個指針依然存在引用。所以該實例并沒有被銷毀。那么在第二次打開的時候,由于沒有走RebuildWidget。導致了子類UScaleBox 的指針引用已經被銷毀,在AddChild時候調用添加了個空Object。(下圖代碼部分)

  • TSharedRef<SWidget> UWidget::TakeWidget_Private(ConstructMethodType ConstructMethod)
  • {
  • bool bNewlyCreated = false;
  • TSharedPtr<SWidget> PublicWidget;
  • // If the underlying widget doesn't exist we need to construct and cache the widget for the first run.
  • if (!MyWidget.IsValid())
  • {
  • PublicWidget = RebuildWidget();
  • #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
  • ensureMsgf(PublicWidget.Get() != &SNullWidget::NullWidget.Get(), TEXT("Don't return SNullWidget from RebuildWidget, because we mutate the state of the return. Return a SSpacer if you need to return a no-op widget."));
  • #endif
  • MyWidget = PublicWidget;
  • bNewlyCreated = true;
  • }
  • else
  • {
  • PublicWidget = MyWidget.Pin();
  • }

  • 那這個指針是被誰Hold住了呢? 經過排查與調試,發現是被UScaleBoxSlot給Hold住了導致了這份資源沒有被釋放。


    為什么在UScaleBox中的共享指針已經釋放了,但是UScaleBoxSlot所持有的相同指針沒有被釋放呢?請看Slot釋放的地方(下面代碼模塊):

  • // If the child is a UserWidget, we should let it manage it's own slate resources instead of forcing a clear here. This fixes issues such as UE-39106
  • if (PanelSlot->Content && !PanelSlot->Content->IsA<UUserWidget>())
  • {
  • const bool bReleaseChildren = true;
  • PanelSlot->ReleaseSlateResources(bReleaseChildren);
  • }

  • UE的注釋寫的很清楚了,我們所Add 的子物體,正是UserWidget 界面。在UE-39106版本中,讓UserWidget 的Slate釋放控制交給了上層。在這里沒有自動釋放。下面是UE 39106的改動鏈接。https://issues.unrealengine.com/issue/UE-39106

    虛幻引擎UE為了補鍋一個slate 被釋放的問題,導致了這個新問題。 我們上層緩存了這份實例,銷毀子物體的時候調用了RemoveFromParent。在ScaleBox被銷毀的時候,因為其子物體為userwidget,而不去釋放PanelSlot的SlateResourse。那么在第二次打開的時候,因為PanelSlot中共享指針的存在,沒有New一份新的子物體實例。
    OK,問題定位了。那么之前提到的第二個問題,為什么換成CanvasPanel就沒有這個問題了呢?,既然查到了在ScaleBox 中是 ScaleBoxSlot 的指針Hold住了這份資源,那么我們去CanvasPanel對應的Slot 看一下。(下面代碼)

  • void UCanvasPanelSlot::BuildSlot(TSharedRef<SConstraintCanvas> Canvas)
  • {
  • Slot = &Canvas->AddSlot()
  • [
  • Content == nullptr ? SNullWidget::NullWidget : Content->TakeWidget()
  • ];
  • SynchronizeProperties();
  • }

  • 明顯可以看出:原來CanvasPanelSlot 在Build過程中沒有緩存那份實例!只是做了常規引用!在上面有貼出過ScaleBoxSlot build 過程的代碼。所以就是ScaleBoxSlot 存住了這份指針,而CanvasPanelSlot 并沒有。所以在都沒有Release對應掉這份資源的情況下,CanvasPanel會走rebuild過程,而ScaleBox不會!
    到這里,我們基本上就定位出了問題全部原因所在。解決辦法
    這里想到了4種解決方案,都能解決這個問題,這里簡單說一下:
    1、不判斷指針是否還存在引用,全部走Rebuild過程。
    2、在Remove判斷是否為UserWidget過程中,對本身PanelSlot做一次釋放。
    3、刪除UScaleBoxSlot中對SscaleBox指針的引用。
    4、將UScaleBoxSlot中的共享引用改為弱引用。
    第一種風險較大,因為所有UMG都會走的創建過程,不太保險。
    第二種風險小于第一種,但是也在所有UMG都會走的創建過程,不太保險。
    第三種和第四種個人感覺都是有效的方案,總的來看還是第四種改動小并優雅一點。
    所以目前采用了第四種。

    總結

    以上是生活随笔為你收集整理的if 组件是否存在_UE4 UMG简介+Slate组件问题排查的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。