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()); ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, FCogWindowWidgets::GetFontWidth());
HoveredBoneIndex = INDEX_NONE; HoveredBoneIndex = INDEX_NONE;
DrawBoneEntry(0, false); RenderBoneEntry(0, false);
ImGui::PopStyleVar(); ImGui::PopStyleVar();
} }
//-------------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------------
void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChildren) void UCogEngineWindow_Skeleton::RenderBoneEntry(int32 BoneIndex, bool OpenAllChildren)
{ {
if (BonesInfos.IsValidIndex(BoneIndex) == false) if (BonesInfos.IsValidIndex(BoneIndex) == false)
{ {
@@ -202,6 +202,7 @@ void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChild
// Checkbox // Checkbox
//------------------------ //------------------------
ImGui::SameLine(); ImGui::SameLine();
FCogWindowWidgets::PushStyleCompact();
if (ImGui::Checkbox("##Visible", &BoneInfo.ShowBone)) if (ImGui::Checkbox("##Visible", &BoneInfo.ShowBone))
{ {
if (IsControlDown) if (IsControlDown)
@@ -217,6 +218,7 @@ void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChild
BoneInfo.ShowTrajectory = false; BoneInfo.ShowTrajectory = false;
} }
} }
FCogWindowWidgets::PopStyleCompact();
const bool HasCustomVisiblity = BoneInfo.ShowName || BoneInfo.ShowAxes || BoneInfo.ShowLocalVelocity || BoneInfo.ShowTrajectory; const bool HasCustomVisiblity = BoneInfo.ShowName || BoneInfo.ShowAxes || BoneInfo.ShowLocalVelocity || BoneInfo.ShowTrajectory;
if (HasCustomVisiblity) if (HasCustomVisiblity)
@@ -239,13 +241,13 @@ void UCogEngineWindow_Skeleton::DrawBoneEntry(int32 BoneIndex, bool OpenAllChild
{ {
for (int32 ChildIndex : BoneInfo.Children) for (int32 ChildIndex : BoneInfo.Children)
{ {
DrawBoneEntry(ChildIndex, OpenAllChildren); RenderBoneEntry(ChildIndex, OpenAllChildren);
} }
} }
if (ShowNode) if (ShowNode)
{ {
if (OpenChildren && ShowNode) if (OpenChildren)
{ {
ImGui::TreePop(); ImGui::TreePop();
} }
@@ -42,7 +42,7 @@ protected:
virtual void OnSelectionChanged(AActor* OldSelection, AActor* NewSelection) override; virtual void OnSelectionChanged(AActor* OldSelection, AActor* NewSelection) override;
private: private:
void DrawBoneEntry(int32 BoneIndex, bool OpenAllChildren); void RenderBoneEntry(int32 BoneIndex, bool OpenAllChildren);
void SetChildrenVisibility(int32 BoneIndex, bool IsVisible); void SetChildrenVisibility(int32 BoneIndex, bool IsVisible);
void DrawSkeleton(); void DrawSkeleton();
void RefreshSkeleton(); 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); APawn* Pawn = Cast<APawn>(Selection);
if (Pawn == nullptr) if (Pawn == nullptr)
{ {
ImGui::TextDisabled("Selection is not a pawn"); ImGui::TextDisabled("Not a pawn");
return; return;
} }
AAIController* AIController = Cast<AAIController>(Pawn->Controller); AAIController* AIController = Cast<AAIController>(Pawn->Controller);
if (AIController == nullptr) if (AIController == nullptr)
{ {
ImGui::TextDisabled("Selection has no AIController"); ImGui::TextDisabled("No AIController");
return; return;
} }
UBrainComponent* Brain = AIController->GetBrainComponent(); UBrainComponent* BehaviorTree = AIController->GetBrainComponent();
if (Brain == nullptr) if (BehaviorTree == nullptr)
{ {
ImGui::TextDisabled("Selection controller has no BrainComponent"); ImGui::TextDisabled("No BrainComponent");
return; return;
} }
UBlackboardComponent* Blackboard = Brain->GetBlackboardComponent(); UBlackboardComponent* Blackboard = BehaviorTree->GetBlackboardComponent();
if (Blackboard == nullptr) if (Blackboard == nullptr)
{ {
ImGui::TextDisabled("Selection controller has no BlackboardComponent"); ImGui::TextDisabled("No BlackboardComponent");
return; return;
} }
UBlackboardData* BlackboardAsset = Blackboard->GetBlackboardAsset(); UBlackboardData* BlackboardAsset = Blackboard->GetBlackboardAsset();
if (BlackboardAsset == nullptr) if (BlackboardAsset == nullptr)
{ {
ImGui::TextDisabled("BlackboardComponent has no BlackboardAsset"); ImGui::TextDisabled("No BlackboardAsset");
return; 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 "CogWindow.h"
#include "CogAIWindow_Blackboard.generated.h" #include "CogAIWindow_Blackboard.generated.h"
namespace FBlackboard { typedef uint8 FKey; }
class UBlackboardData;
UCLASS(Config = Cog) UCLASS(Config = Cog)
class COGAI_API UCogAIWindow_Blackboard : public UCogWindow class COGAI_API UCogAIWindow_Blackboard : public UCogWindow
{ {
@@ -28,5 +25,4 @@ private:
bool bSortByName = true; bool bSortByName = true;
ImGuiTextFilter Filter; ImGuiTextFilter Filter;
}; };
+3 -4
View File
@@ -18,6 +18,7 @@
#include "CogAbilityWindow_Pools.h" #include "CogAbilityWindow_Pools.h"
#include "CogAbilityWindow_Tags.h" #include "CogAbilityWindow_Tags.h"
#include "CogAbilityWindow_Tweaks.h" #include "CogAbilityWindow_Tweaks.h"
#include "CogAIWindow_BehaviorTree.h"
#include "CogAIWindow_Blackboard.h" #include "CogAIWindow_Blackboard.h"
#include "CogDebugDrawImGui.h" #include "CogDebugDrawImGui.h"
#include "CogDebugPlot.h" #include "CogDebugPlot.h"
@@ -206,10 +207,8 @@ void ACogSampleGameState::InitializeCog()
//--------------------------------------- //---------------------------------------
// AI // AI
//--------------------------------------- //---------------------------------------
//const UCogAIDataAsset* InputAsset = GetFirstAssetByClass<UCogAIDataAsset>(); CogWindowManager->CreateWindow<UCogAIWindow_BehaviorTree>("AI.Behavior Tree");
CogWindowManager->CreateWindow<UCogAIWindow_Blackboard>("AI.Blackboard");
UCogAIWindow_Blackboard* BlackboardWindow = CogWindowManager->CreateWindow<UCogAIWindow_Blackboard>("AI.Blackboard");
//BlackboardWindow->SetAsset(InputAsset);
//--------------------------------------- //---------------------------------------
// Input // Input