Add behavior tree window

This commit is contained in:
Arnaud Jamin
2023-10-16 17:04:32 -04:00
parent 170869648c
commit 95d24ae767
8 changed files with 275 additions and 21 deletions
Binary file not shown.
@@ -114,13 +114,13 @@ void UCogEngineWindow_Skeleton::RenderContent()
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, FCogWindowWidgets::GetFontWidth());
HoveredBoneIndex = INDEX_NONE;
DrawBoneEntry(0, false);
RenderBoneEntry(0, false);
ImGui::PopStyleVar();
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChildren)
void UCogEngineWindow_Skeleton::RenderBoneEntry(int32 BoneIndex, bool OpenAllChildren)
{
if (BonesInfos.IsValidIndex(BoneIndex) == false)
{
@@ -202,6 +202,7 @@ void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChild
// Checkbox
//------------------------
ImGui::SameLine();
FCogWindowWidgets::PushStyleCompact();
if (ImGui::Checkbox("##Visible", &BoneInfo.ShowBone))
{
if (IsControlDown)
@@ -217,6 +218,7 @@ void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChild
BoneInfo.ShowTrajectory = false;
}
}
FCogWindowWidgets::PopStyleCompact();
const bool HasCustomVisiblity = BoneInfo.ShowName || BoneInfo.ShowAxes || BoneInfo.ShowLocalVelocity || BoneInfo.ShowTrajectory;
if (HasCustomVisiblity)
@@ -239,13 +241,13 @@ void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChild
{
for (int32 ChildIndex : BoneInfo.Children)
{
DrawBoneEntry(ChildIndex, OpenAllChildren);
RenderBoneEntry(ChildIndex, OpenAllChildren);
}
}
if (ShowNode)
{
if (OpenChildren && ShowNode)
if (OpenChildren)
{
ImGui::TreePop();
}
@@ -42,7 +42,7 @@ protected:
virtual void OnSelectionChanged(AActor* OldSelection, AActor* NewSelection) override;
private:
void DrawBoneEntry(int32 BoneIndex, bool OpenAllChildren);
void RenderBoneEntry(int32 BoneIndex, bool OpenAllChildren);
void SetChildrenVisibility(int32 BoneIndex, bool IsVisible);
void DrawSkeleton();
void RefreshSkeleton();
@@ -0,0 +1,227 @@
#include "CogAIWindow_BehaviorTree.h"
#include "AIController.h"
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BTCompositeNode.h"
#include "BehaviorTree/BTNode.h"
#include "BehaviorTree/Tasks/BTTask_Wait.h"
#include "BrainComponent.h"
#include "CogWindowWidgets.h"
#include "GameFramework/Pawn.h"
#include "imgui_internal.h"
//--------------------------------------------------------------------------------------------------------------------------
void UCogAIWindow_BehaviorTree::RenderHelp()
{
ImGui::Text(
"This window displays the behavior tree of the selected actor. "
);
}
//--------------------------------------------------------------------------------------------------------------------------
UCogAIWindow_BehaviorTree::UCogAIWindow_BehaviorTree()
{
bHasMenu = true;
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogAIWindow_BehaviorTree::RenderContent()
{
Super::RenderContent();
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Options"))
{
ImGui::EndMenu();
}
FCogWindowWidgets::MenuSearchBar(Filter);
ImGui::EndMenuBar();
}
AActor* Selection = GetSelection();
if (Selection == nullptr)
{
ImGui::TextDisabled("No Selection");
return;
}
APawn* Pawn = Cast<APawn>(Selection);
if (Pawn == nullptr)
{
ImGui::TextDisabled("Not a pawn");
return;
}
AAIController* AIController = Cast<AAIController>(Pawn->Controller);
if (AIController == nullptr)
{
ImGui::TextDisabled("No AIController");
return;
}
UBehaviorTreeComponent* BehaviorTreeComponent = Cast<UBehaviorTreeComponent>(AIController->GetBrainComponent());
if (BehaviorTreeComponent == nullptr)
{
ImGui::TextDisabled("No BrainComponent");
return;
}
UBehaviorTree* CurrentTree = BehaviorTreeComponent->GetCurrentTree();
if (CurrentTree == nullptr)
{
ImGui::TextDisabled("No Current Tree");
return;
}
//----------------------------------------------------------------------------------------
// If we use the current tree root node it doesn't seem to be the one instanced.
// Not sure if there is a better way to access it, but we find the root node from
// the current active node. Then we will be able to check if the drawn nodes is
// parent of the active node to display it active.
//----------------------------------------------------------------------------------------
const UBTNode* RootNodeInstanced = nullptr;
for (const UBTNode* ParentNode = BehaviorTreeComponent->GetActiveNode(); ParentNode != nullptr; ParentNode = ParentNode->GetParentNode())
{
RootNodeInstanced = ParentNode;
}
if (RootNodeInstanced != nullptr)
{
RenderNode(BehaviorTreeComponent, RootNodeInstanced, false);
}
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogAIWindow_BehaviorTree::RenderNode(UBehaviorTreeComponent* BehaviorTreeComponent, const UBTNode* Node, bool OpenAllChildren)
{
const char* NodeName = TCHAR_TO_ANSI(*Node->GetNodeName());
const bool ShowNode = Filter.PassFilter(NodeName);
const UBTCompositeNode* CompositeNode = Cast<UBTCompositeNode>(Node);
bool IsActive = false;
for (const UBTNode* ActiveParentNode = BehaviorTreeComponent->GetActiveNode(); ActiveParentNode != nullptr; ActiveParentNode = ActiveParentNode->GetParentNode())
{
if (Node == ActiveParentNode)
{
IsActive = true;
break;
}
}
bool OpenChildren = false;
if (ShowNode)
{
ImGui::PushID(Node);
if (OpenAllChildren)
{
ImGui::SetNextItemOpen(true, ImGuiCond_Always);
}
else
{
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
}
//------------------------
// TreeNode
//------------------------
const bool HasChildren = CompositeNode != nullptr && CompositeNode->GetChildrenNum() > 0;
if (HasChildren && Filter.IsActive() == false)
{
OpenChildren = ImGui::TreeNodeEx("##Node", ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_SpanFullWidth);
}
else
{
ImGui::TreeNodeEx("##Node", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_SpanFullWidth);
}
const bool IsControlDown = ImGui::GetCurrentContext()->IO.KeyCtrl;
if (ImGui::IsItemClicked(ImGuiMouseButton_Left) && IsControlDown)
{
OpenAllChildren = true;
}
//------------------------
// ContextMenu
//------------------------
if (ImGui::BeginPopupContextItem())
{
ImGui::EndPopup();
}
//------------------------
// Tooltip
//------------------------
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::BeginDisabled();
ImGui::Text(TCHAR_TO_ANSI(*Node->GetStaticDescription()));
if (const UBTTask_Wait* Wait = Cast<UBTTask_Wait>(Node))
{
ImGui::Text("Wait: %0.2fs", Wait->WaitTime);
}
ImGui::EndDisabled();
ImGui::EndTooltip();
}
//------------------------
// Checkbox
//------------------------
ImGui::SameLine();
FCogWindowWidgets::PushStyleCompact();
if (IsActive == false)
{
ImGui::BeginDisabled();
}
bool DrawActive = IsActive;
ImGui::Checkbox("##Active", &DrawActive);
if (IsActive == false)
{
ImGui::EndDisabled();
}
FCogWindowWidgets::PopStyleCompact();
//------------------------
// Name
//------------------------
ImGui::SameLine();
ImVec4 NameColor = IsActive ? ImVec4(0.0f, 1.0f, 0.0f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
ImGui::TextColored(NameColor, "%s", NodeName);
}
//------------------------
// Children
//------------------------
if (OpenChildren || Filter.IsActive())
{
if (CompositeNode != nullptr)
{
for (int32 i = 0; i < CompositeNode->GetChildrenNum(); ++i)
{
const UBTNode* ChildNode = CompositeNode->GetChildNode(i);
RenderNode(BehaviorTreeComponent, ChildNode, OpenAllChildren);
}
}
}
if (ShowNode)
{
if (OpenChildren)
{
ImGui::TreePop();
}
ImGui::PopID();
}
}
@@ -50,35 +50,35 @@ void UCogAIWindow_Blackboard::RenderContent()
APawn* Pawn = Cast<APawn>(Selection);
if (Pawn == nullptr)
{
ImGui::TextDisabled("Selection is not a pawn");
ImGui::TextDisabled("Not a pawn");
return;
}
AAIController* AIController = Cast<AAIController>(Pawn->Controller);
if (AIController == nullptr)
{
ImGui::TextDisabled("Selection has no AIController");
ImGui::TextDisabled("No AIController");
return;
}
UBrainComponent* Brain = AIController->GetBrainComponent();
if (Brain == nullptr)
UBrainComponent* BehaviorTree = AIController->GetBrainComponent();
if (BehaviorTree == nullptr)
{
ImGui::TextDisabled("Selection controller has no BrainComponent");
ImGui::TextDisabled("No BrainComponent");
return;
}
UBlackboardComponent* Blackboard = Brain->GetBlackboardComponent();
UBlackboardComponent* Blackboard = BehaviorTree->GetBlackboardComponent();
if (Blackboard == nullptr)
{
ImGui::TextDisabled("Selection controller has no BlackboardComponent");
ImGui::TextDisabled("No BlackboardComponent");
return;
}
UBlackboardData* BlackboardAsset = Blackboard->GetBlackboardAsset();
if (BlackboardAsset == nullptr)
{
ImGui::TextDisabled("BlackboardComponent has no BlackboardAsset");
ImGui::TextDisabled("No BlackboardAsset");
return;
}
@@ -0,0 +1,30 @@
#pragma once
#include "CoreMinimal.h"
#include "CogWindow.h"
#include "CogAIWindow_BehaviorTree.generated.h"
class UBehaviorTreeComponent;
class UBTNode;
UCLASS(Config = Cog)
class COGAI_API UCogAIWindow_BehaviorTree : public UCogWindow
{
GENERATED_BODY()
public:
UCogAIWindow_BehaviorTree();
protected:
void RenderHelp();
virtual void RenderContent() override;
virtual void RenderNode(UBehaviorTreeComponent* BehaviorTreeComponent, const UBTNode* Node, bool OpenAllChildren);
private:
ImGuiTextFilter Filter;
};
@@ -4,9 +4,6 @@
#include "CogWindow.h"
#include "CogAIWindow_Blackboard.generated.h"
namespace FBlackboard { typedef uint8 FKey; }
class UBlackboardData;
UCLASS(Config = Cog)
class COGAI_API UCogAIWindow_Blackboard : public UCogWindow
{
@@ -28,5 +25,4 @@ private:
bool bSortByName = true;
ImGuiTextFilter Filter;
};
+3 -4
View File
@@ -18,6 +18,7 @@
#include "CogAbilityWindow_Pools.h"
#include "CogAbilityWindow_Tags.h"
#include "CogAbilityWindow_Tweaks.h"
#include "CogAIWindow_BehaviorTree.h"
#include "CogAIWindow_Blackboard.h"
#include "CogDebugDrawImGui.h"
#include "CogDebugPlot.h"
@@ -206,10 +207,8 @@ void ACogSampleGameState::InitializeCog()
//---------------------------------------
// AI
//---------------------------------------
//const UCogAIDataAsset* InputAsset = GetFirstAssetByClass<UCogAIDataAsset>();
UCogAIWindow_Blackboard* BlackboardWindow = CogWindowManager->CreateWindow<UCogAIWindow_Blackboard>("AI.Blackboard");
//BlackboardWindow->SetAsset(InputAsset);
CogWindowManager->CreateWindow<UCogAIWindow_BehaviorTree>("AI.Behavior Tree");
CogWindowManager->CreateWindow<UCogAIWindow_Blackboard>("AI.Blackboard");
//---------------------------------------
// Input