Browse Source
- Added TestWorldResourceLocator which is for locating game resources - Added TestMovementTasks (Find Resource, Go to target, Get Resource compound) - TestMovementComponent now more closely resembles how the actual component might behave. It's pretty awful, but the idea is there - My initial slice isn't quite working yet; I am checking in now to make sure everything is safely inunreal4.14

10 changed files with 594 additions and 55 deletions
@ -0,0 +1,126 @@ |
|||
#include "GalavantUnreal.h" |
|||
|
|||
#include "TestMovementTasks.hpp" |
|||
|
|||
#include "world/Position.hpp" |
|||
|
|||
void TestFindResourceTask::Initialize(TestWorldResourceLocator* newResourceLocator) |
|||
{ |
|||
ResourceLocator = newResourceLocator; |
|||
} |
|||
|
|||
bool TestFindResourceTask::StateMeetsPreconditions(const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) const |
|||
{ |
|||
return parameters.size() == 1 && parameters[0].Type == Htn::Parameter::ParamType::Int && |
|||
ResourceLocator && |
|||
ResourceLocator->ResourceExistsInWorld((WorldResourceType)parameters[0].IntValue); |
|||
} |
|||
|
|||
void TestFindResourceTask::ApplyStateChange(gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) |
|||
{ |
|||
// TODO: Should this be the case? Should StateMeetsPreconditions find the position? This isn't
|
|||
// that much of a problem if ResourceLocator caches searches
|
|||
float manhattanTo = 0.f; |
|||
gv::Position targetPosition = ResourceLocator->FindNearestResource( |
|||
(WorldResourceType)parameters[0].IntValue, state.SourceAgent.position, manhattanTo); |
|||
if (manhattanTo != -1.f) |
|||
state.SourceAgent.TargetPosition = targetPosition; |
|||
} |
|||
|
|||
bool TestFindResourceTask::Execute(gv::WorldState& state, const Htn::ParameterList& parameters) |
|||
{ |
|||
if (ResourceLocator) |
|||
{ |
|||
float manhattanTo = 0.f; |
|||
gv::Position targetPosition = ResourceLocator->FindNearestResource( |
|||
(WorldResourceType)parameters[0].IntValue, state.SourceAgent.position, manhattanTo); |
|||
if (manhattanTo != -1.f) |
|||
{ |
|||
state.SourceAgent.TargetPosition = targetPosition; |
|||
// TODO: This task finishes instantly; do we need to return Success, Fail, Running and
|
|||
// add a Running() function? That'd make this observer stuff go away
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
void TestMoveToTask::Initialize(TestMovementComponent* newMovementManager) |
|||
{ |
|||
MovementManager = newMovementManager; |
|||
} |
|||
|
|||
bool TestMoveToTask::StateMeetsPreconditions(const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) const |
|||
{ |
|||
// We're good to move to a position as long as we have a target
|
|||
if (state.SourceAgent.TargetPosition) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
|
|||
void TestMoveToTask::ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters) |
|||
{ |
|||
state.SourceAgent.position = state.SourceAgent.TargetPosition; |
|||
} |
|||
|
|||
bool TestMoveToTask::Execute(gv::WorldState& state, const Htn::ParameterList& parameters) |
|||
{ |
|||
if (MovementManager) |
|||
{ |
|||
gv::EntityList entitiesToMove{state.SourceAgent.SourceEntity}; |
|||
std::vector<gv::Position> positions{state.SourceAgent.TargetPosition}; |
|||
MovementManager->PathEntitiesTo(entitiesToMove, positions); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
// TODO: This isn't generic, so PlanComponentManager has no way of executing it :(
|
|||
bool TestMoveToTask::ExecuteAndObserve(gv::WorldState& state, const Htn::ParameterList& parameters, |
|||
gv::Observer<Htn::TaskEvent>* observer) |
|||
{ |
|||
if (Execute(state, parameters)) |
|||
{ |
|||
MovementManager->AddObserver(observer); |
|||
return true; |
|||
} |
|||
else |
|||
return false; |
|||
} |
|||
|
|||
void TestGetResourceTask::Initialize(TestFindResourceTask* newFindResourceTask, |
|||
TestMoveToTask* newMoveToTask) |
|||
{ |
|||
FindResourceTask.Initialize(newFindResourceTask); |
|||
MoveToTask.Initialize(newMoveToTask); |
|||
} |
|||
|
|||
bool TestGetResourceTask::StateMeetsPreconditions(const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) const |
|||
{ |
|||
// TODO: What is the purpose of the compound task checking preconditions if they'll be near
|
|||
// identical to the combined primitive task preconditions?
|
|||
return parameters.size() == 1 && parameters[1].Type == Htn::Parameter::ParamType::Int; |
|||
} |
|||
|
|||
bool TestGetResourceTask::Decompose(Htn::TaskCallList& taskCallList, const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) |
|||
{ |
|||
Htn::ParameterList findResourceParams = parameters; |
|||
Htn::ParameterList moveToParams; |
|||
|
|||
Htn::TaskCall FindResourceTaskCall{&FindResourceTask, findResourceParams}; |
|||
Htn::TaskCall MoveToTaskCall{&MoveToTask, moveToParams}; |
|||
|
|||
// TODO: Make it clear that users should only ever push to this list
|
|||
taskCallList.push_back(FindResourceTaskCall); |
|||
taskCallList.push_back(MoveToTaskCall); |
|||
|
|||
return true; |
|||
} |
@ -0,0 +1,71 @@ |
|||
#pragma once |
|||
|
|||
#include "TestWorldResourceLocator.hpp" |
|||
#include "../GalaEntityComponents/TestMovementComponent.hpp" |
|||
|
|||
#include "ai/htn/HTNTypes.hpp" |
|||
#include "ai/htn/HTNTasks.hpp" |
|||
#include "ai/WorldState.hpp" |
|||
#include "util/SubjectObserver.hpp" |
|||
|
|||
// Parameters:
|
|||
// [0]: Resource type (int)
|
|||
class TestFindResourceTask : public Htn::PrimitiveTask |
|||
{ |
|||
private: |
|||
TestWorldResourceLocator* ResourceLocator; |
|||
|
|||
public: |
|||
TestFindResourceTask() = default; |
|||
virtual ~TestFindResourceTask() = default; |
|||
|
|||
void Initialize(TestWorldResourceLocator* newResourceLocator); |
|||
virtual bool StateMeetsPreconditions(const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) const; |
|||
virtual void ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters); |
|||
virtual bool Execute(gv::WorldState& state, const Htn::ParameterList& parameters); |
|||
|
|||
bool ExecuteAndObserve(gv::WorldState& state, const Htn::ParameterList& parameters, |
|||
gv::Observer<Htn::TaskEvent>* observer); |
|||
}; |
|||
|
|||
// Parameters:
|
|||
// None - Entity to move and target come from world state
|
|||
class TestMoveToTask : public Htn::PrimitiveTask |
|||
{ |
|||
private: |
|||
TestMovementComponent* MovementManager; |
|||
|
|||
public: |
|||
TestMoveToTask() = default; |
|||
virtual ~TestMoveToTask() = default; |
|||
|
|||
void Initialize(TestMovementComponent* newMovementManager); |
|||
virtual bool StateMeetsPreconditions(const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) const; |
|||
virtual void ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters); |
|||
virtual bool Execute(gv::WorldState& state, const Htn::ParameterList& parameters); |
|||
|
|||
bool ExecuteAndObserve(gv::WorldState& state, const Htn::ParameterList& parameters, |
|||
gv::Observer<Htn::TaskEvent>* observer); |
|||
}; |
|||
|
|||
// Parameters:
|
|||
// [0]: Resource type (int)
|
|||
class TestGetResourceTask : public Htn::CompoundTask |
|||
{ |
|||
private: |
|||
// Should this be how it is? Should tasks be singletons?
|
|||
Htn::Task FindResourceTask; |
|||
Htn::Task MoveToTask; |
|||
|
|||
public: |
|||
TestGetResourceTask() = default; |
|||
virtual ~TestGetResourceTask() = default; |
|||
|
|||
void Initialize(TestFindResourceTask* newFindResourceTask, TestMoveToTask* newMoveToTask); |
|||
virtual bool StateMeetsPreconditions(const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters) const; |
|||
virtual bool Decompose(Htn::TaskCallList& taskCallList, const gv::WorldState& state, |
|||
const Htn::ParameterList& parameters); |
|||
}; |
@ -0,0 +1,105 @@ |
|||
#include "GalavantUnreal.h" |
|||
|
|||
#include "TestWorldResourceLocator.hpp" |
|||
#include <limits> |
|||
|
|||
TestWorldResourceLocator::~TestWorldResourceLocator() |
|||
{ |
|||
for (std::pair<const WorldResourceType, ResourceList*>& resourceTypeList : Resources) |
|||
{ |
|||
delete resourceTypeList.second; |
|||
} |
|||
Resources.clear(); |
|||
} |
|||
|
|||
bool TestWorldResourceLocator::ResourceListExists(const WorldResourceType type) const |
|||
{ |
|||
return Resources.find(type) != Resources.end(); |
|||
} |
|||
|
|||
bool TestWorldResourceLocator::ResourceExistsInWorld(const WorldResourceType type) |
|||
{ |
|||
return ResourceListExists(type) && !Resources[type]->empty(); |
|||
} |
|||
|
|||
void TestWorldResourceLocator::AddResource(const WorldResourceType type, |
|||
const gv::Position& location) |
|||
{ |
|||
gv::Position newResource(location); |
|||
// Ensure we're not exactly 0,0,0 because I designed this poorly
|
|||
newResource.Z = !newResource ? 0.1f : newResource.Z; |
|||
|
|||
if (ResourceListExists(type)) |
|||
{ |
|||
Resources[type]->push_back(newResource); |
|||
} |
|||
else |
|||
{ |
|||
ResourceList* newResourceList = new ResourceList; |
|||
newResourceList->push_back(newResource); |
|||
Resources[type] = newResourceList; |
|||
} |
|||
} |
|||
|
|||
void TestWorldResourceLocator::RemoveResource(const WorldResourceType type, |
|||
const gv::Position& location) |
|||
{ |
|||
if (ResourceListExists(type)) |
|||
{ |
|||
ResourceList* resourceList = Resources[type]; |
|||
ResourceList::iterator resourceIt = |
|||
std::find(resourceList->begin(), resourceList->end(), location); |
|||
if (resourceIt != resourceList->end()) |
|||
resourceList->erase(resourceIt); |
|||
} |
|||
} |
|||
|
|||
void TestWorldResourceLocator::MoveResource(const WorldResourceType type, |
|||
const gv::Position& oldLocation, |
|||
const gv::Position& newLocation) |
|||
{ |
|||
if (ResourceListExists(type)) |
|||
{ |
|||
for (gv::Position& currentResource : *Resources[type]) |
|||
{ |
|||
// They should be exactly equal. It's the caller's responsibility to keep track of this
|
|||
if (currentResource.Equals(oldLocation, 0.f)) |
|||
{ |
|||
currentResource = newLocation; |
|||
// Ensure we're not exactly 0,0,0 because I designed this poorly
|
|||
currentResource.Z = !currentResource ? 0.1f : currentResource.Z; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Find the nearest resource. Uses Manhattan distance
|
|||
// Manhattan distance of -1 indicates no resource was found
|
|||
gv::Position TestWorldResourceLocator::FindNearestResource(const WorldResourceType type, |
|||
const gv::Position& location, |
|||
float& manhattanToOut) |
|||
{ |
|||
gv::Position zeroPosition; |
|||
if (ResourceListExists(type)) |
|||
{ |
|||
gv::Position closestResource; |
|||
float closestResourceDistance = std::numeric_limits<float>::max(); |
|||
|
|||
for (gv::Position& currentResource : *Resources[type]) |
|||
{ |
|||
float currentResourceDistance = location.ManhattanTo(currentResource); |
|||
if (currentResourceDistance < closestResourceDistance) |
|||
{ |
|||
closestResourceDistance = currentResourceDistance; |
|||
closestResource = currentResource; |
|||
} |
|||
} |
|||
|
|||
manhattanToOut = closestResourceDistance; |
|||
return closestResource; |
|||
} |
|||
|
|||
manhattanToOut = -1.f; |
|||
return zeroPosition; |
|||
} |
@ -0,0 +1,41 @@ |
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <vector> |
|||
|
|||
#include "world/Position.hpp" |
|||
|
|||
enum WorldResourceType |
|||
{ |
|||
None = 0, |
|||
Agent = 1, |
|||
BusStop = 2 |
|||
}; |
|||
|
|||
// This is for testing only and will not be used in the final game
|
|||
class TestWorldResourceLocator |
|||
{ |
|||
private: |
|||
typedef std::vector<gv::Position> ResourceList; |
|||
typedef std::map<WorldResourceType, ResourceList*> ResourceMap; |
|||
|
|||
ResourceMap Resources; |
|||
|
|||
bool ResourceListExists(const WorldResourceType type) const; |
|||
|
|||
public: |
|||
TestWorldResourceLocator() = default; |
|||
~TestWorldResourceLocator(); |
|||
|
|||
bool ResourceExistsInWorld(const WorldResourceType type); |
|||
|
|||
void AddResource(const WorldResourceType type, const gv::Position& location); |
|||
void RemoveResource(const WorldResourceType type, const gv::Position& location); |
|||
void MoveResource(const WorldResourceType type, const gv::Position& oldLocation, |
|||
const gv::Position& newLocation); |
|||
|
|||
// Find the nearest resource. Uses Manhattan distance
|
|||
// Manhattan distance of -1 indicates no resource was found
|
|||
gv::Position FindNearestResource(const WorldResourceType type, const gv::Position& location, |
|||
float& manhattanToOut); |
|||
}; |
Loading…
Reference in new issue