先上圖:

分析:
確實只給你個崩潰彈窗並不能提供有價值的信息,考慮到此藍圖的父類是 繼承自UserWidget的自定義的一個子類。所以問題可能出現在這個類中。
這個類中我們重寫了UserWidget的 Initialize()生命周期函數。而且在里面使用了
RootCanvas = Cast<UCanvasPanel>(GetRootWidget());
RootCanvas->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
問題就出現在這里,RootCanvas獲取到實際上是一個Null。
我們看 GetRootWidget() 定義
if ( WidgetTree )
{
return WidgetTree->RootWidget;
}
return nullptr;
我們可以在UserWidget的Initialize()中找到WidgetTree的有着代碼
if ( WidgetTree == nullptr )
{
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
}
所以我們只要在重寫的 Initialize() 中先調用 Super::Initialize(),WidgetTree就已經存在了,問題在於WidgetTree->RootWidget;
Widget的根組件以及根組件的子組件也好,總是在調用Initialize()之后添加上去的。
例如我們可以在 RebuildWidget() 函數中找到相應代碼塊
// In the event this widget is replaced in memory by the blueprint compiler update
// the widget won't be properly initialized, so we ensure it's initialized and initialize
// it if it hasn't been.
...
if ( !bInitialized )
{
Initialize();
}
...
// Add the first component to the root of the widget surface.
TSharedRef<SWidget> UserRootWidget = WidgetTree->RootWidget ? WidgetTree->RootWidget->TakeWidget() : TSharedRef<SWidget>(SNew(SSpacer));
...
由於並沒有進一步看藍圖編輯器blueprint compiler 的源碼,猜測當點擊藍圖類時,會調用對應父類的Initialize()函數,而此時的WidgetTree是沒有任何根組件的,所以在Initialize()中使用GetRootWidget()獲取根組件返回的必然是空指針,使用空指針便會導致崩潰。
解決方案1:
獲取以及使用根組件肯定不能放在Initialize()中,RootWidget具體是在哪個生命周期中被賦值的我沒有找,理論上只要在此函數之后的生命周期中就可以獲取RootWidget了。所以我們可以重載NativeConstruct()函數,在其中使用GetRootWidget()。這樣你的藍圖不至於打不開。(當然打開之后還是要給它一個CanvasPanel才可以不出錯)
void UDDFrameWidget::NativeConstruct()
{
RootCanvas = Cast<UCanvasPanel>(GetRootWidget());
RootCanvas->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
解決方案2:
我是直接基於那個自建類創建的藍圖,所以自建類有邏輯錯誤就可能導致藍圖打不開。可以先基於內置的UserWidget類創建一個藍圖,它是肯定不會進不去的,再在其中更改父類為那個自建類,這樣也是可以的。
總結:
使用了空指針。
