Basic house ah

This commit is contained in:
Bram verhulst
2025-05-18 12:34:25 +02:00
parent 620dafb591
commit 7d797fa207
11 changed files with 443 additions and 115 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -4,8 +4,8 @@ Size=400,400
Collapsed=0 Collapsed=0
[WORLD INFO [462 FPS]] [WORLD INFO [462 FPS]]
Pos=651,10 Pos=710,10
Size=240,431 Size=240,520
Collapsed=0 Collapsed=0
[STATS] [STATS]
@@ -1708,3 +1708,58 @@ Pos=710,10
Size=240,520 Size=240,520
Collapsed=0 Collapsed=0
[WORLD INFO [820 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [638 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [597 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [562 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [530 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [503 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [443 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [397 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [376 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [358 FPS]]
Pos=710,10
Size=240,520
Collapsed=0
[WORLD INFO [325 FPS]]
Pos=710,10
Size=240,520
Collapsed=0

63
project/Behaviour.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "stdafx.h"
#include "Behaviour.h"
#include "BehaviourTree.h"
#include <IExamInterface.h>
namespace BT_Action
{
BT::State FindAHouse(Blackboard* blackboardPtr) {
IExamInterface* interfacePtr{};
blackboardPtr->GetData("Interface", interfacePtr);
const Elite::Vector2 worldDimensions{ interfacePtr->World_GetInfo().Dimensions };
std::random_device rd; // obtain a random number from hardware
std::mt19937 seed(rd()); // seed the generator
std::uniform_int_distribution<> range(-worldDimensions.x, worldDimensions.x); // define the range
const Elite::Vector2 randomLocation(range(seed), range(seed));
const Elite::Vector2 target = interfacePtr->NavMesh_GetClosestPathPoint(randomLocation);
if (randomLocation != target) {
blackboardPtr->ChangeData("Target", randomLocation);
std::cout << "Data send " << randomLocation << "\n";
return BT::State::Success;
}
return BT::State::Failure;
}
BT::State GoToTarget(Blackboard* blackboardPtr) {
IExamInterface* interfacePtr{};
Elite::Vector2 target{};
SteeringPlugin_Output steering{};
blackboardPtr->GetData("Interface", interfacePtr);
blackboardPtr->GetData("Target", target);
blackboardPtr->GetData("Steering", steering);
std::cout << "target received " << target << "\n";
const auto agentInfo = interfacePtr->Agent_GetInfo();
const auto nextTargetPos = interfacePtr->NavMesh_GetClosestPathPoint(target);
steering.LinearVelocity = nextTargetPos - agentInfo.Position;
steering.LinearVelocity.Normalize();
steering.LinearVelocity *= agentInfo.MaxLinearSpeed;
if (Distance(nextTargetPos, agentInfo.Position) < 2.f) {
steering.LinearVelocity = Elite::ZeroVector2;
std::cout << "target reached at " << target << "\n";
return BT::State::Success;
}
blackboardPtr->ChangeData("Steering", steering);
return BT::State::Running;
}
}

18
project/Behaviour.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
namespace BT
{
enum class State;
}
class Blackboard;
namespace BT_Action
{
BT::State FindAHouse(Blackboard* blackboardPtr);
BT::State GoToTarget(Blackboard* blackboardPtr);
}
namespace BT_Conditions
{
}

View File

@@ -1,7 +1,133 @@
#pragma once #include "stdafx.h"
#include "BehaviourTree.h"
#include "SurvivalAgentPlugin.h"
class BehaviorTree using namespace BT;
{
public:
}; #pragma region COMPOSITES
Composite::Composite(const std::vector<IBehavior*>& childBehaviors) {
m_ChildBehaviors = childBehaviors;
}
Composite::~Composite() {
for (auto pb : m_ChildBehaviors)
SAFE_DELETE(pb)
m_ChildBehaviors.clear();
}
//SELECTOR
State Selector::Execute(Blackboard* blackboardPtr) {
// Loop over all children in m_ChildBehaviors
for (IBehavior* pBeh : m_ChildBehaviors) {
//Every Child: Execute and store the result in m_CurrentState
m_CurrentState = pBeh->Execute(blackboardPtr);
//Check the currentstate and apply the selector Logic:
//if a child returns Success:
if (m_CurrentState == State::Success) {
//stop looping over all children and return Success
return m_CurrentState;
}
//if a child returns Running:
if (m_CurrentState == State::Running) {
//Running: stop looping and return Running
return m_CurrentState;
}
//The selector fails if all children failed.
}
//All children failed
m_CurrentState = State::Failure;
return m_CurrentState;
}
//SEQUENCE
State Sequence::Execute(Blackboard* blackboardPtr) {
//Loop over all children in m_ChildBehaviors
for (IBehavior* pBeh : m_ChildBehaviors) {
//Every Child: Execute and store the result in m_CurrentState
m_CurrentState = pBeh->Execute(blackboardPtr);
//Check the currentstate and apply the sequence Logic:
//if a child returns Failed:
if (m_CurrentState == State::Failure) {
//stop looping over all children and return Failed
return m_CurrentState;
}
//if a child returns Running:
if (m_CurrentState == State::Running) {
//Running: stop looping and return Running
return m_CurrentState;
}
//The selector succeeds if all children succeeded.
}
//All children succeeded
m_CurrentState = State::Success;
return m_CurrentState;
}
State PartialSequence::Execute(Blackboard* blackboardPtr) {
while (m_CurrentBehaviorIndex < m_ChildBehaviors.size()) {
m_CurrentState = m_ChildBehaviors[m_CurrentBehaviorIndex]->Execute(blackboardPtr);
switch (m_CurrentState) {
case State::Failure:
m_CurrentBehaviorIndex = 0;
return m_CurrentState;
case State::Success:
++m_CurrentBehaviorIndex;
m_CurrentState = State::Running;
return m_CurrentState;
case State::Running:
return m_CurrentState;
}
}
m_CurrentBehaviorIndex = 0;
m_CurrentState = State::Success;
return m_CurrentState;
}
#pragma endregion
State Conditional::Execute(Blackboard* blackboardPtr) {
if (m_ConditionalPtr == nullptr)
return State::Failure;
if (m_ConditionalPtr(blackboardPtr)) {
m_CurrentState = State::Success;
}
else {
m_CurrentState = State::Failure;
}
return m_CurrentState;
}
State Action::Execute(Blackboard* blackboardPtr) {
if (m_ActionPtr == nullptr)
return State::Failure;
m_CurrentState = m_ActionPtr(blackboardPtr);
return m_CurrentState;
}
BehaviorTree::~BehaviorTree() {
SAFE_DELETE(m_RootBehaviorPtr)
SAFE_DELETE(m_BlackboardPtr) //Takes ownership of passed blackboard!
}
void BehaviorTree::Update() {
if (m_RootBehaviorPtr == nullptr) {
m_CurrentState = State::Failure;
return;
}
m_CurrentState = m_RootBehaviorPtr->Execute(m_BlackboardPtr);
}
Blackboard* BehaviorTree::GetBlackboard() const {
return m_BlackboardPtr;
}

View File

@@ -1,2 +1,115 @@
#include "stdafx.h" #include <Exam_HelperStructs.h>
#include "BehaviorTree.h"
#include "Blackboard.h"
namespace BT
{
enum class State
{
Failure,
Success,
Running
};
class IBehavior
{
public:
IBehavior() = default;
virtual ~IBehavior() = default;
virtual State Execute(Blackboard* blackboardPtr) = 0;
protected:
State m_CurrentState = State::Failure;
};
#pragma region COMPOSITES
class Composite : public IBehavior
{
public:
explicit Composite(const std::vector<IBehavior*>& childBehaviors);
~Composite() override;
State Execute(Blackboard* blackboardPtr) override = 0;
protected:
std::vector<IBehavior*> m_ChildBehaviors = {};
};
class Selector final : public Composite
{
public:
explicit Selector(std::vector<IBehavior*> childBehaviors) :
Composite(std::move(childBehaviors)) {
}
~Selector() override = default;
State Execute(Blackboard* blackboardPtr) override;
};
class Sequence : public Composite
{
public:
inline explicit Sequence(std::vector<IBehavior*> childBehaviors) :
Composite(std::move(childBehaviors)) {
}
~Sequence() override = default;
State Execute(Blackboard* blackboardPtr) override;
};
class PartialSequence final : public Sequence
{
public:
inline explicit PartialSequence(std::vector<IBehavior*> childBehaviors)
: Sequence(std::move(childBehaviors)) {
}
~PartialSequence() override = default;
State Execute(Blackboard* blackboardPtr) override;
private:
unsigned int m_CurrentBehaviorIndex = 0;
};
#pragma endregion
class Conditional final : public IBehavior
{
public:
explicit Conditional(std::function<bool(Blackboard*)> fp)
: m_ConditionalPtr(std::move(fp)) {
}
State Execute(Blackboard* blackboardPtr) override;
private:
std::function<bool(Blackboard*)> m_ConditionalPtr = nullptr;
};
class Action final : public IBehavior
{
public:
explicit Action(std::function<State(Blackboard*)> fp) : m_ActionPtr(std::move(fp)) {}
State Execute(Blackboard* blackboardPtr) override;
private:
std::function<State(Blackboard*)> m_ActionPtr = nullptr;
};
class BehaviorTree final
{
public:
inline explicit BehaviorTree(Blackboard* blackboardPtr, IBehavior* pRootBehavior)
: m_BlackboardPtr(blackboardPtr),
m_RootBehaviorPtr(pRootBehavior) {
}
~BehaviorTree();
void Update();
Blackboard* GetBlackboard() const;
private:
State m_CurrentState = State::Failure;
Blackboard* m_BlackboardPtr = nullptr;
IBehavior* m_RootBehaviorPtr = nullptr;
};
}

View File

@@ -3,17 +3,15 @@
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
class IBlackboardField
class IBlackBoardField
{ {
public: public:
IBlackBoardField() = default; virtual ~IBlackboardField() = default;
virtual ~IBlackBoardField() = default;
}; };
//BlackboardField does not take ownership of pointers whatsoever! //BlackboardField does not take ownership of pointers whatsoever!
template<typename T> template<typename T>
class BlackboardField : public IBlackBoardField class BlackboardField final : public IBlackboardField
{ {
public: public:
explicit BlackboardField(T data) : m_Data(data) { explicit BlackboardField(T data) : m_Data(data) {
@@ -30,7 +28,7 @@ class Blackboard final
public: public:
Blackboard() = default; Blackboard() = default;
~Blackboard() { ~Blackboard() {
for (auto el : m_BlackboardData) for (const auto& el : m_BlackboardData)
delete el.second; delete el.second;
m_BlackboardData.clear(); m_BlackboardData.clear();
} }
@@ -40,6 +38,7 @@ public:
Blackboard(Blackboard&& other) = delete; Blackboard(Blackboard&& other) = delete;
Blackboard& operator=(Blackboard&& other) = delete; Blackboard& operator=(Blackboard&& other) = delete;
//Add data to the blackboard
template<typename T> bool AddData(const std::string& name, T data) { template<typename T> bool AddData(const std::string& name, T data) {
auto it = m_BlackboardData.find(name); auto it = m_BlackboardData.find(name);
if (it == m_BlackboardData.end()) { if (it == m_BlackboardData.end()) {
@@ -50,6 +49,7 @@ public:
return false; return false;
} }
//Change the data of the blackboard
template<typename T> bool ChangeData(const std::string& name, T data) { template<typename T> bool ChangeData(const std::string& name, T data) {
auto it = m_BlackboardData.find(name); auto it = m_BlackboardData.find(name);
if (it != m_BlackboardData.end()) { if (it != m_BlackboardData.end()) {
@@ -63,6 +63,7 @@ public:
return false; return false;
} }
//Get the data from the blackboard //Get the data from the blackboard
template<typename T> bool GetData(const std::string& name, T& data) { template<typename T> bool GetData(const std::string& name, T& data) {
BlackboardField<T>* p = dynamic_cast<BlackboardField<T>*>(m_BlackboardData[name]); BlackboardField<T>* p = dynamic_cast<BlackboardField<T>*>(m_BlackboardData[name]);
@@ -75,6 +76,5 @@ public:
} }
private: private:
std::unordered_map<std::string, IBlackBoardField*> m_BlackboardData; std::unordered_map<std::string, IBlackboardField*> m_BlackboardData;
}; };

View File

@@ -3,7 +3,7 @@
# ADD NEW .cpp FILES HERE # ADD NEW .cpp FILES HERE
add_library(Exam_Plugin SHARED add_library(Exam_Plugin SHARED
"stdafx.cpp" "stdafx.cpp"
"SurvivalAgentPlugin.cpp" "BehaviourTree.cpp" "BehaviourTree.h" "BlackBoard.h" "Thinker.h" "Thinker.cpp") "SurvivalAgentPlugin.cpp" "BehaviourTree.cpp" "BehaviourTree.h" "BlackBoard.h" "Thinker.h" "Thinker.cpp" "Behaviour.h" "Behaviour.cpp")
target_link_libraries(Exam_Plugin PUBLIC ${EXAM_LIB_DEBUG}) target_link_libraries(Exam_Plugin PUBLIC ${EXAM_LIB_DEBUG})
target_include_directories(Exam_Plugin PUBLIC ${EXAM_INCLUDE_DIR}) target_include_directories(Exam_Plugin PUBLIC ${EXAM_INCLUDE_DIR})

View File

@@ -1,8 +1,9 @@
#include "stdafx.h" #include "stdafx.h"
#include "SurvivalAgentPlugin.h" #include "SurvivalAgentPlugin.h"
#include "IExamInterface.h" #include "IExamInterface.h"
#include "Blackboard.h"
using namespace std; #include "BehaviourTree.h"
#include "Behaviour.h"
//Called only once, during initialization //Called only once, during initialization
void SurvivalAgentPlugin::Initialize(IBaseInterface* pInterface, PluginInfo& info) void SurvivalAgentPlugin::Initialize(IBaseInterface* pInterface, PluginInfo& info)
@@ -16,6 +17,35 @@ void SurvivalAgentPlugin::Initialize(IBaseInterface* pInterface, PluginInfo& inf
info.Student_Name = "Bram Verhulst";//No special characters allowed. Highscores won't work with special characters. info.Student_Name = "Bram Verhulst";//No special characters allowed. Highscores won't work with special characters.
info.Student_Class = "2DAE11"; info.Student_Class = "2DAE11";
info.LB_Password = "ILikeCuteCats!";//Don't use a real password! This is only to prevent other students from overwriting your highscore! info.LB_Password = "ILikeCuteCats!";//Don't use a real password! This is only to prevent other students from overwriting your highscore!
Blackboard* blackboard = CreateBlackboard();
m_BehaviourTree = new BT::BehaviorTree(blackboard,
new BT::Selector({
new BT::PartialSequence({
new BT::Action(BT_Action::FindAHouse),
new BT::Action(BT_Action::GoToTarget)
})
})
);
}
Blackboard* SurvivalAgentPlugin::CreateBlackboard() {
Blackboard* blackboard = new Blackboard();
blackboard->AddData("Interface", m_pInterface);
blackboard->AddData("Steering", SteeringPlugin_Output{});
blackboard->AddData("Target", m_Target);
return blackboard;
}
void SurvivalAgentPlugin::UpdateBlackboard(const SteeringPlugin_Output& steering) {
Blackboard* blackboard{ m_BehaviourTree->GetBlackboard() };
//blackboard->ChangeData("playerPos", m_pInterface->Agent_GetInfo().Position);
blackboard->ChangeData("Steering", steering);
}
SurvivalAgentPlugin::~SurvivalAgentPlugin() {
SAFE_DELETE(m_BehaviourTree);
} }
//Called only once //Called only once
@@ -125,101 +155,15 @@ SteeringPlugin_Output SurvivalAgentPlugin::UpdateSteering(float dt)
//Use the Interface (IAssignmentInterface) to 'interface' with the AI_Framework //Use the Interface (IAssignmentInterface) to 'interface' with the AI_Framework
auto agentInfo = m_pInterface->Agent_GetInfo(); auto agentInfo = m_pInterface->Agent_GetInfo();
UpdateBlackboard(steering);
//Use the navmesh to calculate the next navmesh point m_BehaviourTree->Update();
//auto nextTargetPos = m_pInterface->NavMesh_GetClosestPathPoint(checkpointLocation);
//OR, Use the mouse target m_BehaviourTree->GetBlackboard()->GetData("Steering", steering);
auto nextTargetPos = m_pInterface->NavMesh_GetClosestPathPoint(m_Target);
//FOV USAGE DEMO steering.AngularVelocity = m_AngSpeed;
//=============== steering.AutoOrient = false;
//FOV stats = CHEAP! info about the FOV
FOVStats stats = m_pInterface->FOV_GetStats();
//FOV data (snapshot of the FOV of the current frame) = EXPENSIVE! returns a new vector for every call
auto vHousesInFOV = m_pInterface->GetHousesInFOV();
auto vEnemiesInFOV = m_pInterface->GetEnemiesInFOV();
auto vItemsInFOV = m_pInterface->GetItemsInFOV();
auto vPurgezonesInFOV = m_pInterface->GetPurgeZonesInFOV();
//for (auto& zoneInfo : vPurgezonesInFOV)
//{
// std::cout << "Purge Zone in FOV:" << zoneInfo.Center.x << ", "<< zoneInfo.Center.y << "---Radius: "<< zoneInfo.Radius << std::endl;
//}
//for (auto& enemyInfo : vEnemiesInFOV)
//{
// std::cout << "Enemy in FOV:" << enemyInfo.Location.x << ", " << enemyInfo.Location.y << "---Health: " << enemyInfo.Health << std::endl;
//}
//for (auto& item : vItemsInFOV)
//{
// std::cout << "Item in FOV:" << item.Location.x << ", " << item.Location.y << "---Value: " << item.Value << std::endl;
//}
//INVENTORY USAGE DEMO
//********************
if (m_GrabItem)
{
ItemInfo item;
//Item_Grab > When DebugParams.AutoGrabClosestItem is TRUE, the Item_Grab function returns the closest item in range
//Keep in mind that DebugParams are only used for debugging purposes, by default this flag is FALSE
//Otherwise, use GetEntitiesInFOV() to retrieve a vector of all entities in the FOV (EntityInfo)
//Item_Grab gives you the ItemInfo back, based on the passed EntityHash (retrieved by GetEntitiesInFOV)
if (m_pInterface->GrabNearestItem(item))
{
//Once grabbed, you can add it to a specific inventory slot
//Slot must be empty
m_pInterface->Inventory_AddItem(m_InventorySlot, item);
}
}
if (m_UseItem)
{
//Use an item (make sure there is an item at the given inventory slot)
m_pInterface->Inventory_UseItem(m_InventorySlot);
}
if (m_RemoveItem)
{
//Remove an item from a inventory slot
m_pInterface->Inventory_RemoveItem(m_InventorySlot);
}
if (m_DestroyItemsInFOV)
{
for (auto& item : vItemsInFOV)
{
m_pInterface->DestroyItem(item);
}
}
//Simple Seek Behaviour (towards Target)
steering.LinearVelocity = nextTargetPos - agentInfo.Position; //Desired Velocity
steering.LinearVelocity.Normalize(); //Normalize Desired Velocity
steering.LinearVelocity *= agentInfo.MaxLinearSpeed; //Rescale to Max Speed
if (Distance(nextTargetPos, agentInfo.Position) < 2.f)
{
steering.LinearVelocity = Elite::ZeroVector2;
}
//steering.AngularVelocity = m_AngSpeed; //Rotate your character to inspect the world while walking
steering.AutoOrient = true; //Setting AutoOrient to true overrides the AngularVelocity
steering.RunMode = m_CanRun; //If RunMode is True > MaxLinearSpeed is increased for a limited time (until your stamina runs out)
//SteeringPlugin_Output is works the exact same way a SteeringBehaviour output
//@End (Demo Purposes)
m_GrabItem = false; //Reset State
m_UseItem = false;
m_RemoveItem = false;
m_DestroyItemsInFOV = false;
return steering; return steering;
} }

View File

@@ -2,14 +2,19 @@
#include "IExamPlugin.h" #include "IExamPlugin.h"
#include "Exam_HelperStructs.h" #include "Exam_HelperStructs.h"
namespace BT{
class BehaviorTree;
}
class IBaseInterface; class IBaseInterface;
class IExamInterface; class IExamInterface;
class Blackboard;
class SurvivalAgentPlugin :public IExamPlugin class SurvivalAgentPlugin final :public IExamPlugin
{ {
public: public:
SurvivalAgentPlugin() {}; SurvivalAgentPlugin() = default;
virtual ~SurvivalAgentPlugin() {}; virtual ~SurvivalAgentPlugin() override;
void Initialize(IBaseInterface* pInterface, PluginInfo& info) override; void Initialize(IBaseInterface* pInterface, PluginInfo& info) override;
void DllInit() override; void DllInit() override;
@@ -34,6 +39,10 @@ private:
float m_AngSpeed = 0.f; //Demo purpose float m_AngSpeed = 0.f; //Demo purpose
UINT m_InventorySlot = 0; UINT m_InventorySlot = 0;
Blackboard* CreateBlackboard();
void UpdateBlackboard(const SteeringPlugin_Output& steering);
BT::BehaviorTree* m_BehaviourTree = nullptr;
}; };
//ENTRY //ENTRY
@@ -41,7 +50,7 @@ private:
//The plugin returned by this function is also the plugin used by the host program //The plugin returned by this function is also the plugin used by the host program
extern "C" extern "C"
{ {
__declspec (dllexport) IPluginBase* Register() inline __declspec (dllexport) IPluginBase* Register()
{ {
return new SurvivalAgentPlugin(); return new SurvivalAgentPlugin();
} }