前言
如上图,NiagaraSystem中存在一些disabled的modules,TA大佬需要删除这些modules,但是一个一个删除太麻烦了,希望可以有一个批量删除的方法。
但是众所周知NiagaraSystem面向数据,在UNiagaraEmitter中根本没有modules相关的信息。
最后是通过编辑器界面Slate的数据获取方式找到了如何获取NiagaraSystem中的modules信息。
相关源码
通过SlateWidgetReflector定位到了module的数据来源:
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
,最后通过AddEntriesRecursive
将RootChildren
中的数据添加到了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()
函数即可。