Unreal Engine Find In Blueprint 完全指南
目录
功能概述
系统架构
核心类详解
UI 层 - SFindInBlueprints
搜索管理器 - FFindInBlueprintSearchManager
异步搜索 - FStreamSearch
虚拟数据层 - ImaginaryBlueprintData
搜索结果 - FFindInBlueprintsResult
搜索数据结构
搜索语法
索引与缓存机制
版本控制
线程安全机制
配置选项
扩展与自定义
调试与诊断
最佳实践
常见问题
搜索实例 - FFiBSearchInstance
1. 功能概述
1.1 什么是 Find In Blueprint?
Find In Blueprint (FiB) 是虚幻引擎蓝图编辑器中的核心搜索功能,允许开发者在蓝图中快速查找:
节点 (Nodes) - 函数调用、事件、宏等
变量 (Variables) - 蓝图属性
引脚 (Pins) - 节点的输入输出
图表 (Graphs) - 函数、宏、事件图
组件 (Components) - SCS 组件
注释 (Comments) - 节点注释
1.2 两种搜索模式
模式
描述
快捷键
Find Within Blueprint
仅在当前打开的蓝图中搜索
Ctrl+F
Find In All Blueprints
在项目中所有蓝图中搜索
Ctrl+Shift+F
1.3 功能入口
蓝图编辑器
│
├── 菜单栏 → Edit → Find in Blueprints
│
├── 工具栏 → 搜索图标
│
└── 快捷键 → Ctrl+Shift+F
1.4 用户界面
┌─────────────────────────────────────────────────────────────────┐
│ Find in Blueprints [🔒] [×] │
├─────────────────────────────────────────────────────────────────┤
│ [搜索框: Enter function or event name to find...] [🔍] │
├─────────────────────────────────────────────────────────────────┤
│ ▼ /Game/Blueprints/BP_Character │
│ ▼ EventGraph │
│ ▼ BeginPlay │
│ └── Execute │
│ ▼ PrintString │
│ └── InString = "Hello" │
│ ▼ CalculateDamage (Function ) │
│ └── ... │
├─────────────────────────────────────────────────────────────────┤
│ Searching... [████████████░░░░░░░░] 60% │
├─────────────────────────────────────────────────────────────────┤
│ ⚠ Search incomplete: 15 blueprints don
│ index. [Index All] [Export Asset List] [Dismiss] │
└─────────────────────────────────────────────────────────────────┘
2. 系统架构
2.1 整体架构图
┌─────────────────────────────────────────────────────────────────────────┐
│ Find In Blueprint 系统 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ UI 层 (Slate) │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ SFindInBlueprints│ │ SSearchBox │ │ STreeView │ │ │
│ │ │ (主控件) │ │ (搜索框) │ │ (结果树) │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 搜索管理层 (单例) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ FFindInBlueprintSearchManager │ │ │
│ │ │ - 管理搜索索引 (SearchMap, SearchArray) │ │ │
│ │ │ - 处理资产生命周期事件 │ │ │
│ │ │ - 协调异步搜索 │ │ │
│ │ │ - 管理缓存操作 │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 搜索执行层 │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ FStreamSearch │ │FFiBSearchInstance│ │ 文本过滤器 │ │ │
│ │ │ (异步搜索线程) │ │ (搜索实例) │ │ (表达式解析) │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 数据层 │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ FSearchData │ │FImaginaryBlueprint│ │ JSON 索引数据 │ │ │
│ │ │ (搜索数据条目) │ │ (虚拟蓝图) │ │ (序列化存储) │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 结果层 │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ FFindInBlueprintsResult 层次结构 ││ │
│ │ │ - FFindInBlueprintsGraphNode (节点结果) ││ │
│ │ │ - FFindInBlueprintsPin (引脚结果) ││ │
│ │ │ - FFindInBlueprintsProperty (属性结果) ││ │
│ │ │ - FFindInBlueprintsGraph (图表结果) ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.2 数据流
用户输入搜索词
│
▼
┌───────────────────┐
│ SFindInBlueprints │ ──────────────────────────────────────────┐
│ MakeSearchQuery () │ │
└───────────────────┘ │
│ │
├── 本地搜索 ──────────────────────────────┐ │
│ (Find Within Blueprint) │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ QuerySingleBlueprint│ │
│ │ (同步,主线程) │ │
│ └──────────────────┘ │
│ │ │
└── 全局搜索 ──────────────────────────────┼────────────┘
(Find In All Blueprints) │
│ │
▼ │
┌──────────────────┐ │
│ FStreamSearch │ │
│ (异步搜索线程) │ │
└──────────────────┘ │
│ │
▼ │
┌──────────────────┐ │
│ BeginSearchQuery │ │
└──────────────────┘ │
│ │
▼ │
┌──────────────────┐ │
│ ContinueSearchQuery │ ◀──────────────┘
│ (循环获取SearchData)│
└──────────────────┘
│
▼
┌──────────────────┐
│ FFiBSearchInstance│
│ StartSearchQuery │
└──────────────────┘
│
▼
┌──────────────────┐
│ FImaginaryBlueprint│
│ 解析JSON并匹配 │
└──────────────────┘
│
▼
┌──────────────────┐
│ 生成搜索结果树 │
│ FFindInBlueprintsResult │
└──────────────────┘
│
▼
┌──────────────────┐
│ 更新UI显示结果 │
│ TreeView刷新 │
└──────────────────┘
3. 核心类详解
3.1 类关系图
┌─────────────────────────────────────────────────────────────────────────┐
│ 核心类关系 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ FFindInBlueprintSearchManager │ │
│ │ (单例管理器) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ - TMap <FSoftObjectPath , int32> SearchMap │ │ │
│ │ │ - TArray <FSearchData > SearchArray │ │ │
│ │ │ - TMap <FStreamSearch *, FActiveSearchQueryPtr > ActiveSearchQueries │
│ │ │ - TSet <FSoftObjectPath > PendingAssets │ │ │
│ │ │ - TSet <FSoftObjectPath > UnindexedAssets │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ 1 │
│ │ │
│ │ 管理 * │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ FSearchData │ │
│ │ (搜索数据条目) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ - TWeakObjectPtr <UBlueprint > Blueprint │ │ │
│ │ │ - FSoftObjectPath AssetPath │ │ │
│ │ │ - FString Value (JSON编码数据 ) │ │ │
│ │ │ - FImaginaryFiBDataSharedPtr ImaginaryBlueprint │ │ │
│ │ │ - FSearchDataVersionInfo VersionInfo │ │ │
│ │ │ - ESearchDataStateFlags StateFlags │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ 1 │
│ │ │
│ │ 包含 1 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ FImaginaryBlueprint │ │
│ │ (虚拟蓝图) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ - FString BlueprintPath │ │ │
│ │ │ - TMap <int32, FText > LookupTable │ │ │
│ │ │ - TArray <FImaginaryFiBDataSharedPtr > ParsedChildData │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ 1 │
│ │ │
│ │ 包含 * │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ FImaginaryFiBData │ │
│ │ (虚拟数据基类) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ 派生类: │ │ │
│ │ │ - FImaginaryGraph (图表) │ │ │
│ │ │ - FImaginaryGraphNode (节点) │ │ │
│ │ │ - FImaginaryPin (引脚) │ │ │
│ │ │ - FImaginaryProperty (属性) │ │ │
│ │ │ - FImaginaryComponent (组件) │ │ │
│ │ │ - FCategorySectionHelper (分类) │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.2 文件结构
文件
描述
FindInBlueprintManager.h/cpp
搜索管理器和核心类型定义
FindInBlueprints.h/cpp
UI控件和结果类
ImaginaryBlueprintData.h/cpp
虚拟数据层
FiBSearchInstance.h/cpp
搜索实例和表达式解析
4. UI 层 - SFindInBlueprints
4.1 SFindInBlueprints 控件
SFindInBlueprints 是 Find In Blueprint 功能的主要 Slate 控件。
cpp
class KISMET_API SFindInBlueprints : public SCompoundWidget
{
public :
SLATE_BEGIN_ARGS (SFindInBlueprints)
: _bIsSearchWindow(true )
, _bHideSearchBar(false )
, _bHideFindGlobalButton(false )
, _ContainingTab()
{}
SLATE_END_ARGS ()
void Construct (const FArguments& InArgs, TSharedPtr<FBlueprintEditor> InBlueprintEditor = nullptr ) ;
};
4.2 主要功能方法
cpp
void FocusForUse (bool bSetFindWithinBlueprint, FString NewSearchTerms = FString(), bool bSelectFirstResult = false ) ;
void MakeSearchQuery (FString InSearchString, bool bInIsFindWithinBlueprint,
const FStreamSearchOptions& InSearchOptions = FStreamSearchOptions(),
FOnSearchComplete InOnSearchComplete = FOnSearchComplete()) ;
void CacheAllBlueprints (const FFindInBlueprintCachingOptions& InOptions) ;
void ClearResults () ;
bool IsSearchInProgress () const ;
4.3 UI 组件层次
SFindInBlueprints
│
├── SBorder (主边框)
│ │
│ └── SVerticalBox (主垂直布局)
│ │
│ ├── SHorizontalBox (搜索栏)
│ │ │
│ │ ├── SSearchBox (搜索输入框)
│ │ │
│ │ ├── SButton (全局搜索按钮) [可选]
│ │ │
│ │ └── SButton (锁定按钮) [可选]
│ │
│ ├── SBorder (结果区域)
│ │ │
│ │ └── STreeView<FSearchResult> (结果树)
│ │
│ ├── SHorizontalBox (搜索进度栏)
│ │ │
│ │ ├── STextBlock ("Searching...")
│ │ │
│ │ ├── SThrobber (加载动画)
│ │ │
│ │ └── SProgressBar (进度条)
│ │
│ └── CacheBarSlot (缓存状态栏) [动态添加]
│ │
│ ├── SWarningOrErrorBox (警告信息)
│ │
│ ├── SButton ("Index All")
│ │
│ ├── SButton ("Export Asset List")
│ │
│ ├── SProgressBar (缓存进度)
│ │
│ └── SButton ("Cancel")
4.4 搜索流程
cpp
void SFindInBlueprints::MakeSearchQuery (FString InSearchString, bool bInIsFindWithinBlueprint, ...)
{
SearchTextField->SetText (FText::FromString (InSearchString));
ItemsFound.Empty ();
if (InSearchString.Len () > 0 )
{
HighlightText = FText::FromString (InSearchString);
if (bInIsFindWithinBlueprint)
{
UBlueprint* Blueprint = BlueprintEditorPtr.Pin ()->GetBlueprintObj ();
FSearchData SearchData = FFindInBlueprintSearchManager::Get ().QuerySingleBlueprint (Blueprint, true );
FImaginaryFiBDataSharedPtr ImaginaryBlueprint (new FImaginaryBlueprint(...)) ;
TSharedPtr<FFiBSearchInstance> SearchInstance (new FFiBSearchInstance) ;
FSearchResult SearchResult = SearchInstance->StartSearchQuery (SearchValue, ImaginaryBlueprint);
ItemsFound = SearchResult->Children;
}
else
{
LaunchStreamThread (InSearchString, InSearchOptions, InOnSearchComplete);
}
TreeView->RequestTreeRefresh ();
}
}
4.4.1 为什么本地搜索每次都构造新的 FImaginaryBlueprint?
在本地搜索路径中,代码总是构造新的 FImaginaryBlueprint,而不使用缓存:
cpp
const bool bRebuildSearchData = true ;
FSearchData SearchData = FFindInBlueprintSearchManager::Get ().QuerySingleBlueprint (Blueprint, bRebuildSearchData);
const bool bHasValidSearchData = SearchData.IsValid () && !SearchData.Value.IsEmpty ();
if (bHasValidSearchData)
{
FImaginaryFiBDataSharedPtr ImaginaryBlueprint (new FImaginaryBlueprint(
Blueprint->GetName(),
Blueprint->GetPathName(),
ParentClass,
Interfaces,
SearchData.Value,
SearchData.VersionInfo
)) ;
}
原因 1: bRebuildSearchData = true 只更新 Value,不更新缓存
QuerySingleBlueprint(Blueprint, true) 内部调用 AddOrUpdateBlueprintSearchMetadata:
cpp
SearchData.Value = GatherBlueprintSearchMetadata <FFindInBlueprintJsonWriter>(InBlueprint, ...);
AddOrUpdateBlueprintSearchMetadata 重新收集蓝图元数据生成新的 SearchData.Value,但不会更新 SearchData.ImaginaryBlueprint(缓存的虚拟数据对象)。
原因 2: 本地搜索需要反映当前编辑状态
本地搜索的典型场景是用户正在编辑蓝图:
用户可能刚添加/删除/修改了节点
缓存的 ImaginaryBlueprint 是上次索引时的快照
必须从新收集的 SearchData.Value 重新构造,才能搜索到最新内容
原因 3: 条件检查的差异
代码检查的是:
cpp
const bool bHasValidSearchData = SearchData.IsValid () && !SearchData.Value.IsEmpty ();
这里检查 SearchData.Value(新收集的编码字符串),而非 SearchData.ImaginaryBlueprint(可能过时的缓存)。
本地搜索 vs 全局搜索对比
特性
本地搜索 (Find Within Blueprint)
全局搜索 (Find In All Blueprints)
数据来源
新收集的 SearchData.Value
缓存的 SearchData.ImaginaryBlueprint
是否使用缓存
❌ 不使用
✅ 使用
执行方式
同步(主线程)
异步(FStreamSearch 线程)
数据新鲜度
实时(反映当前编辑状态)
可能滞后(上次索引时的快照)
性能
较慢(每次重新收集+解析)
较快(复用已解析的虚拟数据)
全局搜索如何使用缓存
cpp
if (QueryResult.ImaginaryBlueprint.IsValid ())
{
SearchInstance->StartSearchQuery (*SearchValue, QueryResult.ImaginaryBlueprint);
}
全局搜索从 SearchArray 获取 FSearchData,直接使用其中缓存的 ImaginaryBlueprint,避免重复解析。
流程对比图
┌─────────────────────────────────────────────────────────────────────────────┐
│ 本地搜索 (Find Within Blueprint) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ UBlueprint ──▶ QuerySingleBlueprint (true) ──▶ GatherBlueprintSearchMetadata │
│ │ │ │
│ │ ▼ │
│ │ SearchData.Value (新) │
│ │ │ │
│ │ ▼ │
│ │ new FImaginaryBlueprint (Value) │
│ │ │ │
│ └──────────────────────────────────────────────────┼───────────────────│
│ ▼ │
│ 搜索最新内容 │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ 全局搜索 (Find In All Blueprints) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SearchArray ──▶ ContinueSearchQuery () ──▶ FSearchData │
│ │ │
│ ▼ │
│ SearchData.ImaginaryBlueprint (缓存) │
│ │ │
│ ▼ │
│ 直接搜索缓存 │
└─────────────────────────────────────────────────────────────────────────────┘
潜在优化
理论上可以优化:如果蓝图未被修改(Blueprint->GetPackage()->IsDirty() == false),可以复用缓存的 ImaginaryBlueprint。但当前实现选择了更保守的策略——总是重新构造 ,以确保搜索结果的准确性。
5. 搜索管理器 - FFindInBlueprintSearchManager
5.1 单例模式
cpp
class KISMET_API FFindInBlueprintSearchManager : public FTickableEditorObject
{
public :
static FFindInBlueprintSearchManager* Instance;
static FFindInBlueprintSearchManager& Get () ;
FFindInBlueprintSearchManager (const FFindInBlueprintSearchManager&) = delete ;
FFindInBlueprintSearchManager& operator =(const FFindInBlueprintSearchManager&) = delete ;
};
FFindInBlueprintSearchManager& FFindInBlueprintSearchManager::Get ()
{
if (Instance == nullptr )
{
Instance = new FFindInBlueprintSearchManager ();
}
return *Instance;
}
5.2 初始化流程
编辑器启动
│
▼
FFindInBlueprintSearchManager::Get () 首次调用
│
▼
构造函数 FFindInBlueprintSearchManager ()
│
▼
Initialize ()
│
├──▶ 注册 Asset Registry 回调
│ ├── OnAssetAdded
│ ├── OnAssetRemoved
│ ├── OnAssetRenamed
│ ├── OnAssetLoaded
│ └── OnAssetRegistryFilesLoaded
│
├──▶ 注册 GC 回调
│ ├── PreGarbageCollect → PauseFindInBlueprintSearch ()
│ └── PostGarbageCollect → UnpauseFindInBlueprintSearch ()
│
├──▶ 注册蓝图卸载回调
│ └── OnBlueprintUnloaded
│
├──▶ 注册热重载回调
│ └── OnReloadComplete
│
└──▶ BuildCache () - 构建初始搜索索引
5.3 核心数据结构
cpp
private :
TMap<FSoftObjectPath, int32> SearchMap;
TArray<FSearchData> SearchArray;
FThreadSafeCounter ActiveSearchCounter;
TMap<const FStreamSearch*, FActiveSearchQueryPtr> ActiveSearchQueries;
TSet<FSoftObjectPath> PendingAssets;
TSet<FSoftObjectPath> UnindexedAssets;
TSet<FSoftObjectPath> FailedToCachePaths;
TSet<FSoftObjectPath> AssetsToIndexOnFirstSearch;
5.4 搜索查询 API
cpp
void BeginSearchQuery (const FStreamSearch* InSearchOriginator) ;
bool ContinueSearchQuery (const FStreamSearch* InSearchOriginator, FSearchData& OutSearchData) ;
void EnsureSearchQueryEnds (const FStreamSearch* InSearchOriginator) ;
FSearchData QuerySingleBlueprint (UBlueprint* InBlueprint, bool bInRebuildSearchData) ;
float GetPercentComplete (const FStreamSearch* InSearchOriginator) const ;
5.5 索引管理 API
cpp
void AddOrUpdateBlueprintSearchMetadata (UBlueprint* InBlueprint,
EAddOrUpdateBlueprintSearchMetadataFlags InFlags = EAddOrUpdateBlueprintSearchMetadataFlags::None,
EFiBVersion InVersion = EFiBVersion::FIB_VER_NONE) ;
void ApplySearchDataToDatabase (FSearchData InSearchData, bool bAllowNewEntry = false ) ;
FSearchData GetSearchDataForAssetPath (const FSoftObjectPath& InAssetPath) ;
bool ProcessEncodedValueForUnloadedBlueprint (FSearchData& SearchData) ;
5.6 缓存操作 API
cpp
void CacheAllAssets (TWeakPtr<SFindInBlueprints> InSourceWidget,
const FFindInBlueprintCachingOptions& InCachingOptions) ;
void CancelCacheAll (SFindInBlueprints* InFindInBlueprintWidget) ;
int32 GetNumberUnindexedAssets () const ;
int32 GetNumberUncachedAssets () const ;
bool IsCacheInProgress () const ;
float GetCacheProgress () const ;
void ExportOutdatedAssetList () ;
6. 异步搜索 - FStreamSearch
6.1 FStreamSearch 类
FStreamSearch 是一个 FRunnable 实现,在独立线程上执行搜索操作。
cpp
class KISMET_API FStreamSearch : public FRunnable
{
public :
FStreamSearch (const FString& InSearchValue,
const FStreamSearchOptions& InSearchOptions = FStreamSearchOptions ());
virtual bool Init () override ;
virtual uint32 Run () override ;
virtual void Stop () override ;
virtual void Exit () override ;
void EnsureCompletion () ;
bool IsComplete () const ;
bool WasStopped () const ;
void GetFilteredItems (TArray<FSearchResult>& OutItemsFound) ;
float GetPercentComplete () const ;
int32 GetOutOfDateCount () const { return BlueprintCountBelowVersion; }
public :
TUniquePtr<FRunnableThread> Thread;
TArray<FSearchResult> ItemsFound;
FString SearchValue;
FStreamSearchOptions SearchOptions;
FCriticalSection SearchCriticalSection;
TArray<FImaginaryFiBDataSharedPtr> FilteredImaginaryResults;
int32 BlueprintCountBelowVersion;
bool bThreadCompleted;
private :
int32 SearchId;
FThreadSafeCounter StopTaskCounter;
};
6.2 搜索选项
cpp
struct KISMET_API FStreamSearchOptions
{
ESearchQueryFilter ImaginaryDataFilter;
EFiBVersion MinimiumVersionRequirement;
FStreamSearchOptions ()
: ImaginaryDataFilter (ESearchQueryFilter::AllFilter)
, MinimiumVersionRequirement (EFiBVersion::FIB_VER_LATEST)
{
}
};
6.3 搜索执行流程
cpp
uint32 FStreamSearch::Run ()
{
const double StartTime = FPlatformTime::Seconds ();
FFindInBlueprintSearchManager::Get ().BeginSearchQuery (this );
TFunction<void (const FSearchResult&)> OnResultReady = [this ](const FSearchResult& Result) {
FScopeLock ScopeLock (&SearchCriticalSection);
ItemsFound.Add (Result);
};
FSearchData QueryResult;
while (FFindInBlueprintSearchManager::Get ().ContinueSearchQuery (this , QueryResult))
{
if (QueryResult.ImaginaryBlueprint.IsValid ())
{
if (QueryResult.VersionInfo.FiBDataVersion < SearchOptions.MinimiumVersionRequirement)
{
++BlueprintCountBelowVersion;
}
TSharedPtr<FFiBSearchInstance> SearchInstance (new FFiBSearchInstance) ;
FSearchResult SearchResult;
if (SearchOptions.ImaginaryDataFilter != ESearchQueryFilter::AllFilter)
{
SearchInstance->MakeSearchQuery (*SearchValue, QueryResult.ImaginaryBlueprint);
SearchInstance->CreateFilteredResultsListFromTree (SearchOptions.ImaginaryDataFilter, FilteredImaginaryResults);
SearchResult = SearchInstance->GetSearchResults (QueryResult.ImaginaryBlueprint);
}
else
{
SearchResult = SearchInstance->StartSearchQuery (*SearchValue, QueryResult.ImaginaryBlueprint);
}
if (SearchResult.IsValid () && SearchResult->Children.Num () != 0 )
{
OnResultReady (SearchResult);
}
}
}
FFindInBlueprintSearchManager::Get ().EnsureSearchQueryEnds (this );
bThreadCompleted = true ;
UE_LOG (LogFindInBlueprint, Log, TEXT ("Search completed in %0.2f seconds." ),
FPlatformTime::Seconds () - StartTime);
return 0 ;
}
6.4 时序图
主线程 搜索线程 (FStreamSearch)
│ │
│ 创建 FStreamSearch │
│ LaunchStreamThread() │
│───────────────────────────────────────▶│
│ │
│ RegisterActiveTimer() │ Init()
│ │
│ │ Run()
│ │ │
│ │ ▼
│ │ BeginSearchQuery()
│ │ │
│ │ ▼
│ │ while (ContinueSearchQuery())
│ │ {
│ UpdateSearchResults() ◀──────────────│ 搜索并添加结果
│ (定时器回调) │ }
│ │ │
│ ▼ │
│ GetFilteredItems() │
│ │ │
│ ▼ │
│ 更新 TreeView │
│ │
│ │ EnsureSearchQueryEnds()
│ │ │
│ │ ▼
│ │ bThreadCompleted = true
│ │
│ 检测到 IsComplete() │
│ │ │
│ ▼ │
│ StreamSearch.Reset() │
│ │
▼ ▼
7. 虚拟数据层 - ImaginaryBlueprintData
7.1 概述
"虚拟"(Imaginary) 数据层是 Find In Blueprint 的核心创新,它允许在不加载蓝图资产的情况下搜索蓝图内容 。
为什么需要虚拟数据层?
问题
传统方式
虚拟数据层解决方案
内存占用
加载完整 UBlueprint 对象(可能很大)
使用轻量级 JSON 解析结构
搜索速度
需要加载资产才能搜索
直接从索引数据搜索
线程安全
UObject 只能在主线程访问
虚拟数据可在工作线程安全操作
大规模搜索
加载数千蓝图会导致内存溢出
按需解析,内存可控
核心设计理念
蓝图资产 (UBlueprint) 虚拟蓝图 (FImaginaryBlueprint)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• 完整的 UObject 对象 • 轻量级 C++ 结构
• 包含所有图表、节点、引脚 • 仅包含可搜索的元数据
• 需要加载到内存 • 从 JSON 索引按需解析
• 只能在主线程访问 • 线程安全,可并行搜索
• 内存占用大 • 内存占用小
7.2 类层次结构
FImaginaryFiBData (基类)
│
├── FImaginaryBlueprint (虚拟蓝图)
│
├── FImaginaryGraph (虚拟图表)
│
├── FImaginaryGraphNode (虚拟节点)
│
├── FImaginaryPin (虚拟引脚)
│
├── FImaginaryProperty (虚拟属性)
│ │
│ └── FImaginaryComponent (虚拟组件)
│
├── FCategorySectionHelper (分类辅助)
│
└── FFiBMetaData (元数据)
7.3 FImaginaryFiBData 基类
cpp
class FImaginaryFiBData : public ITextFilterExpressionContext,
public TSharedFromThis<FImaginaryFiBData, ESPMode::ThreadSafe>
{
public :
FImaginaryFiBData (FImaginaryFiBDataWeakPtr InOuter,
TSharedPtr<FJsonObject> InUnparsedJsonObject = TSharedPtr <FJsonObject>(),
TMap<int32, FText>* InLookupTablePtr = nullptr );
virtual bool IsCategory () const { return false ; }
virtual bool IsTagAndValueCategory () const { return false ; }
virtual bool IsCompatibleWithFilter (ESearchQueryFilter InSearchQueryFilter) const ;
virtual bool CanCallFilter (ESearchQueryFilter InSearchQueryFilter) const ;
void ParseAllChildData (ESearchableValueStatus InSearchabilityOverride = ESearchableValueStatus::Searchable) ;
virtual bool TestBasicStringExpression (const FTextFilterString& InValue,
const ETextFilterTextComparisonMode InTextComparisonMode,
TMultiMap<const FImaginaryFiBData*, FComponentUniqueDisplay>& InOutMatchingSearchComponents) const ;
virtual bool TestComplexExpression (const FName& InKey, const FTextFilterString& InValue,
const ETextFilterComparisonOperation InComparisonOperation,
const ETextFilterTextComparisonMode InTextComparisonMode,
TMultiMap<const FImaginaryFiBData*, FComponentUniqueDisplay>& InOutMatchingSearchComponents) const ;
virtual UObject* GetObject (UBlueprint* InBlueprint) const ;
virtual UBlueprint* GetBlueprint () const ;
FSearchResult CreateSearchResult (FSearchResult InParent) const ;
const TArray<FImaginaryFiBDataSharedPtr>& GetAllParsedChildData () const ;
static FSearchResult CreateSearchTree (FSearchResult InParentSearchResult,
FImaginaryFiBDataWeakPtr InCurrentPointer,
TArray<const FImaginaryFiBData*>& InValidSearchResults,
TMultiMap<const FImaginaryFiBData*, FComponentUniqueDisplay>& InMatchingSearchComponents) ;
protected :
TSharedPtr<FJsonObject> UnparsedJsonObject;
TArray<FImaginaryFiBDataSharedPtr> ParsedChildData;
TMultiMap<FindInBlueprintsHelpers::FSimpleFTextKeyStorage, FSearchableValueInfo> ParsedTagsAndValues;
TMap<int32, FText>* LookupTablePtr;
FImaginaryFiBDataWeakPtr Outer;
TAtomic<bool > bHasParsedJsonObject;
static FCriticalSection ParseChildDataCriticalSection;
};
7.4 FImaginaryBlueprint 详解
FImaginaryBlueprint 是虚拟数据层的根节点类 ,代表一个蓝图资产的可搜索内容。
核心作用
内存优化 - 避免为搜索而加载完整的 UBlueprint 对象(可能很大)
异步搜索 - 可在工作线程上安全解析和匹配,无需访问 UObject
延迟解析 - 按需将 JSON 索引数据解析为可搜索的结构
缓存复用 - 解析后的虚拟蓝图可被多次搜索复用
类定义
cpp
class FImaginaryBlueprint : public FImaginaryFiBData
{
public :
FImaginaryBlueprint (
const FString& InBlueprintName,
const FString& InBlueprintPath,
const FString& InBlueprintParentClass,
const TArray<FString>& InInterfaces,
const FString& InUnparsedStringData,
FSearchDataVersionInfo InVersionInfo
);
virtual bool IsCompatibleWithFilter (ESearchQueryFilter InSearchQueryFilter) const override ;
virtual bool CanCallFilter (ESearchQueryFilter InSearchQueryFilter) const override ;
virtual UBlueprint* GetBlueprint () const override ;
protected :
virtual bool TrySpecialHandleJsonValue (FText InKey, TSharedPtr<FJsonValue> InJsonValue) override ;
virtual FSearchResult CreateSearchResult_Internal (FSearchResult InTemplate) const override ;
void ParseGraph (TSharedPtr<FJsonValue> InJsonValue, FString InCategoryTitle, EGraphType InGraphType) ;
void ParseComponents (TSharedPtr<FJsonObject> InJsonObject, TArray<FImaginaryFiBDataSharedPtr>& OutParsedChildData) ;
void ParseToJson (FSearchDataVersionInfo InVersionInfo, const FString& UnparsedStringData) ;
protected :
FString BlueprintPath;
TMap<int32, FText> LookupTable;
};
工作流程
JSON 索引数据 (FSearchData.Value)
│
▼
FImaginaryBlueprint 构造函数
│
├── 保存基本信息 (路径、父类、接口)
│
└── ParseToJson () ← 解析 JSON 字符串
│
├── 构建 LookupTable (文本查找表)
│
└── 存储 UnparsedJsonObject
│
▼ (首次搜索时)
ParseAllChildData ()
│
├── ParseGraph () → FImaginaryGraph
│ │
│ └── 解析节点 → FImaginaryGraphNode
│ │
│ └── 解析引脚 → FImaginaryPin
│
├── ParseComponents () → FImaginaryComponent
│
└── 解析属性 → FImaginaryProperty
│
▼
虚拟蓝图树 (内存中的轻量结构)
│
▼
TestBasicStringExpression () ← 搜索匹配
│
▼
CreateSearchResult () → FFindInBlueprintsResult
关键特性
特性
说明
线程安全
使用 ESPMode::ThreadSafe 共享指针,可在工作线程安全访问
延迟子数据解析
只在首次搜索时解析子节点,避免不必要的内存占用
查找表机制
存储 TMap<int32, FText> 用于高效反序列化文本值
过滤器兼容
支持 ESearchQueryFilter 过滤特定类型(节点、引脚等)
版本感知
根据 FSearchDataVersionInfo 处理不同版本的索引格式
与 FSearchData 的关系
cpp
struct FSearchData
{
TWeakObjectPtr<UBlueprint> Blueprint;
FSoftObjectPath AssetPath;
FString Value;
FImaginaryFiBDataSharedPtr ImaginaryBlueprint;
FSearchDataVersionInfo VersionInfo;
ESearchDataStateFlags StateFlags;
};
使用示例
cpp
FSearchData& SearchData = ...;
if (SearchData.HasEncodedValue () && !SearchData.ImaginaryBlueprint.IsValid ())
{
SearchData.ImaginaryBlueprint = MakeShared <FImaginaryBlueprint, ESPMode::ThreadSafe>(
FPaths::GetBaseFilename (SearchData.AssetPath.ToString ()),
SearchData.AssetPath.ToString (),
SearchData.ParentClass,
SearchData.Interfaces,
SearchData.Value,
SearchData.VersionInfo
);
SearchData.ClearEncodedValue ();
}
if (SearchData.ImaginaryBlueprint.IsValid ())
{
TSharedPtr<FFiBSearchInstance> SearchInstance = MakeShared <FFiBSearchInstance>();
FSearchResult Result = SearchInstance->StartSearchQuery (SearchValue, SearchData.ImaginaryBlueprint);
if (Result.IsValid () && Result->Children.Num () > 0 )
{
}
}
7.5 搜索值状态
cpp
enum ESearchableValueStatus
{
NotSearchable = 0x00000000 ,
Searchable = 0x00000001 ,
Hidden = 0x00000002 ,
Explicit = 0x00000004 ,
CoreDisplayItem = Hidden | Searchable,
ExplicitySearchable = Explicit | Searchable,
ExplicitySearchableHidden = Explicit | Searchable | Hidden,
AllSearchable = CoreDisplayItem | ExplicitySearchable,
};
8. 搜索结果 - FFindInBlueprintsResult
8.1 基类
cpp
class KISMET_API FFindInBlueprintsResult : public TSharedFromThis<FFindInBlueprintsResult>
{
public :
FFindInBlueprintsResult () = default ;
virtual ~FFindInBlueprintsResult () = default ;
explicit FFindInBlueprintsResult (const FText& InDisplayText) ;
virtual FReply OnClick () ;
virtual FText GetCategory () const ;
virtual TSharedRef<SWidget> CreateIcon () const ;
virtual void FinalizeSearchData () {};
FString GetCommentText () const ;
UBlueprint* GetParentBlueprint () const ;
virtual void ParseSearchInfo (FText InKey, FText InValue) {};
virtual UObject* GetObject (UBlueprint* InBlueprint) const ;
FText GetDisplayString () const ;
public :
TArray<TSharedPtr<FFindInBlueprintsResult>> Children;
TWeakPtr<FFindInBlueprintsResult> Parent;
FText DisplayText;
FString CommentText;
};
typedef TSharedPtr<FFindInBlueprintsResult> FSearchResult;
8.2 派生类
FFindInBlueprintsGraphNode (节点结果)
cpp
class FFindInBlueprintsGraphNode : public FFindInBlueprintsResult
{
public :
virtual FReply OnClick () override ;
virtual TSharedRef<SWidget> CreateIcon () const override ;
virtual void ParseSearchInfo (FText InKey, FText InValue) override ;
virtual FText GetCategory () const override ;
virtual void FinalizeSearchData () override ;
virtual UObject* GetObject (UBlueprint* InBlueprint) const override ;
private :
FGuid NodeGuid;
FSlateIcon Glyph;
FLinearColor GlyphColor;
UClass* Class;
FString ClassName;
};
FFindInBlueprintsPin (引脚结果)
cpp
class FFindInBlueprintsPin : public FFindInBlueprintsResult
{
public :
FFindInBlueprintsPin (FString InSchemaName);
virtual TSharedRef<SWidget> CreateIcon () const override ;
virtual void ParseSearchInfo (FText InKey, FText InValue) override ;
virtual FText GetCategory () const override ;
virtual void FinalizeSearchData () override ;
private :
FString SchemaName;
FEdGraphPinType PinType;
FSlateColor IconColor;
};
FFindInBlueprintsProperty (属性结果)
cpp
class FFindInBlueprintsProperty : public FFindInBlueprintsResult
{
public :
virtual FReply OnClick () override ;
virtual TSharedRef<SWidget> CreateIcon () const override ;
virtual void ParseSearchInfo (FText InKey, FText InValue) override ;
virtual FText GetCategory () const override ;
virtual void FinalizeSearchData () override ;
private :
FEdGraphPinType PinType;
FString DefaultValue;
bool bIsSCSComponent;
};
FFindInBlueprintsGraph (图表结果)
cpp
class FFindInBlueprintsGraph : public FFindInBlueprintsResult
{
public :
FFindInBlueprintsGraph (EGraphType InGraphType);
virtual FReply OnClick () override ;
virtual TSharedRef<SWidget> CreateIcon () const override ;
virtual void ParseSearchInfo (FText InKey, FText InValue) override ;
virtual FText GetCategory () const override ;
private :
EGraphType GraphType;
};
8.3 结果层次结构示例
FSearchResult (根)
│
├── FFindInBlueprintsResult: "/Game/Blueprints/BP_Character"
│ │
│ ├── FFindInBlueprintsGraph: "EventGraph" (GT_Ubergraph)
│ │ │
│ │ ├── FFindInBlueprintsGraphNode: "BeginPlay"
│ │ │ │
│ │ │ └── FFindInBlueprintsPin: "Execute"
│ │ │
│ │ └── FFindInBlueprintsGraphNode: "PrintString"
│ │ │
│ │ ├── FFindInBlueprintsPin: "InString"
│ │ │
│ │ └── FFindInBlueprintsPin: "Target"
│ │
│ ├── FFindInBlueprintsGraph: "CalculateDamage" (GT_Function)
│ │ │
│ │ └── ...
│ │
│ └── FFindInBlueprintsProperty: "Health" (变量)
│
└── FFindInBlueprintsResult: "/Game/Blueprints/BP_Enemy"
│
└── ...
9. 搜索数据结构
9.1 FSearchData
cpp
struct FSearchData
{
TWeakObjectPtr<UBlueprint> Blueprint;
FSoftObjectPath AssetPath;
FString Value;
FName AssetKeyForValue;
FString ParentClass;
TArray<FString> Interfaces;
FImaginaryFiBDataSharedPtr ImaginaryBlueprint;
FSearchDataVersionInfo VersionInfo;
ESearchDataStateFlags StateFlags;
bool IsValid () const { return !AssetPath.IsNull (); }
bool HasEncodedValue () const { return !Value.IsEmpty () || !AssetKeyForValue.IsNone (); }
void ClearEncodedValue () { Value.Reset (); AssetKeyForValue = NAME_None; }
bool IsIndexingCompleted () const { return EnumHasAllFlags (StateFlags, ESearchDataStateFlags::IsIndexed); }
bool IsMarkedForDeletion () const { return EnumHasAllFlags (StateFlags, ESearchDataStateFlags::WasRemoved); }
};
9.2 状态标志
cpp
enum class ESearchDataStateFlags : uint8
{
None = 0 ,
IsIndexed = 1 << 0 ,
WasRemoved = 1 << 1 ,
};
9.3 版本信息
cpp
struct FSearchDataVersionInfo
{
int32 FiBDataVersion = EFiBVersion::FIB_VER_NONE;
int32 EditorObjectVersion = -1 ;
static FSearchDataVersionInfo Current;
};
9.4 搜索标签
cpp
struct KISMET_API FFindInBlueprintSearchTags
{
static const FText FiB_Properties;
static const FText FiB_Components;
static const FText FiB_IsSCSComponent;
static const FText FiB_Nodes;
static const FText FiB_SchemaName;
static const FText FiB_UberGraphs;
static const FText FiB_Functions;
static const FText FiB_Macros;
static const FText FiB_SubGraphs;
static const FText FiB_ExtensionGraphs;
static const FText FiB_Extensions;
static const FText FiB_Name;
static const FText FiB_NativeName;
static const FText FiB_ClassName;
static const FText FiB_NodeGuid;
static const FText FiB_DefaultValue;
static const FText FiB_Tooltip;
static const FText FiB_Description;
static const FText FiB_Comment;
static const FText FiB_Path;
static const FText FiB_ParentClass;
static const FText FiB_Interfaces;
static const FText FiB_FuncOriginClass;
static const FText FiB_Pins;
static const FText FiB_PinCategory;
static const FText FiB_PinSubCategory;
static const FText FiB_ObjectClass;
static const FText FiB_IsArray;
static const FText FiB_IsReference;
static const FText FiB_Glyph;
static const FText FiB_GlyphStyleSet;
static const FText FiB_GlyphColor;
static const FText FiBMetaDataTag;
};
10. 搜索语法
10.1 基本搜索
语法
描述
示例
关键词
简单文本搜索
BeginPlay
关键词1 关键词2
AND 搜索
Print String
"精确短语"
精确匹配
"Print String"
10.2 高级搜索语法
Find In Blueprint 使用 FTextFilterExpressionEvaluator 支持复杂表达式:
语法
描述
示例
Tag=Value
标签匹配
Name=BeginPlay
Tag="Value"
精确标签匹配
Name="Print String"
Tag=+Value
精确值匹配
ClassName=+K2Node_CallFunction
Tag!=Value
不等于
Name!=BeginPlay
A && B
AND 操作
Name=Print && Pins(Name=Target)
A || B
OR 操作
Name=BeginPlay || Name=Tick
!A
NOT 操作
!Name=BeginPlay
(A && B)
分组
(Name=Print) && (Pins(Name=Target))
10.3 嵌套搜索
Nodes (Name=PrintString && Pins (Name=InString))
Nodes ("Native Name" =+"PrintString" && (Pins (Name=Target && ObjectClass=+"/Script/Engine.KismetSystemLibrary" ) || FuncOriginClass=+"/Script/Engine.KismetSystemLibrary" ))
10.4 可用标签
标签
描述
示例
Name
名称
Name=MyVariable
ClassName
类名
ClassName=K2Node_Event
NodeGuid
节点 GUID
NodeGuid=...
DefaultValue
默认值
DefaultValue=100
Comment
注释
Comment=TODO
ParentClass
父类
ParentClass=Character
Interfaces
接口
Interfaces=BPI_Interactable
PinCategory
引脚类别
PinCategory=float
ObjectClass
对象类
ObjectClass=Actor
Nodes(...)
节点过滤
Nodes(Name=BeginPlay)
Pins(...)
引脚过滤
Pins(Name=Target)
Properties(...)
属性过滤
Properties(Name=Health)
Functions(...)
函数过滤
Functions(Name=CalculateDamage)
10.5 编程构建搜索词
cpp
bool FindInBlueprintsHelpers::ConstructSearchTermFromFunction (const UFunction* Function, FString& SearchTerm)
{
if (!Function)
return false ;
const UClass* FuncOriginClass = GetFunctionOriginClass (Function);
if (!FuncOriginClass)
return false ;
const FString FunctionNativeName = Function->GetName ();
const FString TargetTypeName = FuncOriginClass->GetPathName ();
SearchTerm = FString::Printf (
TEXT ("Nodes(\"Native Name\"=+\"%s\" && (Pins(Name=Target && ObjectClass=+\"%s\") || FuncOriginClass=+\"%s\"))" ),
*FunctionNativeName,
*TargetTypeName,
*TargetTypeName
);
return true ;
}
11. 索引与缓存机制
11.1 索引数据来源
搜索索引数据存储在蓝图资产的元数据标签中:
蓝图资产 (.uasset)
│
└── Asset Registry Tags
│
├── "FiBData" (FBlueprintTags ::FindInBlueprintsData )
│ └── 版本化的 JSON 编码搜索数据(当前使用)
│
└── "FiB" (FBlueprintTags ::UnversionedFindInBlueprintsData )
└── 旧版无版本的搜索数据(已废弃)
GatherBlueprintSearchMetadata 是收集蓝图可搜索元数据的核心函数 ,它将蓝图中的所有可搜索内容提取并序列化为 JSON 格式的索引数据。
函数签名
cpp
template <class PrintPolicy>
void GatherBlueprintSearchMetadata (
const TSharedRef<TFindInBlueprintJsonStringWriter<PrintPolicy>>& InWriter,
const UBlueprint* Blueprint) ;
template <class JsonWriterType>
FString GatherBlueprintSearchMetadata (const UBlueprint* Blueprint, int32 FiBDataVersion) ;
收集的内容
类别
搜索标签
描述
收集函数
属性/变量
FiB_Properties
蓝图中定义的所有变量 (NewVariables)
SaveVariableDescriptionToJson()
UberGraph
FiB_UberGraphs
EventGraph 等主图表
GatherGraphSearchData()
函数图
FiB_Functions
函数图 + 接口实现图
GatherGraphSearchData()
宏图
FiB_Macros
宏定义图表
GatherGraphSearchData()
子图
FiB_SubGraphs
嵌套的子图表
GatherGraphSearchData()
扩展图
FiB_ExtensionGraphs
蓝图扩展中的图表
GatherGraphSearchData()
组件
FiB_Components
SCS 组件
SavePinTypeToJson()
扩展
FiB_Extensions
蓝图扩展数据
GatherExtensionsSearchData()
执行流程
GatherBlueprintSearchMetadata(Writer, Blueprint)
│
├── WriteObjectStart()
│
├── 收集属性 (FiB_Properties)
│ │
│ └── for (Variable : Blueprint->NewVariables)
│ └── SaveVariableDescriptionToJson ()
│
├── 收集 UberGraph (FiB_UberGraphs)
│ └── GatherGraphSearchData (UbergraphPages)
│ │
│ ├── for (Graph : Graphs)
│ │ ├── 图表名称、Schema
│ │ │
│ │ └── for (Node : Graph->Nodes)
│ │ ├── 节点名称、类名、GUID
│ │ ├── 图标、颜色
│ │ │
│ │ └── for (Pin : Node->Pins)
│ │ └── 引脚名称、类型、默认值
│ │
│ └── 收集子图到 SubGraphs 列表
│
├── 收集扩展图 (FiB_ExtensionGraphs)
│ └── for (Extension : Blueprint->GetExtensions ())
│ └── GatherGraphSearchData (ExtensionGraphs)
│
├── 收集函数图 + 接口图 (FiB_Functions)
│ ├── Blueprint->FunctionGraphs
│ │
│ └── if (Version >= FIB_VER_INTERFACE_GRAPHS)
│ └── InterfaceDesc.Graphs
│
├── 收集宏图 (FiB_Macros)
│ └── GatherGraphSearchData (MacroGraphs)
│
├── 收集子图 (FiB_SubGraphs)
│ └── GatherGraphSearchData (SubGraphs)
│
├── 收集 SCS 组件 (FiB_Components)
│ └── for (Property : SkeletonGeneratedClass)
│ └── if (IsComponentProperty && IsSCSNode)
│ └── SavePinTypeToJson ()
│
├── 收集扩展数据 (FiB_Extensions)
│ └── GatherExtensionsSearchData ()
│
└── WriteObjectEnd () / Close ()
调用时机
时机
调用方
说明
蓝图保存
AddOrUpdateBlueprintSearchMetadata()
蓝图保存时自动收集并更新索引
手动索引
CacheAllAssets() / FCacheAllBlueprintsTickableObject
用户点击 "Index All" 时批量收集
查询单个蓝图
QuerySingleBlueprint()
本地搜索时重新收集
调试
GenerateSearchIndexForDebugging()
生成人类可读的索引用于调试
代码示例
cpp
FString GatherBlueprintSearchMetadata (const UBlueprint* Blueprint, int32 FiBDataVersion)
{
CSV_SCOPED_TIMING_STAT (FindInBlueprint, GatherBlueprintSearchMetadata);
FString SearchMetaData;
TSharedRef<FFindInBlueprintJsonWriter> Writer =
MakeShared <FFindInBlueprintJsonWriter>(&SearchMetaData, FiBDataVersion);
GatherBlueprintSearchMetadata <FFindInBlueprintJsonWriter::InnerPrintPolicy>(Writer, Blueprint);
return SearchMetaData;
}
void FFindInBlueprintSearchManager::AddOrUpdateBlueprintSearchMetadata (UBlueprint* InBlueprint, ...)
{
SearchData.Value = GatherBlueprintSearchMetadata <FFindInBlueprintJsonWriter>(
InBlueprint,
SearchData.VersionInfo.FiBDataVersion
);
}
辅助函数
函数
作用
GatherGraphSearchData()
收集图表及其节点、引脚数据
SaveVariableDescriptionToJson()
序列化变量描述
SavePinTypeToJson()
序列化引脚类型信息
GatherExtensionsSearchData()
收集蓝图扩展数据
GatherNodeSearchData()
收集单个节点的搜索数据
GatherPinSearchData()
收集单个引脚的搜索数据
11.3 索引时机
时机
触发条件
处理方式
蓝图保存
用户保存蓝图
同步收集并序列化
资产发现
Asset Registry 扫描
从元数据提取
蓝图加载
资产被加载到内存
可选重新生成
手动索引
用户点击 "Index All"
批量加载并索引
11.4 JSON 数据结构
json
{
"Name" : "BP_Character" ,
"Path" : "/Game/Blueprints/BP_Character" ,
"ParentClass" : "Character" ,
"Interfaces" : [ "BPI_Interactable" ] ,
"Properties" : [
{
"Name" : "Health" ,
"PinCategory" : "float" ,
"DefaultValue" : "100.0"
}
] ,
"Components" : [
{
"Name" : "CapsuleComponent" ,
"ClassName" : "UCapsuleComponent" ,
"IsSCSComponent" : true
}
] ,
"Uber" : [
{
"Name" : "EventGraph" ,
"SchemaName" : "EdGraphSchema_K2" ,
"Nodes" : [
{
"Name" : "BeginPlay" ,
"ClassName" : "K2Node_Event" ,
"NodeGuid" : "..." ,
"Glyph" : "GraphEditor.Event_16x" ,
"GlyphColor" : "(R=1.0,G=0.0,B=0.0,A=1.0)" ,
"Pins" : [
{
"Name" : "Execute" ,
"PinCategory" : "exec"
}
]
}
]
}
] ,
"Functions" : [ ...] ,
"Macros" : [ ...]
}
11.5 缓存操作类型
cpp
enum class EFiBCacheOpType
{
CachePendingAssets,
CacheUnindexedAssets
};
11.6 缓存操作标志
cpp
enum class EFiBCacheOpFlags
{
None = 0 ,
ShowProgress = 1 << 0 ,
HideNotifications = 1 << 1 ,
AllowUserCancel = 1 << 2 ,
CheckOutAndSave = 1 << 3 ,
HideProgressBars = 1 << 4 ,
AllowUserCloseProgress = 1 << 5 ,
IsCachingDiscoveredAssets = 1 << 6 ,
KeepProgressVisibleOnCompletion = 1 << 7 ,
ExecuteOnMainThread = 1 << 8 ,
ExecuteOnSingleThread = 1 << 9 ,
ExecuteGatherPhaseOnly = 1 << 10
};
11.7 缓存流程
CacheAllAssets () 调用
│
▼
创建 FCacheAllBlueprintsTickableObject
│
▼
每帧 Tick:
│
├── 获取下一个待缓存资产
│
├── 加载蓝图资产
│
├── 收集搜索元数据
│ │
│ ├── 遍历所有图表
│ │
│ ├── 遍历所有节点
│ │
│ ├── 遍历所有引脚
│ │
│ ├── 遍历所有属性
│ │
│ └── 遍历所有组件
│
├── 序列化为 JSON
│
├── 更新 SearchArray
│
└── 可选: 保存资产
│
▼
完成后调用 FinishedCachingBlueprints ()
12. 版本控制
12.1 FSearchDataVersionInfo 结构
FSearchDataVersionInfo 是 FSearchData 中的版本信息字段,用于追踪蓝图搜索数据的格式版本 ,确保搜索系统能正确处理不同版本的索引数据。
cpp
struct FSearchDataVersionInfo
{
int32 FiBDataVersion = EFiBVersion::FIB_VER_NONE;
int32 EditorObjectVersion = -1 ;
static FSearchDataVersionInfo Current;
};
字段说明
字段
类型
默认值
描述
FiBDataVersion
int32
FIB_VER_NONE (-1)
FiB 搜索数据格式版本,决定了索引包含哪些信息
EditorObjectVersion
int32
-1
编辑器对象序列化版本,用于正确反序列化 JSON 查找表中的值
12.2 FiB 版本枚举
cpp
UENUM ()
enum EFiBVersion : int
{
FIB_VER_NONE = -1 ,
FIB_VER_BASE = 0 ,
FIB_VER_VARIABLE_REFERENCE,
FIB_VER_INTERFACE_GRAPHS,
FIB_VER_FUNC_CALL_SITES,
FIB_VER_PLUS_ONE,
FIB_VER_LATEST = FIB_VER_PLUS_ONE - 1
};
12.3 版本演进
版本
值
新增功能
影响
FIB_VER_NONE
-1
-
未知/未设置版本
FIB_VER_BASE
0
基础搜索功能
节点、引脚、属性的基本搜索
FIB_VER_VARIABLE_REFERENCE
1
变量引用 (FMemberReference) 收集
可搜索变量的使用位置
FIB_VER_INTERFACE_GRAPHS
2
实现的接口图收集
可搜索接口函数实现
FIB_VER_FUNC_CALL_SITES
3
隐藏目标引脚和函数来源类收集
改进函数调用点的搜索精度
12.4 VersionInfo 的核心作用
1. 判断是否需要重新索引
cpp
struct FStreamSearchOptions
{
EFiBVersion MinimiumVersionRequirement = EFiBVersion::FIB_VER_LATEST;
};
struct FFindInBlueprintCachingOptions
{
EFiBVersion MinimiumVersionRequirement = EFiBVersion::FIB_VER_LATEST;
};
2. 搜索时统计过时蓝图
cpp
if (QueryResult.VersionInfo.FiBDataVersion < SearchOptions.MinimiumVersionRequirement)
{
++BlueprintCountBelowVersion;
}
int32 GetOutOfDateCount () const { return BlueprintCountBelowVersion; }
3. 兼容性处理
cpp
static TSharedPtr<FJsonObject> ConvertJsonStringToObject (
FSearchDataVersionInfo InVersionInfo,
const FString& InJsonString,
TMap<int32, FText>& OutFTextLookupTable
) ;
4. 缓存操作控制
cpp
void AddOrUpdateBlueprintSearchMetadata (
UBlueprint* InBlueprint,
EAddOrUpdateBlueprintSearchMetadataFlags InFlags,
EFiBVersion InVersion = EFiBVersion::FIB_VER_NONE
) ;
12.5 版本检查流程
搜索开始
│
▼
获取 FSearchData
│
▼
检查 VersionInfo.FiBDataVersion
│
├── >= MinimiumVersionRequirement
│ │
│ └── 正常搜索
│
└── < MinimiumVersionRequirement
│
├── 仍然搜索(使用现有数据)
│
├── BlueprintCountBelowVersion++
│
└── UI 显示警告:
"Search incomplete: X blueprints don't have up-to-date index"
[Index All ] [Export Asset List] [Dismiss]
12.6 版本信息在 FSearchData 中的位置
cpp
struct FSearchData
{
TWeakObjectPtr<UBlueprint> Blueprint;
FSoftObjectPath AssetPath;
FString Value;
FImaginaryFiBDataSharedPtr ImaginaryBlueprint;
FSearchDataVersionInfo VersionInfo;
ESearchDataStateFlags StateFlags;
};
12.7 版本更新时机
时机
操作
版本设置
蓝图保存
重新收集搜索元数据
FIB_VER_LATEST
手动索引
用户点击 "Index All"
FIB_VER_LATEST
资产发现
从元数据提取
保留原始版本
强制重缓存
ForceRecache 标志
FIB_VER_LATEST
13. 线程安全机制
13.1 临界区
cpp
mutable FCriticalSection SafeQueryModifyCriticalSection;
FCriticalSection PauseThreadsCriticalSection;
FCriticalSection SafeModifyCacheCriticalSection;
static FCriticalSection ParseChildDataCriticalSection;
13.2 原子变量
cpp
TAtomic<bool > bIsPausing;
TAtomic<bool > bHasFirstSearchOccurred;
FThreadSafeCounter ActiveSearchCounter;
struct FActiveSearchQuery
{
TAtomic<int32> NextIndex;
TAtomic<int32> SearchCount;
};
13.3 线程安全共享指针
cpp
typedef TSharedPtr<FImaginaryFiBData, ESPMode::ThreadSafe> FImaginaryFiBDataSharedPtr;
typedef TWeakPtr<FImaginaryFiBData, ESPMode::ThreadSafe> FImaginaryFiBDataWeakPtr;
13.4 GC 暂停机制
PreGarbageCollect 回调
│
▼
PauseFindInBlueprintSearch()
│
├── 设置 bIsPausing = true
│
└── 获取 PauseThreadsCriticalSection 锁
│
▼
搜索线程调用 BlockSearchQueryIfPaused()
│
▼
尝试获取锁 → 阻塞等待
│
▼
GC 执行
│
▼
PostGarbageCollect 回调
│
▼
UnpauseFindInBlueprintSearch()
│
├── CleanCache() - 清理已删除条目
│
├── 释放锁
│
└── 设置 bIsPausing = false
│
▼
搜索线程恢复执行
14. 配置选项
14.1 编辑器设置
在 UBlueprintEditorSettings 中:
cpp
UPROPERTY (config)
EFiBIndexAllPermission AllowIndexAllBlueprints;
enum class EFiBIndexAllPermission
{
None,
LoadOnly,
CheckoutAndResave
};
14.2 运行时配置
cpp
bool bEnableGatheringData;
bool bDisableDeferredIndexing;
bool bDisableThreadedIndexing;
bool bEnableCSVStatsProfiling;
bool bEnableDeveloperMenuTools;
bool bDisableSearchResultTemplates;
bool bDisableImmediateAssetDiscovery;
int32 AsyncTaskBatchSize;
14.3 缓存配置
cpp
struct KISMET_API FFindInBlueprintCachingOptions
{
EFiBCacheOpType OpType = EFiBCacheOpType::CachePendingAssets;
EFiBCacheOpFlags OpFlags = EFiBCacheOpFlags::None;
FSimpleDelegate OnFinished;
EFiBVersion MinimiumVersionRequirement = EFiBVersion::FIB_VER_LATEST;
};
15. 扩展与自定义
15.1 自定义可搜索元数据
使用 UPROPERTY 元数据标记属性为可搜索:
cpp
UPROPERTY (meta=(BlueprintSearchable))
FString MySearchableProperty;
UPROPERTY (meta=(BlueprintSearchableShallow))
FMyStruct MyShallowSearchableStruct;
UPROPERTY (meta=(BlueprintSearchableExplicit))
FString MyExplicitSearchableProperty;
UPROPERTY (meta=(BlueprintSearchableHiddenExplicit))
FString MyHiddenExplicitProperty;
15.2 自定义节点搜索标签
在自定义节点中实现:
cpp
void UMyCustomNode::GetSearchTags (TArray<FSearchTagDataPair>& OutTags) const
{
Super::GetSearchTags (OutTags);
OutTags.Add (FSearchTagDataPair (
FFindInBlueprintSearchTags::FiB_Name,
FText::FromString (GetNodeTitle (ENodeTitleType::FullTitle).ToString ())
));
OutTags.Add (FSearchTagDataPair (
FText::FromString ("CustomTag" ),
FText::FromString (MyCustomValue)
));
}
15.3 禁用数据收集的作用域
cpp
{
FDisableGatheringDataOnScope DisableScope;
}
15.4 编程式搜索
cpp
TSharedPtr<SFindInBlueprints> GlobalFindResults = FFindInBlueprintSearchManager::Get ().GetGlobalFindResults ();
if (GlobalFindResults.IsValid ())
{
GlobalFindResults->MakeSearchQuery (
TEXT ("BeginPlay" ),
false ,
FStreamSearchOptions (),
FOnSearchComplete::CreateLambda ([](TArray<FImaginaryFiBDataSharedPtr>& Results) {
for (const auto & Result : Results)
{
}
})
);
}
16. 调试与诊断
16.1 日志类别
cpp
DECLARE_LOG_CATEGORY_EXTERN (LogFindInBlueprint, Warning, All);
UE_LOG (LogFindInBlueprint, Log, TEXT ("Search completed in %0.2f seconds." ), ElapsedTime);
16.2 CSV 性能分析
cpp
CSV_DECLARE_CATEGORY_EXTERN (FindInBlueprint);
CSV_SCOPED_TIMING_STAT (FindInBlueprint, SearchQuery);
CSV_EVENT (FindInBlueprint, TEXT ("FStreamSearch_%d START" ), SearchId);
16.3 调试函数
cpp
void FFindInBlueprintSearchManager::ExportOutdatedAssetList () ;
void FFindInBlueprintSearchManager::DumpCache (FArchive& Ar) ;
static FString FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (UBlueprint* InBlueprint) ;
void FImaginaryFiBData::DumpParsedObject (FArchive& Ar, int32 InTreeLevel = 0 ) const ;
16.4 状态查询
cpp
int32 GetNumberUnindexedAssets () const ;
int32 GetNumberUncachedAssets () const ;
bool IsCacheInProgress () const ;
bool IsUnindexedCacheInProgress () const ;
bool IsAssetDiscoveryInProgress () const ;
bool IsAsyncSearchQueryInProgress () const ;
float GetCacheProgress () const ;
FSoftObjectPath GetCurrentCacheBlueprintPath () const ;
TSet<FSoftObjectPath> GetFailedToCachePathList () const ;
17. 最佳实践
17.1 性能优化
使用过滤器缩小范围
cpp
FStreamSearchOptions Options;
Options.ImaginaryDataFilter = ESearchQueryFilter::NodesFilter;
避免频繁全局搜索
全局搜索会遍历所有蓝图
优先使用本地搜索 (Find Within Blueprint)
保持索引最新
定期运行 "Index All" 更新过时蓝图
在 CI/CD 中集成索引更新
17.2 内存管理
使用弱引用
cpp
TWeakObjectPtr<UBlueprint> Blueprint;
线程安全共享指针
cpp
TSharedPtr<FImaginaryFiBData, ESPMode::ThreadSafe>
及时清理搜索结果
cpp
StreamSearch.Reset ();
ItemsFound.Empty ();
17.3 用户体验
显示搜索进度
处理过时索引
结果导航
18. 常见问题
18.1 搜索结果不完整
原因 : 部分蓝图索引过时或缺失
解决方案 :
点击 "Index All" 重新索引所有蓝图
检查 "Export Asset List" 导出的列表
手动打开并保存过时蓝图
18.2 搜索速度慢
原因 : 项目蓝图数量过多或索引未优化
解决方案 :
使用更精确的搜索语法
使用过滤器缩小范围
确保所有蓝图已索引
18.3 搜索崩溃
原因 : 线程安全问题或内存不足
解决方案 :
检查是否有损坏的蓝图
增加编辑器内存限制
分批索引大型项目
18.4 无法找到特定内容
原因 : 搜索语法错误或内容未被索引
解决方案 :
检查搜索语法
使用标签进行精确搜索
确认目标内容类型可被索引
19. 搜索实例 - FFiBSearchInstance
19.1 概述
FFiBSearchInstance 是 Find In Blueprint 搜索系统的核心搜索执行类 ,负责:
解析用户输入的搜索表达式
遍历虚拟蓝图数据(Imaginary Blueprint Data)
使用表达式求值器匹配搜索条件
收集并组织搜索结果
19.2 类定义
cpp
class FFiBSearchInstance
{
public :
FSearchResult StartSearchQuery (const FString& InSearchString,
FImaginaryFiBDataSharedPtr InImaginaryBlueprint) ;
bool DoSearchQuery (const FString& InSearchString, bool bInComplete = true ) ;
void MakeSearchQuery (const FString& InSearchString,
FImaginaryFiBDataSharedPtr InImaginaryBlueprint) ;
void CreateFilteredResultsListFromTree (ESearchQueryFilter InSearchQueryFilter,
TArray<FImaginaryFiBDataSharedPtr>& OutFilteredResults) ;
FSearchResult GetSearchResults (FImaginaryFiBDataSharedPtr InImaginaryBlueprint) ;
public :
FImaginaryFiBDataWeakPtr CurrentSearchable;
TArray<const FImaginaryFiBData*> MatchesSearchQuery;
TMultiMap<const FImaginaryFiBData*, FComponentUniqueDisplay> MatchingSearchComponents;
TArray<FImaginaryFiBDataWeakPtr> PendingSearchables;
TArray<const FImaginaryFiBData*> LastFunctionResultMatchesSearchQuery;
TMultiMap<const FImaginaryFiBData*, FComponentUniqueDisplay> LastFunctionMatchingSearchComponents;
};
19.3 DoSearchQuery 详解
DoSearchQuery 是 FFiBSearchInstance 的核心搜索执行函数 。
函数签名
cpp
bool FFiBSearchInstance::DoSearchQuery (const FString& InSearchString, bool bInComplete = true ) ;
参数说明
参数
类型
说明
InSearchString
const FString&
用户输入的搜索字符串,支持复杂表达式
bInComplete
bool
TRUE = 完整搜索所有子项;FALSE = 仅搜索当前项
返回值
true - 找到匹配结果
false - 未找到匹配结果
执行流程
DoSearchQuery (InSearchString, bInComplete)
│
▼
┌─────────────────────────────────────────┐
│ 1 . 创建表达式求值器 │
│ FFindInBlueprintExpressionEvaluator │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 2 . 注册函数过滤器回调 │
│ All , Blueprint, Graphs, Functions, │
│ Macros, Properties, Nodes, Pins... │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 3 . 设置搜索文本 │
│ ExpressionEvaluator->SetFilterText () │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 4 . 遍历 PendingSearchables │
│ for each 待搜索项: │
│ ├── ParseAllChildData () 解析子数据 │
│ ├── TestTextFilter () 测试匹配 │
│ │ └── 匹配则加入 MatchesSearchQuery │
│ └── if (bInComplete) │
│ └── 将子项加入 PendingSearchables │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 5 . 返回是否有匹配结果 │
│ return MatchesSearchQuery.Num () > 0 │
└─────────────────────────────────────────┘
完整代码解析
cpp
bool FFiBSearchInstance::DoSearchQuery (const FString& InSearchString, bool bInComplete)
{
TSharedPtr<FFindInBlueprintExpressionEvaluator> ExpressionEvaluator (
new FFindInBlueprintExpressionEvaluator(ETextFilterExpressionEvaluatorMode::Complex, this )) ;
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("All" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterAll));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Blueprint" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterBlueprint));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Graphs" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterGraphs));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("EventGraphs" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterUberGraphs));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Functions" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterFunctions));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Macros" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterMacros));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Properties" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterProperties));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Variables" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterProperties));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Components" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterComponents));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Nodes" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterNodes));
ExpressionEvaluator->AddFunctionTokenCallback (TEXT ("Pins" ),
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterPins));
ExpressionEvaluator->SetDefaultFunctionHandler (
FTokenFunctionHandler::CreateRaw (this , &FFiBSearchInstance::OnFilterDefaultFunction));
ExpressionEvaluator->SetFilterText (FText::FromString (InSearchString));
for (int SearchableIdx = 0 ; SearchableIdx < PendingSearchables.Num (); ++SearchableIdx)
{
CurrentSearchable = PendingSearchables[SearchableIdx];
FImaginaryFiBDataSharedPtr CurrentSearchablePinned = CurrentSearchable.Pin ();
if (!CurrentSearchablePinned.IsValid ())
{
continue ;
}
CurrentSearchablePinned->ParseAllChildData ();
if (ExpressionEvaluator->TestTextFilter (*CurrentSearchablePinned.Get ()))
{
MatchesSearchQuery.AddUnique (CurrentSearchablePinned.Get ());
}
if (bInComplete || CurrentSearchablePinned->IsCategory ())
{
TArray<FImaginaryFiBDataSharedPtr> Children = CurrentSearchablePinned->GetAllParsedChildData ();
for (FImaginaryFiBDataSharedPtr Child : Children)
{
if (!Child->IsTagAndValueCategory ())
{
PendingSearchables.Add (Child);
}
}
}
}
CurrentSearchable.Reset ();
return MatchesSearchQuery.Num () > 0 ;
}
19.4 广度优先搜索机制
DoSearchQuery 使用动态扩展的队列 实现广度优先搜索:
PendingSearchables = [ImaginaryBlueprint]
PendingSearchables = [ImaginaryBlueprint, UberGraphs, Functions, Macros, Properties, Components]
↑ 已处理
PendingSearchables = [..., UberGraphs, EventGraph1, EventGraph2, ...]
↑ 已处理
PendingSearchables = [..., EventGraph1, Node1, Node2, Node3, ...]
↑ 已处理
可视化流程
PendingSearchables = [ImaginaryBlueprint]
bInComplete 或 IsCategory()?
返回 MatchesSearchQuery.Num() > 0
19.5 bInComplete 参数的作用
cpp
if (bInComplete || CurrentSearchablePinned->IsCategory ())
{
for (FImaginaryFiBDataSharedPtr Child : Children)
{
if (!Child->IsTagAndValueCategory ())
{
PendingSearchables.Add (Child);
}
}
}
bInComplete
行为
true
完整搜索:递归搜索所有子项(深度遍历整棵树)
false
浅层搜索:仅搜索当前项,但如果是 Category 仍会展开
19.6 内置过滤器函数
函数名
对应过滤器
搜索范围
示例
All()
AllFilter
所有内容
All(Health)
Blueprint()
BlueprintFilter
蓝图级别信息
Blueprint(Character)
Graphs()
GraphsFilter
所有图表
Graphs(EventGraph)
EventGraphs()
UberGraphsFilter
事件图表
EventGraphs(BeginPlay)
Functions()
FunctionsFilter
函数图表
Functions(CalculateDamage)
Macros()
MacrosFilter
宏图表
Macros(ForEachLoop)
Properties() / Variables()
PropertiesFilter
属性/变量
Properties(Health)
Components()
ComponentsFilter
组件
Components(Mesh)
Nodes()
NodesFilter
节点
Nodes(PrintString)
Pins()
PinsFilter
引脚
Pins(Target)
19.7 支持的搜索语法
语法
示例
说明
简单文本
Health
搜索包含 "Health" 的所有内容
函数过滤
Nodes(Health)
仅在节点中搜索
AND 组合
Health AND Damage
同时包含两个关键词
OR 组合
Health OR Damage
包含任一关键词
NOT 排除
NOT Health
排除包含 Health 的结果
嵌套函数
Functions(Nodes(Cast))
在函数的节点中搜索 Cast
标签匹配
Name=BeginPlay
精确匹配标签值
自定义函数
MyCategory(Value)
搜索名为 MyCategory 的子数据
19.8 与 FTextFilterExpressionEvaluator 的集成
FFindInBlueprintExpressionEvaluator 继承自 UE 的文本过滤表达式求值器,支持:
布尔运算符(AND, OR, NOT)
函数调用语法(FunctionName(params))
比较运算符(=, !=, <, >, <=, >=)
括号分组
cpp
if (ExpressionEvaluator->TestTextFilter (*CurrentSearchablePinned.Get ()))
{
MatchesSearchQuery.AddUnique (CurrentSearchablePinned.Get ());
}
19.8.1 FTextFilterExpressionEvaluator 核心机制
FTextFilterExpressionEvaluator 是 UE 核心模块中的文本过滤表达式求值器 ,位于:
Engine /Source/ Runtime /Core/ Public /Misc/ TextFilterExpressionEvaluator .h
Engine /Source/ Runtime /Core/ Private /Misc/ TextFilterExpressionEvaluator .cpp
核心功能
词法分析 (Lexing) - 将搜索字符串分解为 Token
语法分析 (Parsing) - 构建表达式树
表达式求值 (Evaluation) - 对给定上下文执行过滤
支持的操作符
cpp
const TCHAR* FSubExpressionStart::Monikers[] = { TEXT ("(" ) };
const TCHAR* FSubExpressionEnd::Monikers[] = { TEXT (")" ) };
const TCHAR* FEqual::Monikers[] = { TEXT ("==" ), TEXT ("=" ), TEXT (":" ) };
const TCHAR* FNotEqual::Monikers[] = { TEXT ("!=" ), TEXT ("!:" ) };
const TCHAR* FLess::Monikers[] = { TEXT ("<" ) };
const TCHAR* FLessOrEqual::Monikers[] = { TEXT ("<=" ), TEXT ("<:" ) };
const TCHAR* FGreater::Monikers[] = { TEXT (">" ) };
const TCHAR* FGreaterOrEqual::Monikers[] = { TEXT (">=" ), TEXT (">:" ) };
const TCHAR* FOr::Monikers[] = { TEXT ("OR " ), TEXT ("OR(" ), TEXT ("||" ), TEXT ("|" ) };
const TCHAR* FAnd::Monikers[] = { TEXT ("AND " ), TEXT ("AND(" ), TEXT ("&&" ), TEXT ("&" ) };
const TCHAR* FNot::Monikers[] = { TEXT ("NOT " ), TEXT ("NOT(" ), TEXT ("!" ) };
两种求值模式
cpp
enum class ETextFilterExpressionEvaluatorMode : uint8
{
BasicString,
Complex,
};
特殊文本修饰符
cpp
if (InString[0 ] == '+' )
TextComparisonMode = ETextFilterTextComparisonMode::Exact;
else if (InString.StartsWith (TEXT ("..." )))
TextComparisonMode = ETextFilterTextComparisonMode::EndsWith;
else if (InString.EndsWith (TEXT ("..." )))
TextComparisonMode = ETextFilterTextComparisonMode::StartsWith;
if (InString[0 ] == '-' )
InvertResult = FTextToken::EInvertResult::Yes;
修饰符
含义
示例
+Value
精确匹配
Name=+BeginPlay
Value...
以 Value 开头
Name=Begin...
...Value
以 Value 结尾
Name=...Play
-Value
取反
Name=-BeginPlay
上下文接口 ITextFilterExpressionContext
要使用 FTextFilterExpressionEvaluator,需要实现此接口:
cpp
class ITextFilterExpressionContext
{
public :
virtual bool TestBasicStringExpression (
const FTextFilterString& InValue,
const ETextFilterTextComparisonMode InTextComparisonMode
) const = 0 ;
virtual bool TestComplexExpression (
const FName& InKey,
const FTextFilterString& InValue,
const ETextFilterComparisonOperation InComparisonOperation,
const ETextFilterTextComparisonMode InTextComparisonMode
) const = 0 ;
};
FiB 中 FImaginaryFiBData 实现了此接口,使虚拟数据可以被表达式求值器测试。
函数回调扩展
可以注册自定义函数处理器:
cpp
Evaluator.AddFunctionTokenCallback (TEXT ("Nodes" ),
FTokenFunctionHandler::CreateLambda ([](const FTextFilterString& Param) -> bool {
return true ;
})
);
FiB 注册的函数过滤器:
函数
说明
示例
Nodes(...)
过滤节点
Nodes(Name=BeginPlay)
Pins(...)
过滤引脚
Pins(Name=Target)
Graphs(...)
过滤图表
Graphs(Name=EventGraph)
Properties(...)
过滤属性
Properties(Name=Health)
Components(...)
过滤组件
Components(Name=Mesh)
UberGraphs(...)
过滤事件图
UberGraphs(Name=EventGraph)
Functions(...)
过滤函数
Functions(Name=Calculate)
Macros(...)
过滤宏
Macros(Name=MyMacro)
Variables(...)
过滤变量
Variables(Name=Score)
All(...)
匹配所有类型
All(Name=Target)
表达式求值流程
输入: "Name=BeginPlay && ClassName=K2Node_Event"
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1 . Lexing (词法分析) │
│ → [Name] [=] [BeginPlay] [&&] [ClassName] [=] [K2Node] │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2 . Parsing (语法分析) - 构建表达式树 │
│ && │
│ / \ │
│ = = │
│ / \ / \ │
│ Name BP CN K2 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3 . Evaluation (求值) │
│ 对每个 ITextFilterExpressionContext 调用: │
│ - TestComplexExpression ("Name" , "BeginPlay" , Equal) │
│ - TestComplexExpression ("ClassName" , "K2Node" , Equal) │
│ → 返回 bool 结果 │
└─────────────────────────────────────────────────────────────┘
使用示例
cpp
FTextFilterExpressionEvaluator Evaluator (ETextFilterExpressionEvaluatorMode::Complex) ;
Evaluator.SetFilterText (FText::FromString (TEXT ("Name=BeginPlay && ClassName=K2Node_Event" )));
if (Evaluator.GetFilterType () != ETextFilterExpressionType::Invalid)
{
for (const auto & Item : Items)
{
MyFilterContext Context (Item) ;
if (Evaluator.TestTextFilter (Context))
{
Results.Add (Item);
}
}
}
19.8.2 ExpressionParser 命名空间 - 底层表达式解析引擎
FTextFilterExpressionEvaluator 内部使用 ExpressionParser 命名空间提供的底层解析能力。这是 UE 核心模块中的通用表达式解析器。
位置
Engine /Source/ Runtime /Core/ Public /Misc/ ExpressionParser .h
Engine /Source/ Runtime /Core/ Private /Misc/ ExpressionParser .cpp
核心 API
cpp
namespace ExpressionParser
{
LexResultType Lex (const TCHAR* InExpression, const FTokenDefinitions& TokenDefinitions) ;
CompileResultType Compile (TArray<FExpressionToken> InTokens, const FExpressionGrammar& InGrammar) ;
FExpressionResult Evaluate (const TArray<FCompiledToken>& CompiledTokens,
const IOperatorEvaluationEnvironment& InEnvironment) ;
}
三阶段处理流程
┌─────────────────────────────────────────────────────────────────────────┐
│ ExpressionParser 处理流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 输入: "Name=BeginPlay && ClassName=K2Node" │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 1 : Lex (词法分析) │ │
│ │ FTokenDefinitions 定义如何识别 Token │ │
│ │ │ │
│ │ 输入字符串 → FExpressionTokenConsumer → TArray<FExpressionToken> │ │
│ │ │ │
│ │ 结果: [Name] [=] [BeginPlay] [&&] [ClassName] [=] [K2Node] │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 2 : Compile (编译) │ │
│ │ FExpressionGrammar 定义运算符优先级和结合性 │ │
│ │ │ │
│ │ 使用 Shunting-Yard 算法转换为逆波兰表示法 (RPN) │ │
│ │ │ │
│ │ 结果: [Name] [BeginPlay] [=] [ClassName] [K2Node] [=] [&&] │ │
│ │ (操作数先入栈,遇到操作符时执行) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 3 : Evaluate (求值) │ │
│ │ TOperatorJumpTable 定义操作符的执行逻辑 │ │
│ │ │ │
│ │ 使用栈执行 RPN: │ │
│ │ 1. 遇到操作数 → 压入栈 │ │
│ │ 2. 遇到二元操作符 → 弹出两个操作数,执行,结果压栈 │ │
│ │ 3. 遇到一元操作符 → 弹出一个操作数,执行,结果压栈 │ │
│ │ │ │
│ │ 结果: bool (true /false ) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
核心类型
类型
说明
FTokenStream
字符流读取器,支持 Peek、Parse 等操作
FExpressionTokenConsumer
Token 消费者,收集词法分析产生的 Token
FTokenDefinitions
Token 定义集合,定义如何识别各种 Token
FExpressionGrammar
语法定义,包括运算符优先级、结合性、分组
FExpressionToken
词法 Token,包含上下文和节点数据
FCompiledToken
编译后的 Token,包含类型(操作数/操作符)和短路信息
TOperatorJumpTable<T>
操作符跳转表,映射操作符到执行函数
IOperatorEvaluationEnvironment
求值环境接口
FCompiledToken 类型
cpp
struct FCompiledToken
{
enum EType
{
Benign,
Operand,
BinaryOperator,
PreUnaryOperator,
PostUnaryOperator,
ShortCircuit,
};
EType Type;
FExpressionToken Context;
TOptional<int32> ShortCircuitIndex;
};
Evaluate 核心实现
cpp
FExpressionResult Evaluate (const TArray<FCompiledToken>& CompiledTokens,
const IOperatorEvaluationEnvironment& InEnvironment)
{
TArray<FExpressionToken> RuntimeGeneratedTokens;
TArray<int32> OperandStack;
for (int32 Index = 0 ; Index < CompiledTokens.Num (); ++Index)
{
const auto & Token = CompiledTokens[Index];
switch (Token.Type)
{
case FCompiledToken::Operand:
OperandStack.Push (Index);
break ;
case FCompiledToken::ShortCircuit:
if (OperandStack.Num () >= 1 &&
InEnvironment.ShouldShortCircuit (Token, GetToken (OperandStack.Last ())))
{
Index = Token.ShortCircuitIndex.GetValue ();
}
break ;
case FCompiledToken::BinaryOperator:
if (OperandStack.Num () >= 2 )
{
const auto & R = GetToken (OperandStack.Pop ());
const auto & L = GetToken (OperandStack.Pop ());
auto OpResult = InEnvironment.ExecBinary (Token, L, R);
if (OpResult.IsValid ())
{
OperandStack.Push (AddToken (FExpressionToken (L.Context, MoveTemp (OpResult.GetValue ()))));
}
}
break ;
case FCompiledToken::PreUnaryOperator:
case FCompiledToken::PostUnaryOperator:
if (OperandStack.Num () >= 1 )
{
const auto & Operand = GetToken (OperandStack.Pop ());
FExpressionResult OpResult = (Token.Type == FCompiledToken::PreUnaryOperator) ?
InEnvironment.ExecPreUnary (Token, Operand) :
InEnvironment.ExecPostUnary (Token, Operand);
}
break ;
}
}
if (OperandStack.Num () == 1 )
{
return MakeValue (GetToken (OperandStack[0 ]).Node.Copy ());
}
return MakeError (...);
}
短路求值 (Short-Circuit Evaluation)
ExpressionParser 支持短路求值优化:
cpp
Grammar.DefineBinaryOperator <FAnd>(1 , EAssociativity::RightToLeft, bCanShortCircuit=true );
Grammar.DefineBinaryOperator <FOr>(1 , EAssociativity::RightToLeft, bCanShortCircuit=true );
JumpTable.MapShortCircuit <FOr>([](bool A) { return A; });
JumpTable.MapShortCircuit <FAnd>([](bool A) { return !A; });
示例:
true || (expensive_call()) → 直接返回 true,不执行 expensive_call
false && (expensive_call()) → 直接返回 false,不执行 expensive_call
FTextFilterExpressionEvaluator 如何使用 ExpressionParser
cpp
bool FTextFilterExpressionEvaluator::SetFilterText (const FText& InFilterText)
{
auto LexResult = ExpressionParser::Lex (*FilterText.ToString (), TokenDefinitions);
if (LexResult.IsValid ())
{
CompiledFilter = ExpressionParser::Compile (MoveTemp (LocalExpressionTokens), Grammar);
}
}
bool FTextFilterExpressionEvaluator::TestTextFilter (const ITextFilterExpressionContext& InContext) const
{
TOperatorEvaluationEnvironment<ITextFilterExpressionContext> Env (JumpTable, &InContext) ;
FExpressionResult Result = ExpressionParser::Evaluate (CompiledFilter.GetValue ().GetValue (), Env);
if (const bool * BoolResult = Result.GetValue ().Cast <bool >())
{
return *BoolResult;
}
return false ;
}
与 FiB 的关系图
┌─────────────────────────────────────────────────────────────────────────┐
│ 用户搜索输入 │
│ "Nodes (Name=BeginPlay && Pins(Target))" │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ FFindInBlueprintExpressionEvaluator │
│ (继承自 FTextFilterExpressionEvaluator) │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ 注册 FiB 特定函数过滤器: │ │
│ │ - Nodes (), Pins (), Properties (), Functions () 等 │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ FTextFilterExpressionEvaluator │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ 定义 Token 和语法: │ │
│ │ - 操作符: =, !=, <, >, &&, ||, !, () │ │
│ │ - 文本修饰符: +, -, ... │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ ExpressionParser 命名空间 │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Lex │───▶│ Compile │───▶│ Evaluate │ │
│ │ (词法) │ │ (编译) │ │ (求值) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ ITextFilterExpressionContext │
│ (由 FImaginaryFiBData 实现) │
│ │
│ - TestBasicStringExpression (): 基本字符串匹配 │
│ - TestComplexExpression (): Key=Value 匹配 │
└─────────────────────────────────────────────────────────────────────────┘
19.9 调用链
用户输入搜索 → SFindInBlueprints
│
▼
FStreamSearch::Run ()
│
▼
FFindInBlueprintSearchManager::ContinueSearchQuery ()
│
▼
FFiBSearchInstance::StartSearchQuery ()
│
├── PendingSearchables.Add (ImaginaryBlueprint)
│
└── DoSearchQuery (SearchString)
│
├── 创建 ExpressionEvaluator
├── 注册过滤器函数
├── 遍历 PendingSearchables
│ ├── ParseAllChildData ()
│ ├── TestTextFilter ()
│ └── 添加子项到队列
│
└── return MatchesSearchQuery.Num () > 0
19.10 StartSearchQuery 入口函数
cpp
FSearchResult FFiBSearchInstance::StartSearchQuery (
const FString& InSearchString,
FImaginaryFiBDataSharedPtr InImaginaryBlueprint)
{
MatchesSearchQuery.Empty ();
MatchingSearchComponents.Empty ();
PendingSearchables.Empty ();
PendingSearchables.Add (InImaginaryBlueprint);
if (DoSearchQuery (InSearchString))
{
FSearchResult RootResult = MakeShared <FFindInBlueprintsResult>();
FImaginaryFiBData::CreateSearchTree (
RootResult,
InImaginaryBlueprint,
MatchesSearchQuery,
MatchingSearchComponents
);
return RootResult;
}
return nullptr ;
}
19.11 过滤器函数实现示例
cpp
bool FFiBSearchInstance::OnFilterNodes (
const FTextFilterString& InValue,
ETextFilterTextComparisonMode InTextComparisonMode)
{
TArray<const FImaginaryFiBData*> SavedMatches = MatchesSearchQuery;
TMultiMap<const FImaginaryFiBData*, FComponentUniqueDisplay> SavedComponents = MatchingSearchComponents;
MatchesSearchQuery.Empty ();
MatchingSearchComponents.Empty ();
FImaginaryFiBDataSharedPtr CurrentPinned = CurrentSearchable.Pin ();
if (CurrentPinned.IsValid ())
{
for (const FImaginaryFiBDataSharedPtr& Child : CurrentPinned->GetAllParsedChildData ())
{
if (Child->IsCompatibleWithFilter (ESearchQueryFilter::NodesFilter))
{
PendingSearchables.Add (Child);
}
}
}
bool bResult = DoSearchQuery (InValue.AsString (), false );
LastFunctionResultMatchesSearchQuery = MatchesSearchQuery;
LastFunctionMatchingSearchComponents = MatchingSearchComponents;
MatchesSearchQuery = SavedMatches;
MatchingSearchComponents = SavedComponents;
return bResult;
}
19.12 Mermaid 时序图
FImaginaryFiBData FFindInBlueprintExpressionEvaluator FFiBSearchInstance FFindInBlueprintSearchManager FStreamSearch SFindInBlueprints FImaginaryFiBData FFindInBlueprintExpressionEvaluator FFiBSearchInstance FFindInBlueprintSearchManager FStreamSearch SFindInBlueprints alt [匹配成功] loop [遍历 PendingSearchables] loop [遍历所有蓝图] LaunchStreamThread(SearchString) BeginSearchQuery(this) ContinueSearchQuery() FSearchData StartSearchQuery(SearchString, ImaginaryBlueprint) PendingSearchables.Add(ImaginaryBlueprint) DoSearchQuery(SearchString) new FFindInBlueprintExpressionEvaluator() AddFunctionTokenCallback("Nodes", ...) AddFunctionTokenCallback("Pins", ...) SetFilterText(SearchString) ParseAllChildData() TestTextFilter(Data) TestBasicStringExpression() 匹配结果 true/false MatchesSearchQuery.Add(Data) GetAllParsedChildData() Children PendingSearchables.Add(Children) CreateSearchTree() FSearchResult ItemsFound.Add(Result) EnsureSearchQueryEnds()
附录
A. 相关类汇总
类名
文件
职责
FFindInBlueprintSearchManager
FindInBlueprintManager.h/cpp
单例管理器
FStreamSearch
FindInBlueprintManager.h/cpp
异步搜索线程
FSearchData
FindInBlueprintManager.h
搜索数据条目
FFiBSearchInstance
FiBSearchInstance.h/cpp
搜索实例,核心搜索执行
FFindInBlueprintExpressionEvaluator
FiBSearchInstance.cpp
表达式求值器
SFindInBlueprints
FindInBlueprints.h/cpp
UI 控件
FFindInBlueprintsResult
FindInBlueprints.h/cpp
搜索结果基类
FFindInBlueprintsGraphNode
FindInBlueprints.h/cpp
节点结果
FFindInBlueprintsPin
FindInBlueprints.h/cpp
引脚结果
FFindInBlueprintsProperty
FindInBlueprints.h/cpp
属性结果
FFindInBlueprintsGraph
FindInBlueprints.h/cpp
图表结果
FImaginaryFiBData
ImaginaryBlueprintData.h/cpp
虚拟数据基类
FImaginaryBlueprint
ImaginaryBlueprintData.h/cpp
虚拟蓝图
FImaginaryGraph
ImaginaryBlueprintData.h/cpp
虚拟图表
FImaginaryGraphNode
ImaginaryBlueprintData.h/cpp
虚拟节点
FImaginaryPin
ImaginaryBlueprintData.h/cpp
虚拟引脚
FImaginaryProperty
ImaginaryBlueprintData.h/cpp
虚拟属性
FImaginaryComponent
ImaginaryBlueprintData.h/cpp
虚拟组件
B. 搜索过滤器
cpp
enum ESearchQueryFilter
{
BlueprintFilter = 0 ,
GraphsFilter,
UberGraphsFilter,
FunctionsFilter,
MacrosFilter,
NodesFilter,
PinsFilter,
PropertiesFilter,
VariablesFilter,
ComponentsFilter,
AllFilter,
};
C. 快捷键
快捷键
功能
Ctrl+F
在当前蓝图中查找
Ctrl+Shift+F
在所有蓝图中查找
Ctrl+A
选择所有结果
Ctrl+C
复制选中结果
Enter
跳转到选中结果
Esc
关闭搜索窗口
D. 调试命令与工具
控制台命令
DUMPFIBINDEXCACHE
作用 : 将完整的索引缓存导出到 CSV 文件
输出路径 : {Project}/Intermediate/BlueprintSearchTools/FullBlueprintIndexCacheDump.csv
位置 : FindInBlueprintManager.cpp
蓝图编辑器菜单命令
菜单位置
蓝图编辑器 → File → Developer → Search Tools
蓝图编辑器菜单栏
│
└── File
│
└── Developer (需要启用)
│
└── Search Tools
├── Generate Search Index
└── Dump Cached Index Data
菜单项说明
菜单项
功能
输出路径
Generate Search Index
重新收集并生成人类可读的索引
{Project}/Intermediate/BlueprintSearchTools/{BlueprintName}.index.xml
Dump Cached Index Data
导出当前已缓存的索引数据
{Project}/Intermediate/BlueprintSearchTools/{BlueprintName}.cache.csv
启用 Developer 菜单
重要 : 该菜单默认是隐藏的 ,需要先启用 bEnableDeveloperMenuTools。
方法 1: 修改配置文件
在 {Project}/Config/DefaultEditor.ini 或 {Engine}/Config/BaseEditor.ini 中添加:
ini
[BlueprintSearchSettings]
bEnableDeveloperMenuTools =true
方法 2: 代码中设置
cpp
GConfig->SetBool (TEXT ("BlueprintSearchSettings" ), TEXT ("bEnableDeveloperMenuTools" ), true , GEditorIni);
源码位置
文件
行号
内容
BlueprintEditorCommands.cpp
97
命令定义 UI_COMMAND(GenerateSearchIndex, ...)
BlueprintEditorCommands.h
81
命令声明 TSharedPtr<FUICommandInfo> GenerateSearchIndex
SBlueprintEditorToolbar.cpp
138-143
菜单注册(检查 ShouldEnableDeveloperMenuTools())
BlueprintEditor.cpp
3321-3322
命令映射 MapAction(GenerateSearchIndex, ...)
BlueprintEditor.cpp
3371-3391
实现 FBlueprintEditor::OnGenerateSearchIndexForDebugging()
BlueprintEditor.cpp
3393-3416
实现 FBlueprintEditor::OnDumpCachedIndexDataForBlueprint()
菜单注册代码
cpp
void FKismet2Menu::FillDeveloperMenu (UToolMenu* InMenu)
{
if (FFindInBlueprintSearchManager::Get ().ShouldEnableDeveloperMenuTools ())
{
FToolMenuSection& Section = InMenu->AddSection ("FileDeveloperSearchTools" ,
LOCTEXT ("SearchToolsHeading" , "Search Tools" ));
Section.AddMenuEntry (FBlueprintEditorCommands::Get ().GenerateSearchIndex);
Section.AddMenuEntry (FBlueprintEditorCommands::Get ().DumpCachedIndexData);
}
}
GenerateSearchIndexForDebugging 详解
GenerateSearchIndexForDebugging 是用于生成人类可读搜索索引 的静态函数,主要用于调试和验证索引内容。
函数签名
cpp
static FString FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (UBlueprint* InBlueprint) ;
内部实现
静态函数 (FindInBlueprintManager.cpp:3208-3212)
cpp
FString FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (UBlueprint* InBlueprint)
{
using namespace BlueprintSearchMetaDataHelpers;
return GatherBlueprintSearchMetadata<TFindInBlueprintJsonStringWriter<TPrettyJsonPrintPolicy<TCHAR>>>(
InBlueprint,
EFiBVersion::FIB_VER_LATEST
);
}
菜单命令处理函数 (BlueprintEditor.cpp:3371-3391)
cpp
void FBlueprintEditor::OnGenerateSearchIndexForDebugging ()
{
UBlueprint* Blueprint = GetBlueprintObj ();
if (Blueprint)
{
FString FileLocation = FPaths::ConvertRelativePathToFull (
FPaths::ProjectIntermediateDir () + TEXT ("BlueprintSearchTools" ));
FString FullPath = FString::Printf (TEXT ("%s/%s.index.xml" ),
*FileLocation, *Blueprint->GetName ());
FArchive* DumpFile = IFileManager::Get ().CreateFileWriter (*FullPath);
if (DumpFile)
{
FString JsonOutput = FFindInBlueprintSearchManager::Get ()
.GenerateSearchIndexForDebugging (Blueprint);
DumpFile->Serialize (TCHAR_TO_ANSI (*JsonOutput), JsonOutput.Len ());
DumpFile->Close ();
delete DumpFile;
UE_LOG (LogFindInBlueprint, Log, TEXT ("Wrote search index to %s" ), *FullPath);
}
}
}
Dump Cached Index Data 处理函数 (BlueprintEditor.cpp:3393-3416)
cpp
void FBlueprintEditor::OnDumpCachedIndexDataForBlueprint ()
{
UBlueprint* Blueprint = GetBlueprintObj ();
if (Blueprint)
{
FString FileLocation = FPaths::ConvertRelativePathToFull (
FPaths::ProjectIntermediateDir () + TEXT ("BlueprintSearchTools" ));
FString FullPath = FString::Printf (TEXT ("%s/%s.cache.csv" ),
*FileLocation, *Blueprint->GetName ());
FArchive* DumpFile = IFileManager::Get ().CreateFileWriter (*FullPath);
if (DumpFile)
{
const FSoftObjectPath AssetPath (Blueprint) ;
FSearchData SearchData = FFindInBlueprintSearchManager::Get ()
.GetSearchDataForAssetPath (AssetPath);
if (SearchData.IsValid () && SearchData.ImaginaryBlueprint.IsValid ())
{
SearchData.ImaginaryBlueprint->DumpParsedObject (*DumpFile);
}
DumpFile->Close ();
delete DumpFile;
UE_LOG (LogFindInBlueprint, Log, TEXT ("Wrote cached index data to %s" ), *FullPath);
}
}
}
使用方式
方式 1: 通过蓝图编辑器菜单(推荐)
打开蓝图编辑器
菜单栏 → Developer → Generate Search Index For Debugging
索引文件自动保存到 {Project}/Intermediate/BlueprintSearchTools/{BlueprintName}.index.xml
方式 2: 代码调用
cpp
#include "FindInBlueprintManager.h"
UBlueprint* MyBlueprint = ...;
FString ReadableIndex = FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (MyBlueprint);
UE_LOG (LogTemp, Log, TEXT ("%s" ), *ReadableIndex);
FString FilePath = FPaths::ProjectIntermediateDir () / TEXT ("BlueprintSearchTools" ) / MyBlueprint->GetName () + TEXT (".index.xml" );
FFileHelper::SaveStringToFile (ReadableIndex, *FilePath);
方式 3: 批量导出多个蓝图
cpp
void DumpAllBlueprintIndices ()
{
FString OutputDir = FPaths::ProjectIntermediateDir () / TEXT ("BlueprintSearchTools" );
IFileManager::Get ().MakeDirectory (*OutputDir, true );
for (TObjectIterator<UBlueprint> It; It; ++It)
{
UBlueprint* BP = *It;
if (BP && !BP->IsPendingKill ())
{
FString Index = FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (BP);
FString FilePath = OutputDir / BP->GetName () + TEXT (".index.xml" );
FFileHelper::SaveStringToFile (Index, *FilePath);
}
}
}
与 DumpCachedIndexData 的区别
函数
数据来源
用途
GenerateSearchIndexForDebugging
实时从蓝图重新收集
验证索引收集逻辑是否正确
DumpCachedIndexData
从缓存中读取已有数据
检查缓存中存储的内容
输出示例
json
{
"Name" : "BP_Character" ,
"Path" : "/Game/Blueprints/BP_Character" ,
"ParentClass" : "Character" ,
"Properties" : [
{
"Name" : "Health" ,
"PinCategory" : "real" ,
"SubCategory" : "double" ,
"DefaultValue" : "100.0"
}
] ,
"Uber" : [
{
"Name" : "EventGraph" ,
"SchemaName" : "EdGraphSchema_K2" ,
"Nodes" : [
{
"Name" : "BeginPlay" ,
"ClassName" : "K2Node_Event" ,
"NodeGuid" : "A1B2C3D4..." ,
"Glyph" : "GraphEditor.Event_16x" ,
"GlyphColor" : "(R=1.0,G=0.0,B=0.0,A=1.0)" ,
"Pins" : [ ...]
}
]
}
]
}
搜索数据编码机制
GatherBlueprintSearchMetadata 生成的搜索数据是人类不可读的 ,因为使用了多层编码和压缩优化:
1. 紧凑 JSON 格式 (TCondensedJsonPrintPolicy)
cpp
class FFindInBlueprintJsonWriter : public TFindInBlueprintJsonStringWriter<TCondensedJsonPrintPolicy<TCHAR>>
正常 JSON 使用 TPrettyJsonPrintPolicy(有缩进和换行),而 FiB 使用 TCondensedJsonPrintPolicy(无空格、无换行的紧凑格式)。
2. 字符串用整数索引替代 (Lookup Table)
cpp
virtual void WriteTextValue (const FText& Text) override
{
if (int32* TableLookupValuePtr = ReverseLookupTable.Find (FLookupTableItem (Text)))
{
WriteStringValue (FString::FromInt (*TableLookupValuePtr));
}
else
{
int32 TableLookupValue = LookupTable.Num ();
LookupTable.Add (TableLookupValue, Text);
WriteStringValue (FString::FromInt (TableLookupValue));
}
}
所有字符串(包括 JSON 键名和值)都被替换为整数索引,原始文本存储在 LookupTable 中。
3. 二进制序列化 + 字节转字符串
cpp
namespace FiBSerializationHelpers
{
template <class Type>
const FString Serialize (Type& InValue, bool bInIncludeSize)
{
TArray<uint8> SerializedData;
FMemoryWriter Ar (SerializedData) ;
Ar << InValue;
Ar.Close ();
FString Result = BytesToString (SerializedData.GetData (), SerializedData.Num ());
}
}
LookupTable 被二进制序列化后转换为字符串格式。
4. 最终输出格式
cpp
*JsonOutput = FiBSerializationHelpers::Serialize (DataVersion, false )
+ FiBSerializationHelpers::Serialize (LookupTable, true )
+ MoveTemp (*JsonOutput);
最终格式:[版本号字符串] + [查找表长度+内容字符串] + [紧凑JSON(用整数代替字符串)]
编码对比示例
人类可读格式 (使用 GenerateSearchIndexForDebugging):
json
{
"Properties" : [
{
"Name" : "MyVariable" ,
"PinCategory" : "bool"
}
] ,
"Uber" : [
{
"Name" : "EventGraph" ,
"Nodes" : [
{
"Name" : "BeginPlay" ,
"ClassName" : "K2Node_Event"
}
]
}
]
}
实际存储格式 (使用 FFindInBlueprintJsonWriter):
[版本号] [查找表长度] [序列化查找表] {0 :[{1 :2 ,3 :4 }],5 :[{1 :6 ,7 :[{1 :8 ,9 :10 }]}]}
其中数字 0-10 对应查找表中的:
0 → "Properties"
1 → "Name"
2 → "MyVariable"
3 → "PinCategory"
4 → "bool"
5 → "Uber"
6 → "EventGraph"
7 → "Nodes"
8 → "BeginPlay"
9 → "ClassName"
10 → "K2Node_Event"
为什么这样设计?
优化目标
实现方式
效果
减少存储空间
相同字符串只存储一次(查找表去重)
大幅减少重复标签如 "Name"、"Pins" 的存储
加速搜索
整数比较比字符串比较快
搜索时可直接比较整数索引
Asset Registry 兼容
数据存储在 Asset Registry Tag 中
需要尽量压缩以减少内存占用
本地化支持
FText 序列化保留 Namespace/Key
支持多语言搜索
如何获取可读版本?
使用 GenerateSearchIndexForDebugging 静态函数:
cpp
FString FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (UBlueprint* InBlueprint)
{
using namespace BlueprintSearchMetaDataHelpers;
return GatherBlueprintSearchMetadata<TFindInBlueprintJsonStringWriter<TPrettyJsonPrintPolicy<TCHAR>>>(
InBlueprint,
EFiBVersion::FIB_VER_LATEST
);
}
关键区别:
使用 TPrettyJsonPrintPolicy 而非 TCondensedJsonPrintPolicy
使用 TFindInBlueprintJsonStringWriter 基类而非 FFindInBlueprintJsonWriter
基类不会进行查找表替换,直接输出原始字符串
编码流程图
┌─────────────────────────────────────────────────────────────────────┐
│ GatherBlueprintSearchMetadata │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Blueprint │───▶│ 收集元数据 │───▶│ JSON 结构 │ │
│ │ (源数据) │ │ (遍历节点) │ │ (内存中) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ FFindInBlueprintJsonWriter │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ WriteTextValue () 重写: │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ "Properties" │────▶│ LookupTable │────▶│ 输出 0 │ │ │
│ │ │ (FText) │ │ [0 ]=Prop.. │ │ (整数) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Close () │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ 最终输出 = Serialize (Version) │ │
│ │ + Serialize (LookupTable, withLength=true) │ │
│ │ + CompactJSON │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 存储到 FSearchData.Value → Asset Registry Tag │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
编程式调试函数
cpp
FString ReadableIndex = FFindInBlueprintSearchManager::GenerateSearchIndexForDebugging (MyBlueprint);
UE_LOG (LogTemp, Log, TEXT ("%s" ), *ReadableIndex);
FFindInBlueprintSearchManager::Get ().ExportOutdatedAssetList ();
FArchive& Ar = ...;
FFindInBlueprintSearchManager::Get ().DumpCache (Ar);
FImaginaryFiBData* Data = ...;
Data->DumpParsedObject (Ar, 0 );
UI 按钮
按钮
功能
触发函数
Index All
重新索引所有过时蓝图
CacheAllAssets()
Export Asset List
导出过时资产列表
ExportOutdatedAssetList()
附录 E. Mermaid 图表
以下是使用 Mermaid 格式绘制的系统图表,便于在支持 Mermaid 的环境中渲染。
E.1 系统整体架构
FTextFilterExpressionEvaluator 表达式解析
FFindInBlueprintSearchManager ━━━━━━━━━━━━━━━━━━━━ • SearchMap / SearchArray • 资产生命周期事件 • 异步搜索协调 • 缓存管理
E.2 搜索数据流
Find Within Blueprint (Ctrl+F)
Find In All Blueprints (Ctrl+Shift+F)
SFindInBlueprints MakeSearchQuery()
QuerySingleBlueprint 同步 / 主线程
ContinueSearchQuery() 循环获取 SearchData
FFiBSearchInstance StartSearchQuery()
FImaginaryBlueprint 解析 JSON 并匹配
生成搜索结果树 FFindInBlueprintsResult
E.3 核心类关系图
1
1
1
*
1
*
FFindInBlueprintSearchManager
-TMap<FSoftObjectPath, int32> SearchMap
-TArray<FSearchData> SearchArray
-TMap<FStreamSearch*, FActiveSearchQueryPtr> ActiveSearchQueries
-TSet<FSoftObjectPath> PendingAssets
-TSet<FSoftObjectPath> UnindexedAssets
+Get() : FFindInBlueprintSearchManager
+TWeakObjectPtr<UBlueprint> Blueprint
+FSoftObjectPath AssetPath
+FImaginaryFiBDataSharedPtr ImaginaryBlueprint
+FSearchDataVersionInfo VersionInfo
+ESearchDataStateFlags StateFlags
+HasEncodedValue() : bool
-TMap<int32, FText> LookupTable
#TSharedPtr<FJsonObject> UnparsedJsonObject
#TArray<FImaginaryFiBDataSharedPtr> ParsedChildData
+IsCompatibleWithFilter() : bool
+TestBasicStringExpression() : bool
+CreateSearchResult() : FSearchResult
+TUniquePtr<FRunnableThread> Thread
+TArray<FSearchResult> ItemsFound
E.4 虚拟数据类层次结构
+IsCompatibleWithFilter() : bool
+TestBasicStringExpression() : bool
+TestComplexExpression() : bool
+CreateSearchResult() : FSearchResult
-TMap<int32, FText> LookupTable
+GetBlueprint() : UBlueprint
E.5 搜索结果类层次结构
+TArray<TSharedPtr> Children
+CreateIcon() : TSharedRef<SWidget>
+GetParentBlueprint() : UBlueprint
FFindInBlueprintsGraphNode
FFindInBlueprintsProperty
E.6 异步搜索时序图
FImaginaryBlueprint 搜索线程 (FStreamSearch) FFindInBlueprintSearchManager 主线程 (SFindInBlueprints) FImaginaryBlueprint 搜索线程 (FStreamSearch) FFindInBlueprintSearchManager 主线程 (SFindInBlueprints) loop [遍历所有蓝图] 定时器回调 (每帧) 创建 FStreamSearch LaunchStreamThread() RegisterActiveTimer() Init() Run() BeginSearchQuery(this) ContinueSearchQuery() FSearchData 解析 JSON 并匹配 匹配结果 ItemsFound.Add(Result) GetFilteredItems() 新增结果 TreeView 刷新 EnsureSearchQueryEnds() bThreadCompleted = true 检测 IsComplete() StreamSearch.Reset()
E.7 GC 暂停机制
搜索线程 FFindInBlueprintSearchManager 垃圾回收器 搜索线程 FFindInBlueprintSearchManager 垃圾回收器 搜索线程尝试继续 GC 执行中 PreGarbageCollect 回调 PauseFindInBlueprintSearch() bIsPausing = true 获取 PauseThreadsCriticalSection 锁 BlockSearchQueryIfPaused() 阻塞等待锁 垃圾回收 PostGarbageCollect 回调 UnpauseFindInBlueprintSearch() CleanCache() 释放锁 bIsPausing = false 解除阻塞 恢复搜索
E.8 初始化流程
FFindInBlueprintSearchManager::Get() 首次调用
构造函数 FFindInBlueprintSearchManager()
OnAssetRegistryFilesLoaded
PreGarbageCollect → PauseFindInBlueprintSearch()
PostGarbageCollect → UnpauseFindInBlueprintSearch()
E.9 缓存操作流程
创建 FCacheAllBlueprintsTickableObject
FinishedCachingBlueprints()
E.10 资产生命周期事件
FFindInBlueprintSearchManager
OnAssetRegistryFilesLoaded
AddUnloadedBlueprintSearchMetadata()
AddOrUpdateBlueprintSearchMetadata()
E.11 搜索结果树结构示例
/Game/Blueprints/BP_Character
/Game/Blueprints/BP_Enemy
CalculateDamage (GT_Function)
E.12 版本演进
FIB_VER_BASE (0) 基础搜索功能 节点、引脚、属性搜索 FIB_VER_VARIABLE_REFERENCE (1) 变量引用收集 FMemberReference 支持 FIB_VER_INTERFACE_GRAPHS (2) 实现的接口图收集 接口函数搜索 FIB_VER_FUNC_CALL_SITES (3) 隐藏目标引脚收集 函数来源类收集 改进函数调用点搜索 FiB 版本演进
E.13 线程安全机制
TSharedPtr<FImaginaryFiBData, ESPMode::ThreadSafe>
TWeakPtr<FImaginaryFiBData, ESPMode::ThreadSafe>
SafeQueryModifyCriticalSection 保护 ActiveSearchQueries
PauseThreadsCriticalSection GC 暂停同步
SafeModifyCacheCriticalSection 保护缓存数据
ParseChildDataCriticalSection 虚拟数据解析
bHasFirstSearchOccurred 首次搜索标记
ActiveSearchCounter 活动搜索计数
E.14 UI 组件层次
E.15 搜索语法示例