[UE4奇怪错误合集-01] UE4 蓝图BluePrint崩溃 原因分析
先上图:
分析:
确实只给你个崩溃弹窗并不能提供有价值的信息,考虑到此蓝图的父类是 继承自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类创建一个蓝图,它是肯定不会进不去的,再在其中更改父类为那个自建类,这样也是可以的。
总结:
使用了空指针。