UE4:NiagaraSystem批量删除modules

前言

NiagaraSystem

如上图,NiagaraSystem中存在一些disabled的modules,TA大佬需要删除这些modules,但是一个一个删除太麻烦了,希望可以有一个批量删除的方法。

但是众所周知NiagaraSystem面向数据,在UNiagaraEmitter中根本没有modules相关的信息。

最后是通过编辑器界面Slate的数据获取方式找到了如何获取NiagaraSystem中的modules信息。

相关源码

通过SlateWidgetReflector定位到了module的数据来源:

SlateWidgetReflector

1
2
3
4
5
6
7
8
9
ChildSlot
[
SAssignNew(EntryListView, SListView<UNiagaraStackEntry*>)
.ListItemsSource(&FlattenedEntryList)
.OnGenerateRow(this, &SNiagaraOverviewStack::OnGenerateRowForEntry)
.OnSelectionChanged(this, &SNiagaraOverviewStack::OnSelectionChanged)
.SelectionMode(ESelectionMode::Multi)
.OnItemToString_Debug_Static(&FNiagaraStackEditorWidgetsUtilities::StackEntryToStringForListDebug)
];

StackItem的数据来源于FlattenedEntryList

那么FlattenedEntryList的数据来源于哪里?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void SNiagaraOverviewStack::RefreshEntryList()
{
if (bRefreshEntryListPending)
{
FlattenedEntryList.Empty();
EntryObjectKeyToParentChain.Empty();
TArray<UClass*> AcceptableClasses;
AcceptableClasses.Add(UNiagaraStackItemGroup::StaticClass());
AcceptableClasses.Add(UNiagaraStackItem::StaticClass());

UNiagaraStackEntry* RootEntry = StackViewModel->GetRootEntry();
checkf(RootEntry != nullptr, TEXT("Root entry was null."));
TArray<UNiagaraStackEntry*> RootChildren;
RootEntry->GetFilteredChildren(RootChildren);
for (UNiagaraStackEntry* RootChild : RootChildren)
{
checkf(RootEntry != nullptr, TEXT("Root entry child was null."));
TArray<UNiagaraStackEntry*> ParentChain;
AddEntriesRecursive(*RootChild, FlattenedEntryList, AcceptableClasses, ParentChain);
}

bRefreshEntryListPending = false;
EntryListView->RequestListRefresh();
}
}

从上面这段代码可以看出来,StackViewModel->GetRootEntry()获得了UNiagaraStackEntry类型的RootEntry,然后通过RootEntry->GetFilteredChildren(RootChildren)获得了RootChildren,最后通过AddEntriesRecursiveRootChildren中的数据添加到了FlattenedEntryList中。

StackViewModel则是在Construct函数中传递过来的,那么继续往上一层寻找,发现是在SNiagaraOverviewStackNode.cpp中的CreateNodeContentArea()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TSharedRef<SWidget> SNiagaraOverviewStackNode::CreateNodeContentArea()
{
TSharedPtr<SWidget> ContentWidget;
if (StackViewModel != nullptr && OverviewSelectionViewModel != nullptr)
{
ContentWidget = SNew(SBox)
.MaxDesiredWidth(300)
[
SNew(SNiagaraOverviewStack, *StackViewModel, *OverviewSelectionViewModel)
];
}
else
{
ContentWidget = SNullWidget::NullWidget;
}

//...
}

最后找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FNiagaraEditorModule& NiagaraEditorModule = FModuleManager::Get().LoadModuleChecked<FNiagaraEditorModule>("NiagaraEditor");
TSharedPtr<FNiagaraSystemViewModel> OwningSystemViewModel = NiagaraEditorModule.GetExistingViewModelForSystem(OverviewStackNode->GetOwningSystem());
if (OwningSystemViewModel.IsValid())
{
if (OverviewStackNode->GetEmitterHandleGuid().IsValid() == false)
{
StackViewModel = OwningSystemViewModel->GetSystemStackViewModel();
}
else
{
EmitterHandleViewModelWeak = OwningSystemViewModel->GetEmitterHandleViewModelById(OverviewStackNode->GetEmitterHandleGuid());
if (EmitterHandleViewModelWeak.IsValid())
{
StackViewModel = EmitterHandleViewModelWeak.Pin()->GetEmitterStackViewModel();
}
}
}

这个OverviewStackNode->GetOwningSystem()就是我们要找的NiagaraSystem,然后通过NiagaraEditorModule.GetExistingViewModelForSystem获得了OwningSystemViewModel,最后通过OwningSystemViewModel->GetSystemStackViewModel()获得了StackViewModel

然鹅,我惊讶的发现,直接将UNiagaraSystem传进去,通过NiagaraEditorModule.GetExistingViewModelForSystem获得的OwningSystemViewModel是空的,除非我手动打开了当前的NiagaraSystem的Editor。

毕竟是面向数据,得想办法绕绕路。

于是我又通过断点调试等方式,发现需要手动初始化一个FNiagaraSystemViewModel才可以,该类型的Initialize函数中处理了相关的数据初始化。

最终实现

由于FNiagaraSystemViewModel类型并没有从编辑器方法中导出,无法直接在外部使用,但是幸好FNiagaraEditorModule是处于同一个模块下的,于是在这里扩展出一个方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
TSharedPtr<FNiagaraSystemViewModel> FNiagaraEditorModule::GetSystemExistingViewModel(UNiagaraSystem* InSystem)
{
TSharedPtr<FNiagaraSystemViewModel> SystemViewModel = MakeShared<FNiagaraSystemViewModel>();
FNiagaraSystemViewModelOptions SystemViewModelOptions = FNiagaraSystemViewModelOptions();
SystemViewModelOptions.EditMode = ENiagaraSystemViewModelEditMode::SystemAsset;
SystemViewModelOptions.MessageLogGuid = InSystem->GetAssetGuid();
SystemViewModelOptions.bCanAutoCompile = false;
SystemViewModelOptions.bCanSimulate = false;
SystemViewModelOptions.bIsForDataProcessingOnly = true;
SystemViewModel->Initialize(*InSystem, SystemViewModelOptions);
TSharedPtr<FNiagaraSystemViewModel> ExistingViewModel = SystemViewModel->GetExistingViewModelForObject(InSystem);
return ExistingViewModel;
}

通过以上方法就可以直接对UNiagaraSystem进行操作了,可以获取到FNiagaraSystemViewModel,并且不需要打开编辑器

在获取到FNiagaraSystemViewModel之后,可以通过EmitterHandleID获取到FNiagaraEmitterHandleViewModel,再进一步获取到UNiagaraStackViewModel,然后就可以通过UNiagaraStackViewModel获取到UNiagaraStackEntry,最后可以获取到UNiagaraStackModuleItem

UNiagaraStackModuleItem可以通过函数GetIsEnabled获取到其是否启用,如果没有启用并且需要删除的话,只需要调用Delete()
函数即可。


UE4:NiagaraSystem批量删除modules
http://muchenhen.com/posts/29435/
作者
木尘痕
发布于
2023年4月1日
许可协议