diff --git a/Content/Characters/Creature1/BT_Creature1.uasset b/Content/Characters/Creature1/BT_Creature1.uasset index bf2a7d4..1809239 100644 Binary files a/Content/Characters/Creature1/BT_Creature1.uasset and b/Content/Characters/Creature1/BT_Creature1.uasset differ diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Skeleton.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Skeleton.cpp index a956dd7..780e4a8 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Skeleton.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Skeleton.cpp @@ -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(); } diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Skeleton.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Skeleton.h index 6f68065..fc2c214 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Skeleton.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Skeleton.h @@ -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(); diff --git a/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_BehaviorTree.cpp b/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_BehaviorTree.cpp new file mode 100644 index 0000000..899692a --- /dev/null +++ b/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_BehaviorTree.cpp @@ -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(Selection); + if (Pawn == nullptr) + { + ImGui::TextDisabled("Not a pawn"); + return; + } + + AAIController* AIController = Cast(Pawn->Controller); + if (AIController == nullptr) + { + ImGui::TextDisabled("No AIController"); + return; + } + + UBehaviorTreeComponent* BehaviorTreeComponent = Cast(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(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(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(); + } +} + diff --git a/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_Blackboard.cpp b/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_Blackboard.cpp index 40301a8..4d960fe 100644 --- a/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_Blackboard.cpp +++ b/Plugins/CogAI/Source/CogAI/Private/CogAIWindow_Blackboard.cpp @@ -50,35 +50,35 @@ void UCogAIWindow_Blackboard::RenderContent() APawn* Pawn = Cast(Selection); if (Pawn == nullptr) { - ImGui::TextDisabled("Selection is not a pawn"); + ImGui::TextDisabled("Not a pawn"); return; } AAIController* AIController = Cast(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; } diff --git a/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_BehaviorTree.h b/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_BehaviorTree.h new file mode 100644 index 0000000..a340255 --- /dev/null +++ b/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_BehaviorTree.h @@ -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; +}; diff --git a/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_Blackboard.h b/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_Blackboard.h index 2888046..a1e108b 100644 --- a/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_Blackboard.h +++ b/Plugins/CogAI/Source/CogAI/Public/CogAIWindow_Blackboard.h @@ -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; - }; diff --git a/Source/CogSample/CogSampleGameState.cpp b/Source/CogSample/CogSampleGameState.cpp index 6cc6ff9..994c2db 100644 --- a/Source/CogSample/CogSampleGameState.cpp +++ b/Source/CogSample/CogSampleGameState.cpp @@ -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(); - - UCogAIWindow_Blackboard* BlackboardWindow = CogWindowManager->CreateWindow("AI.Blackboard"); - //BlackboardWindow->SetAsset(InputAsset); + CogWindowManager->CreateWindow("AI.Behavior Tree"); + CogWindowManager->CreateWindow("AI.Blackboard"); //--------------------------------------- // Input