BehaviorTree

BehaviorTree

#Overview

name: BehaviorTree

The value of this variable can be defined or overridden in .ini config files. 1 .ini config file referencing this setting variable.

It is referenced in 52 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of BehaviorTree is to provide a structure for defining AI behavior in Unreal Engine. It is a core component of the AI system used for creating complex, hierarchical behaviors for AI-controlled characters or entities.

Key points about BehaviorTree:

  1. It is part of the AI Module in Unreal Engine.

  2. The BehaviorTree asset contains a graph (BTGraph) that defines the AI logic.

  3. It is closely associated with the BlackboardData asset, which stores key-value pairs used by the behavior tree.

  4. BehaviorTree is used in conjunction with AIControllers to drive AI behavior.

  5. It can be assigned and run on AI characters through code or Blueprint.

  6. The BehaviorTreeEditor is used to create and edit BehaviorTree assets.

  7. BehaviorTree assets can be compared and diffed using the SBehaviorTreeDiff tool.

  8. It supports debugging through the BehaviorTreeDebugger.

Developers should be aware that:

  1. Changes to the BehaviorTree asset may require updating associated AIControllers and Blackboard assets.

  2. The BehaviorTree asset is tightly coupled with its graph (BTGraph) and should be manipulated through proper editor tools.

  3. When spawning AI with a BehaviorTree, ensure the PawnClass has an appropriate AIController set.

  4. BehaviorTrees can be assigned dynamically at runtime using AIController’s RunBehaviorTree function.

Best practices:

  1. Use the BehaviorTreeEditor for creating and modifying BehaviorTree assets.

  2. Utilize the Blackboard for sharing data between different parts of the behavior tree.

  3. Keep behavior trees modular and reusable where possible.

  4. Make use of the debugging tools provided to troubleshoot complex behaviors.

  5. Consider performance implications when designing large or complex behavior trees.

  6. Use version control to track changes in behavior tree assets over time.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseEngine.ini:3246, section: [GameplayDebuggerSettings]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Plugins/Experimental/CommonConversation/Source/CommonConversationEditor/Private/ConversationDebugger.cpp:1039

Scope (from outer to inner):

file
function     void FConversationDebugger::StopPlaySession

Source code excerpt:

 
		// @TODO: we need a unified flow to leave debugging mode from the different debuggers to prevent strong coupling between modules.
		// Each debugger (Blueprint & BehaviorTree for now) could then take the appropriate actions to resume the session.
		if (FSlateApplication::Get().InKismetDebuggingMode())
		{
			FSlateApplication::Get().LeaveDebuggingMode();
		}
	}
}

#Loc: <Workspace>/Engine/Plugins/Experimental/CommonConversation/Source/CommonConversationEditor/Private/ConversationDebugger.cpp:1060

Scope (from outer to inner):

file
function     void FConversationDebugger::ResumePlaySession

Source code excerpt:

	{
		// @TODO: we need a unified flow to leave debugging mode from the different debuggers to prevent strong coupling between modules.
		// Each debugger (Blueprint & BehaviorTree for now) could then take the appropriate actions to resume the session.
		if (FSlateApplication::Get().InKismetDebuggingMode())
		{
			FSlateApplication::Get().LeaveDebuggingMode();
		}

		GUnrealEd->PlaySessionResumed();

#Loc: <Workspace>/Engine/Plugins/Experimental/GameplayBehaviors/Source/GameplayBehaviorsModule/Private/AI/GameplayBehaviorConfig_BehaviorTree.cpp:16

Scope (from outer to inner):

file
function     UBehaviorTree* UGameplayBehaviorConfig_BehaviorTree::GetBehaviorTree

Source code excerpt:

UBehaviorTree* UGameplayBehaviorConfig_BehaviorTree::GetBehaviorTree() const 
{ 
	return BehaviorTree.IsPending()
		? BehaviorTree.LoadSynchronous()
		: BehaviorTree.Get();
}

#Loc: <Workspace>/Engine/Plugins/Experimental/GameplayBehaviors/Source/GameplayBehaviorsModule/Public/AI/GameplayBehaviorConfig_BehaviorTree.h:21

Scope (from outer to inner):

file
class        class UGameplayBehaviorConfig_BehaviorTree : public UGameplayBehaviorConfig

Source code excerpt:

protected:
	UPROPERTY(EditAnywhere, Category = SmartObject)
	mutable TSoftObjectPtr<UBehaviorTree> BehaviorTree;

	UPROPERTY(EditAnywhere, Category = SmartObject)
	uint32 bRevertToPreviousBTOnFinish : 1;
};

#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2

#Loc: <Workspace>/Engine/Plugins/Experimental/GameplayBehaviors/Source/GameplayBehaviorsModule/Public/AI/GameplayBehavior_BehaviorTree.h:30

Scope (from outer to inner):

file
function     class GAMEPLAYBEHAVIORSMODULE_API UGameplayBehavior_BehaviorTree : public UGameplayBehavior { GENERATED_BODY

Source code excerpt:

	TObjectPtr<AAIController> AIController;

	/** Indicates if BehaviorTree should run only once or in loop. */
	UPROPERTY(EditAnywhere, Category = SmartObject)
	bool bSingleRun = true;
	
	FTimerHandle TimerHandle;
};

#Loc: <Workspace>/Engine/Plugins/Tests/EditorTests/Source/EditorTests/Private/UnrealEd/EditorAssetAutomationTests.cpp:591

Scope (from outer to inner):

file
function     bool FAssetEditorTest::RunTest

Source code excerpt:

		if (!FModuleManager::Get().IsModuleLoaded(TEXT("BehaviorTreeEditor")))
		{
			//NOTE: This module gets left in after the test completes otherwise the content browser would crash when it tries to access the created BehaviorTree.
			FModuleManager::Get().LoadModule(TEXT("BehaviorTreeEditor"));
		}
	}

	//Holds info on each asset we are creating
	TArray< TSharedPtr<CreateAssetHelper::FCreateAssetInfo> > AssetInfos;

#Loc: <Workspace>/Engine/Plugins/Tests/EditorTests/Source/EditorTests/Private/UnrealEd/EditorAssetAutomationTests.cpp:638

Scope: file

Source code excerpt:

	ASSET_TEST_CREATE(USlateBrushAsset, USlateBrushAssetFactory, SB, )
	ASSET_TEST_CREATE(USlateWidgetStyleAsset, USlateWidgetStyleAssetFactory, SWS, FactoryInst->StyleType = UButtonWidgetStyle::StaticClass();)
	ASSET_TEST_CREATE_BY_NAME(AIModule.BehaviorTree, BehaviorTreeEditor.BehaviorTreeFactory, BT, )
	ASSET_TEST_CREATE(UBlueprint, UBlueprintFunctionLibraryFactory, BFL, )
	ASSET_TEST_CREATE(UBlueprint, UBlueprintMacroFactory, MPL, FactoryInst->ParentClass = AActor::StaticClass();)
	ASSET_TEST_CREATE(UCurveBase, UCurveFactory, C, FactoryInst->CurveClass = UCurveFloat::StaticClass();)
	UClass* GameplayAbilityClass = StaticLoadClass(UObject::StaticClass(), NULL, TEXT("GameplayAbilities.GameplayAbilitySet"), NULL, LOAD_None, NULL);
	if (GameplayAbilityClass)
	{

#Loc: <Workspace>/Engine/Source/Developer/FunctionalTesting/Classes/FunctionalAITest.h:91

Scope: file

Source code excerpt:

	/** if set will be applied to spawned AI */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=AISpawn)
	TObjectPtr<class UBehaviorTree> BehaviorTree;

	FAITestSpawnInfo()
		: BehaviorTree(nullptr)
	{}

	FORCEINLINE virtual bool IsValid() const override { return PawnClass != NULL && FAITestSpawnInfoBase::IsValid(); }

	virtual bool Spawn(AFunctionalAITestBase* AITest) const override;
};

#Loc: <Workspace>/Engine/Source/Developer/FunctionalTesting/Private/FunctionalAITest.cpp:371

Scope (from outer to inner):

file
function     bool FAITestSpawnInfo::Spawn

Source code excerpt:

	bool bSuccessfullySpawned = false;

	APawn* SpawnedPawn = UAIBlueprintHelperLibrary::SpawnAIFromClass(AITest->GetWorld(), PawnClass, BehaviorTree
		, AITest->GetRandomizedLocation(SpawnLocation->GetActorLocation())
		, SpawnLocation->GetActorRotation()
		, /*bNoCollisionFail=*/true);

	if (SpawnedPawn == NULL)
	{

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/AssetDefinition_BehaviorTree.cpp:35

Scope (from outer to inner):

file
function     EAssetCommandResult UAssetDefinition_BehaviorTree::OpenAssets

Source code excerpt:

	const EToolkitMode::Type Mode = OpenArgs.GetToolkitMode();

	for (UBehaviorTree* BehaviorTree : OpenArgs.LoadObjects<UBehaviorTree>())
	{
		// check if we have an editor open for this BT's blackboard & use that if we can
		bool bFoundExisting = false;
		if (BehaviorTree->BlackboardAsset != nullptr)
		{
			constexpr bool bFocusIfOpen = false;
			FBehaviorTreeEditor* ExistingInstance = static_cast<FBehaviorTreeEditor*>(GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->FindEditorForAsset(BehaviorTree->BlackboardAsset, bFocusIfOpen));
			if (ExistingInstance != nullptr && ExistingInstance->GetBehaviorTree() == nullptr)
			{
				ExistingInstance->InitBehaviorTreeEditor(Mode, OpenArgs.ToolkitHost, BehaviorTree);
				bFoundExisting = true;
			}
		}

		if (!bFoundExisting)
		{
			FBehaviorTreeEditorModule& BehaviorTreeEditorModule = FModuleManager::GetModuleChecked<FBehaviorTreeEditorModule>("BehaviorTreeEditor");
			BehaviorTreeEditorModule.CreateBehaviorTreeEditor(Mode, OpenArgs.ToolkitHost, BehaviorTree);
		}
	}
	return EAssetCommandResult::Handled;
}

EAssetCommandResult UAssetDefinition_BehaviorTree::PerformAssetDiff(const FAssetDiffArgs& DiffArgs) const

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeDebugger.cpp:1078

Scope (from outer to inner):

file
function     void FBehaviorTreeDebugger::StopPlaySession

Source code excerpt:

 
		// @TODO: we need a unified flow to leave debugging mode from the different debuggers to prevent strong coupling between modules.
		// Each debugger (Blueprint & BehaviorTree for now) could then take the appropriate actions to resume the session.
		if (FSlateApplication::Get().InKismetDebuggingMode())
		{
			FSlateApplication::Get().LeaveDebuggingMode();
		}
	}
}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeDebugger.cpp:1099

Scope (from outer to inner):

file
function     void FBehaviorTreeDebugger::ResumePlaySession

Source code excerpt:

	{
		// @TODO: we need a unified flow to leave debugging mode from the different debuggers to prevent strong coupling between modules.
		// Each debugger (Blueprint & BehaviorTree for now) could then take the appropriate actions to resume the session.
		if (FSlateApplication::Get().InKismetDebuggingMode())
		{
			FSlateApplication::Get().LeaveDebuggingMode();
		}

		GUnrealEd->PlaySessionResumed();

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:139

Scope (from outer to inner):

file
function     FBehaviorTreeEditor::FBehaviorTreeEditor

Source code excerpt:

	bHasMultipleServiceBP = false;

	BehaviorTree = nullptr;
	BlackboardData = nullptr;

	bCheckDirtyOnAssetSave = true;

	GraphClass = UBehaviorTreeGraph::StaticClass();
	GraphName = "Behavior Tree";

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:197

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::NotifyPostChange

Source code excerpt:

		if(PropertyChangedEvent.Property != nullptr && PropertyChangedEvent.Property->GetFName() == TEXT("BlackboardAsset"))
		{
			BlackboardData = BehaviorTree->BlackboardAsset;
		}

		RefreshBlackboardViewsAssociatedObject();
	}
}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:223

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::InitBehaviorTreeEditor

Source code excerpt:

	if(BehaviorTreeToEdit != nullptr)
	{
		BehaviorTree = BehaviorTreeToEdit;
		if(BehaviorTree->BlackboardAsset != nullptr)
		{
			BlackboardData = BehaviorTree->BlackboardAsset;
		}
	}
	else if(BlackboardDataToEdit != nullptr)
	{
		BlackboardData = BlackboardDataToEdit;
	}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:253

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::InitBehaviorTreeEditor

Source code excerpt:


	TArray<UObject*> ObjectsToEdit;
	if(BehaviorTree != nullptr)
	{
		ObjectsToEdit.Add(BehaviorTree);
	}
	if(BlackboardData != nullptr)
	{
		ObjectsToEdit.Add(BlackboardData);
	}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:268

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::InitBehaviorTreeEditor

Source code excerpt:


	// if we are already editing objects, dont try to recreate the editor from scratch but update the list of objects in edition
    // ex: BehaviorTree may want to reuse an editor already opened for its associated Blackboard asset.
	const TArray<UObject*>* EditedObjects = GetObjectsCurrentlyBeingEdited();
	if(EditedObjects == nullptr || EditedObjects->Num() == 0)
	{
		FGraphEditorCommands::Register();
		FBTCommonCommands::Register();
		FBTDebuggerCommands::Register();

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:286

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::InitBehaviorTreeEditor

Source code excerpt:


		Debugger = MakeShareable(new FBehaviorTreeDebugger);
		Debugger->Setup(BehaviorTree, SharedThis(this));
		BindDebuggerToolbarCommands();

		FBehaviorTreeEditorModule& BehaviorTreeEditorModule = FModuleManager::LoadModuleChecked<FBehaviorTreeEditorModule>( "BehaviorTreeEditor" );
		AddMenuExtender(BehaviorTreeEditorModule.GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
		AddToolbarExtender(BehaviorTreeEditorModule.GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:315

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::InitBehaviorTreeEditor

Source code excerpt:

	{
		check(Debugger.IsValid());
		Debugger->Setup(BehaviorTree, SharedThis(this));

		for (UObject* ObjectToEdit : ObjectsToEdit)
		{
			if (!EditedObjects->Contains(ObjectToEdit))
		    {
				AddEditingObject(ObjectToEdit);

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:342

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::RestoreBehaviorTree

Source code excerpt:

{
	// Update BT asset data based on saved graph to have correct data in editor
	UBehaviorTreeGraph* MyGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
	const bool bNewGraph = MyGraph == NULL;
	if (MyGraph == NULL)
	{
		const TSubclassOf<UEdGraphSchema> SchemaClass = GetDefault<UBehaviorTreeGraph>(GraphClass)->Schema;
		check(SchemaClass);
		BehaviorTree->BTGraph = FBlueprintEditorUtils::CreateNewGraph(BehaviorTree, GraphName, GraphClass, SchemaClass);
		MyGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);

		// Initialize the behavior tree graph
		const UEdGraphSchema* Schema = MyGraph->GetSchema();
		Schema->CreateDefaultNodesForGraph(*MyGraph);

		MyGraph->OnCreated();

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:367

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::RestoreBehaviorTree

Source code excerpt:

	TSharedPtr<SDockTab> DocumentTab = DocumentManager->OpenDocument(Payload, bNewGraph ? FDocumentTracker::OpenNewDocument : FDocumentTracker::RestorePreviousDocument);

	if(BehaviorTree->LastEditedDocuments.Num() > 0)
	{
		TSharedRef<SGraphEditor> GraphEditor = StaticCastSharedRef<SGraphEditor>(DocumentTab->GetContent());
		GraphEditor->SetViewLocation(BehaviorTree->LastEditedDocuments[0].SavedViewOffset, BehaviorTree->LastEditedDocuments[0].SavedZoomAmount);
	}

	const bool bIncreaseVersionNum = false;
	if(bNewGraph)
	{
		MyGraph->UpdateAsset(UBehaviorTreeGraph::ClearDebuggerFlags | UBehaviorTreeGraph::KeepRebuildCounter);

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:396

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::SaveEditedObjectState

Source code excerpt:

{
	// Clear currently edited documents
	BehaviorTree->LastEditedDocuments.Empty();

	// Ask all open documents to save their state, which will update LastEditedDocuments
	DocumentManager->SaveAllState();
}

void FBehaviorTreeEditor::HandleBlackboardEntrySelected(const FBlackboardEntry* BlackboardEntry, bool bIsInherited)

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:780

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::OnSelectedNodesChanged

Source code excerpt:

	TArray<UObject*> Selection = BehaviorTreeEditorUtils::GetSelectionForPropertyEditor(NewSelection, SelectionInfo);

	UBehaviorTreeGraph* MyGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
	FAbortDrawHelper Mode0, Mode1;
	bShowDecoratorRangeLower = false;
	bShowDecoratorRangeSelf = false;
	bForceDisablePropertyEdit = SelectionInfo.bInjectedNode;
	bSelectedNodeIsInjected = SelectionInfo.bInjectedNode;
	bSelectedNodeIsRootLevel = SelectionInfo.bRootLevelNode;

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1143

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::OnFinishedChangingProperties

Source code excerpt:

						GetAbortModePreview(Cast<UBTDecorator>(DecoratorNode->NodeInstance), Mode0, Mode1);

						UBehaviorTreeGraph* MyGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
						MyGraph->UpdateAbortHighlight(Mode0, Mode1);
					}
				}			
			}
		}
		else if(PropertyChangedEvent.Property->GetFName() == TEXT("BlackboardAsset"))

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1160

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::OnFinishedChangingProperties

Source code excerpt:

	if (PropertyChangedEvent.Property && PropertyChangedEvent.Property->GetFName() == TEXT("BehaviorAsset"))
	{
		UBehaviorTreeGraph* MyGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
		MyGraph->UpdateInjectedNodes();
		MyGraph->UpdateAsset(UBehaviorTreeGraph::ClearDebuggerFlags);
	}

	const TSharedPtr<SGraphEditor> FocusedGraphEd = UpdateGraphEdPtr.Pin();
	if (FocusedGraphEd.IsValid() && FocusedGraphEd->GetCurrentGraph())

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1174

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::OnPackageSaved

Source code excerpt:

void FBehaviorTreeEditor::OnPackageSaved(const FString& PackageFileName, UPackage* Package, FObjectPostSaveContext ObjectSaveContext)
{
	UBehaviorTreeGraph* MyGraph = BehaviorTree ? Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph) : NULL;
	if (MyGraph)
	{
		const bool bUpdated = MyGraph->UpdateInjectedNodes();
		if (bUpdated)
		{
			MyGraph->UpdateAsset(UBehaviorTreeGraph::ClearDebuggerFlags);

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1406

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::SaveAsset_Execute

Source code excerpt:

void FBehaviorTreeEditor::SaveAsset_Execute()
{
	if (BehaviorTree)
	{
		UBehaviorTreeGraph* BTGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
		if (BTGraph)
		{
			BTGraph->OnSave();
		}
	}
	// save it

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1605

Scope (from outer to inner):

file
function     bool FBehaviorTreeEditor::CanAccessBehaviorTreeMode

Source code excerpt:

bool FBehaviorTreeEditor::CanAccessBehaviorTreeMode() const
{
	return BehaviorTree != nullptr;
}

FText FBehaviorTreeEditor::GetLocalizedMode(FName InMode)
{
	static TMap< FName, FText > LocModes;

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1626

Scope (from outer to inner):

file
function     UEdGraphNode* FBehaviorTreeEditor::FindInjectedNode

Source code excerpt:

UEdGraphNode* FBehaviorTreeEditor::FindInjectedNode(int32 Index) const
{
	UBehaviorTreeGraph* BTGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
	return BTGraph ? BTGraph->FindInjectedNode(Index) : NULL;
}

void FBehaviorTreeEditor::DoubleClickNode(UEdGraphNode* Node)
{
	FocusAttentionOnNode(Node);

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1649

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::FocusWindow

Source code excerpt:

void FBehaviorTreeEditor::FocusWindow(UObject* ObjectToFocusOn)
{
	if(ObjectToFocusOn == BehaviorTree)
	{
		SetCurrentMode(BehaviorTreeMode);
	}
	else if(ObjectToFocusOn == GetBlackboardData())
	{
		SetCurrentMode(BlackboardMode);

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1663

Scope (from outer to inner):

file
function     bool FBehaviorTreeEditor::IncludeAssetInRestoreOpenAssetsPrompt

Source code excerpt:

bool FBehaviorTreeEditor::IncludeAssetInRestoreOpenAssetsPrompt(UObject* Asset) const
{
	// If we're in a BT editor which has a valid BehaviorTree, then don't reopen the BB during restore, only the BT
	return Asset && (!Asset->IsA(UBlackboardData::StaticClass()) || !BehaviorTree);
}

void FBehaviorTreeEditor::OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged)
{
	if (NodeBeingChanged)
	{

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1702

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::DebuggerUpdateGraph

Source code excerpt:

void FBehaviorTreeEditor::DebuggerUpdateGraph()
{
	UBehaviorTreeGraph* BTGraph = BehaviorTree ? Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph) : NULL;
	if (BTGraph)
	{
		BTGraph->RebuildExecutionOrder();
	}
}

FText FBehaviorTreeEditor::GetToolkitName() const
{
	const UObject* EditingObject = GetCurrentMode() == BehaviorTreeMode ? (UObject*)BehaviorTree : (UObject*)GetBlackboardData();
	if(EditingObject != nullptr)
	{
		return FAssetEditorToolkit::GetLabelForObject(EditingObject);
	}

	return FText();

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1722

Scope (from outer to inner):

file
function     FText FBehaviorTreeEditor::GetToolkitToolTipText

Source code excerpt:

FText FBehaviorTreeEditor::GetToolkitToolTipText() const
{
	const UObject* EditingObject = GetCurrentMode() == BehaviorTreeMode ? (UObject*)BehaviorTree : (UObject*)GetBlackboardData();
	if(EditingObject != nullptr)
	{
		return FAssetEditorToolkit::GetToolTipTextForObject(EditingObject);
	}

	return FText();

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1733

Scope (from outer to inner):

file
function     UBehaviorTree* FBehaviorTreeEditor::GetBehaviorTree

Source code excerpt:

UBehaviorTree* FBehaviorTreeEditor::GetBehaviorTree() const 
{
	return BehaviorTree; 
}

UBlackboardData* FBehaviorTreeEditor::GetBlackboardData() const 
{
	return BehaviorTree == nullptr ? BlackboardData : ToRawPtr(BehaviorTree->BlackboardAsset);
}

void FBehaviorTreeEditor::RefreshDebugger()
{
	Debugger->Refresh();
}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1818

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::HandleNewNodeClassPicked

Source code excerpt:

{
	UE_CLOG(InClass == nullptr, LogBehaviorTreeEditor, Error, TEXT("Trying to handle new node of NULL class for Behavior Treee %s ")
		, *GetNameSafe(BehaviorTree));

	if(BehaviorTree != nullptr && InClass != nullptr && BehaviorTree->GetOutermost())
	{
		const FString ClassName = FBlueprintEditorUtils::GetClassNameWithoutSuffix(InClass);

		FString PathName = BehaviorTree->GetOutermost()->GetPathName();
		PathName = FPaths::GetPath(PathName);
		
		// Now that we've generated some reasonable default locations/names for the package, allow the user to have the final say
		// before we create the package and initialize the blueprint inside of it.
		FSaveAssetDialogConfig SaveAssetDialogConfig;
		SaveAssetDialogConfig.DialogTitleOverride = LOCTEXT("SaveAssetDialogTitle", "Save Asset As");

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1911

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::CreateNewBlackboard

Source code excerpt:

void FBehaviorTreeEditor::CreateNewBlackboard()
{
	FString PathName = BehaviorTree->GetOutermost()->GetPathName();
	PathName = FPaths::GetPath(PathName);
	const FString PathNameWithFilename = PathName / LOCTEXT("NewBlackboardName", "NewBlackboardData").ToString();

	FString Name;
	FString PackageName;
	FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1925

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::CreateNewBlackboard

Source code excerpt:

	if (NewAsset != nullptr)
	{
		UBehaviorTreeGraph* BTGraph = Cast<UBehaviorTreeGraph>(BehaviorTree->BTGraph);
		if (BTGraph)
		{
			// Update root node with the newly created asset
			UBehaviorTreeGraphNode_Root* RootNode = nullptr;
			for (const auto& Node : BTGraph->Nodes)
			{

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/BehaviorTreeEditor.cpp:1943

Scope (from outer to inner):

file
function     void FBehaviorTreeEditor::CreateNewBlackboard

Source code excerpt:

		}

		UE_CLOG(BehaviorTree->BlackboardAsset != nullptr, LogBehaviorTreeEditor, Log, TEXT("Blackboard data asset %s has been replaced by %s"), *GetNameSafe(BlackboardData), *GetNameSafe(NewAsset));
		BehaviorTree->BlackboardAsset = NewAsset;
		BlackboardData = NewAsset;

		RefreshBlackboardViewsAssociatedObject();
	}
}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/FindInBT.h:66

Scope: file

Source code excerpt:

};

/** Widget for searching for (BT nodes) across focused BehaviorTree */
class SFindInBT : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SFindInBT){}
	SLATE_END_ARGS()

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.cpp:142

Scope (from outer to inner):

file
function     BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SBehaviorTreeDiff::Construct

Source code excerpt:

	FDiffListCommands::Register();

	PanelOld.BehaviorTree = InArgs._BehaviorTreeOld;
	PanelNew.BehaviorTree = InArgs._BehaviorTreeNew;

	PanelOld.RevisionInfo = InArgs._OldRevision;
	PanelNew.RevisionInfo = InArgs._NewRevision;

	PanelOld.bShowAssetName = InArgs._ShowAssetNames;
	PanelNew.bShowAssetName = InArgs._ShowAssetNames;

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.cpp:223

Scope (from outer to inner):

file
function     BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SBehaviorTreeDiff::Construct

Source code excerpt:

	];

	PanelOld.GeneratePanel(PanelOld.BehaviorTree ? PanelOld.BehaviorTree->BTGraph : nullptr, FoundDiffs);
	PanelNew.GeneratePanel(PanelNew.BehaviorTree ? PanelNew.BehaviorTree->BTGraph : nullptr, FoundDiffs);
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION

FReply SBehaviorTreeDiff::OnOpenInDefaults()
{
	OpenInDefaults.ExecuteIfBound(PanelOld.BehaviorTree, PanelNew.BehaviorTree);
	return FReply::Handled();
}

TSharedRef<SWidget> SBehaviorTreeDiff::GenerateDiffListWidget()
{
	BuildDiffSourceArray();

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.cpp:303

Scope (from outer to inner):

file
function     void SBehaviorTreeDiff::BuildDiffSourceArray

Source code excerpt:

	FoundDiffs->Empty();
	DiffListSource.Empty();
	if (!PanelOld.BehaviorTree || !PanelNew.BehaviorTree)
	{
		return;
	}
	
	FGraphDiffControl::DiffGraphs(PanelOld.BehaviorTree->BTGraph, PanelNew.BehaviorTree->BTGraph, *FoundDiffs);

	for (auto DiffIt(FoundDiffs->CreateConstIterator()); DiffIt; ++DiffIt)
	{
		DiffListSource.Add(FSharedDiffOnGraph(new FTreeDiffResultItem(*DiffIt)));
	}
}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.cpp:456

Scope (from outer to inner):

file
function     SBehaviorTreeDiff::FBehaviorTreeDiffPanel::FBehaviorTreeDiffPanel

Source code excerpt:

SBehaviorTreeDiff::FBehaviorTreeDiffPanel::FBehaviorTreeDiffPanel()
{
	BehaviorTree = NULL;
}

void SBehaviorTreeDiff::FBehaviorTreeDiffPanel::GeneratePanel(UEdGraph* Graph, TSharedPtr<TArray<FDiffSingleResult>> DiffResults)
{
	TSharedPtr<SWidget> Widget = SNew(SBorder)
		.HAlign(HAlign_Center)

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.cpp:544

Scope (from outer to inner):

file
function     FText SBehaviorTreeDiff::FBehaviorTreeDiffPanel::GetTitle

Source code excerpt:

		const FText ChangelistText = FText::AsNumber(RevisionInfo.Changelist, &FNumberFormattingOptions::DefaultNoGrouping());

		if (bShowAssetName && BehaviorTree)
		{
			FString AssetName = BehaviorTree->GetName();
			if(ISourceControlModule::Get().GetProvider().UsesChangelists())
			{
				FText LocalizedFormat = LOCTEXT("NamedRevisionDiffFmtUsesChangelists", "{0} - Revision {1}, CL {2}, {3}");
				Title = FText::Format(LocalizedFormat, FText::FromString(AssetName), RevisionText, ChangelistText, DateText);
			}
			else

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.cpp:572

Scope (from outer to inner):

file
function     FText SBehaviorTreeDiff::FBehaviorTreeDiffPanel::GetTitle

Source code excerpt:

		}
	}
	else if (bShowAssetName && BehaviorTree)
	{
		FString AssetName = BehaviorTree->GetName();
		FText LocalizedFormat = LOCTEXT("NamedCurrentRevisionFmt", "{0} - Current Revision");
		Title = FText::Format(LocalizedFormat, FText::FromString(AssetName));
	}

	return Title;
}

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Private/SBehaviorTreeDiff.h:91

Scope (from outer to inner):

file
class        class SBehaviorTreeDiff: public SCompoundWidget

Source code excerpt:


		// The behavior Tree that owns the graph we are showing
		class UBehaviorTree* BehaviorTree;

		// Revision information for this behavior tree
		FRevisionInfo RevisionInfo;

		// The border around the graph editor, used to change the content when new graphs are set
		TSharedPtr<SBorder> GraphEditorBorder;

#Loc: <Workspace>/Engine/Source/Editor/BehaviorTreeEditor/Public/BehaviorTreeEditor.h:306

Scope (from outer to inner):

file
class        class FBehaviorTreeEditor : public IBehaviorTreeEditor, public FAIGraphEditor, public FNotifyHook

Source code excerpt:


	/* The Behavior Tree being edited */
	UBehaviorTree* BehaviorTree;

	/* The Blackboard Data being edited */
	UBlackboardData* BlackboardData;

	TWeakObjectPtr<class UBehaviorTreeGraphNode_CompositeDecorator> FocusedGraphOwner;

#Loc: <Workspace>/Engine/Source/Runtime/AIModule/Classes/Blueprint/AIBlueprintHelperLibrary.h:35

Scope (from outer to inner):

file
class        class UAIBlueprintHelperLibrary : public UBlueprintFunctionLibrary

Source code excerpt:

	/**	Spawns AI agent of a given class. The PawnClass needs to have AIController 
	 *	set for the function to spawn a controller as well.
	 *	@param BehaviorTree if set, and the function has successfully spawned 
	 *		and AI controller, this BehaviorTree asset will be assigned to the AI 
	 *		controller, and run.
	 *	@param Owner lets you spawn the AI in a sublevel rather than in the 
	 *		persistent level (which is the default behavior).
	 */
	UFUNCTION(BlueprintCallable, Category="AI", meta=(WorldContext="WorldContextObject", UnsafeDuringActorConstruction="true", AdvancedDisplay = "Owner"))
	static AIMODULE_API APawn* SpawnAIFromClass(UObject* WorldContextObject, TSubclassOf<APawn> PawnClass, UBehaviorTree* BehaviorTree, FVector Location, FRotator Rotation = FRotator::ZeroRotator, bool bNoCollisionFail = false, AActor* Owner = nullptr);

	/** The way it works exactly is if the actor passed in is a pawn, then the function retrieves 
	 *	pawn's controller cast to AIController. Otherwise the function returns actor cast to AIController. */
	UFUNCTION(BlueprintPure, Category = "AI", meta = (DefaultToSelf = "ControlledObject"))
	static AIMODULE_API AAIController* GetAIController(AActor* ControlledActor);

	UFUNCTION(BlueprintPure, Category="AI", meta=(DefaultToSelf="Target"))

#Loc: <Workspace>/Engine/Source/Runtime/AIModule/Private/BehaviorTree/BehaviorTree.cpp:2

Scope: file

Source code excerpt:

#include "BehaviorTree/BehaviorTree.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(BehaviorTree)

DEFINE_LOG_CATEGORY(LogBehaviorTree);

UBehaviorTree::UBehaviorTree(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}

#Loc: <Workspace>/Engine/Source/Runtime/AIModule/Private/Blueprint/AIBlueprintHelperLibrary.cpp:184

Scope (from outer to inner):

file
function     APawn* UAIBlueprintHelperLibrary::SpawnAIFromClass

Source code excerpt:

}

APawn* UAIBlueprintHelperLibrary::SpawnAIFromClass(UObject* WorldContextObject, TSubclassOf<APawn> PawnClass, UBehaviorTree* BehaviorTree, FVector Location, FRotator Rotation, bool bNoCollisionFail, AActor *Owner)
{
	APawn* NewPawn = NULL;

	UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
	if (World && *PawnClass)
	{

#Loc: <Workspace>/Engine/Source/Runtime/AIModule/Private/Blueprint/AIBlueprintHelperLibrary.cpp:204

Scope (from outer to inner):

file
function     APawn* UAIBlueprintHelperLibrary::SpawnAIFromClass

Source code excerpt:

			}

			if (BehaviorTree != NULL)
			{
				AAIController* AIController = Cast<AAIController>(NewPawn->Controller);

				if (AIController != NULL)
				{
					AIController->RunBehaviorTree(BehaviorTree);
				}
			}
		}
	}

	return NewPawn;

#Loc: <Workspace>/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectMacros.h:1193

Scope (from outer to inner):

file
namespace    UM

Source code excerpt:

		IgnoreCategoryKeywordsInSubclasses,

		/// [ClassMetadata] For BehaviorTree nodes indicates that the class is deprecated and will display a warning when compiled.
		DeprecatedNode,

		/// [ClassMetadata] [PropertyMetadata] [FunctionMetadata] Used in conjunction with DeprecatedNode, DeprecatedProperty, or DeprecatedFunction to customize the warning message displayed to the user.
		DeprecationMessage,

		/// [ClassMetadata] [PropertyMetadata] [FunctionMetadata] The name to display for this class, property, or function instead of auto-generating it from the name.