mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-06 02:49:41 +00:00
Merge pull request #2670 from elsid/fix_aiwander_stuck
Fix AiWander stuck (bug #5237)
This commit is contained in:
commit
e1e49832c7
10 changed files with 215 additions and 71 deletions
|
@ -197,7 +197,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
|
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
|
||||||
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
||||||
|
|
||||||
mObstacleCheck.update(actor, duration);
|
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
|
||||||
|
mObstacleCheck.update(actor, destination, duration);
|
||||||
|
|
||||||
// handle obstacles on the way
|
// handle obstacles on the way
|
||||||
evadeObstacles(actor);
|
evadeObstacles(actor);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/esm/aisequence.hpp>
|
#include <components/esm/aisequence.hpp>
|
||||||
|
#include <components/detournavigator/navigator.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -52,6 +53,14 @@ namespace MWMechanics
|
||||||
return 1;
|
return 1;
|
||||||
return COUNT_BEFORE_RESET;
|
return COUNT_BEFORE_RESET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec3f getRandomPointAround(const osg::Vec3f& position, const float distance)
|
||||||
|
{
|
||||||
|
const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI;
|
||||||
|
osg::Matrixf rotation;
|
||||||
|
rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));
|
||||||
|
return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||||
|
@ -310,14 +319,24 @@ namespace MWMechanics
|
||||||
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
||||||
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
||||||
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
|
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
|
||||||
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
|
const auto halfExtents = world->getPathfindingHalfExtents(actor);
|
||||||
|
const auto navigator = world->getNavigator();
|
||||||
|
const auto navigatorFlags = getNavigatorFlags(actor);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Determine a random location within radius of original position
|
// Determine a random location within radius of original position
|
||||||
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
||||||
const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI;
|
if (!isWaterCreature && !isFlyingCreature)
|
||||||
const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection);
|
{
|
||||||
const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection);
|
// findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance
|
||||||
const float destinationZ = mInitialActorPosition.z();
|
if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, currentPosition, wanderDistance, navigatorFlags))
|
||||||
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
mDestination = *destination;
|
||||||
|
else
|
||||||
|
mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);
|
||||||
|
|
||||||
// Check if land creature will walk onto water or if water creature will swim onto land
|
// Check if land creature will walk onto water or if water creature will swim onto land
|
||||||
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
|
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
|
||||||
|
@ -327,15 +346,9 @@ namespace MWMechanics
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (isWaterCreature || isFlyingCreature)
|
if (isWaterCreature || isFlyingCreature)
|
||||||
{
|
|
||||||
mPathFinder.buildStraightPath(mDestination);
|
mPathFinder.buildStraightPath(mDestination);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags);
|
||||||
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
|
|
||||||
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents,
|
|
||||||
getNavigatorFlags(actor));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,89 +77,94 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
ObstacleCheck::ObstacleCheck()
|
ObstacleCheck::ObstacleCheck()
|
||||||
: mWalkState(State_Norm)
|
: mWalkState(WalkState::Initial)
|
||||||
, mStuckDuration(0)
|
, mStateDuration(0)
|
||||||
, mEvadeDuration(0)
|
|
||||||
, mDistSameSpot(-1) // avoid calculating it each time
|
|
||||||
, mEvadeDirectionIndex(0)
|
, mEvadeDirectionIndex(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObstacleCheck::clear()
|
void ObstacleCheck::clear()
|
||||||
{
|
{
|
||||||
mWalkState = State_Norm;
|
mWalkState = WalkState::Initial;
|
||||||
mStuckDuration = 0;
|
|
||||||
mEvadeDuration = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObstacleCheck::isEvading() const
|
bool ObstacleCheck::isEvading() const
|
||||||
{
|
{
|
||||||
return mWalkState == State_Evade;
|
return mWalkState == WalkState::Evade;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* input - actor, duration (time since last check)
|
* input - actor, duration (time since last check)
|
||||||
* output - true if evasive action needs to be taken
|
* output - true if evasive action needs to be taken
|
||||||
*
|
*
|
||||||
* Walking state transitions (player greeting check not shown):
|
* Walking state transitions (player greeting check not shown):
|
||||||
*
|
*
|
||||||
* MoveNow <------------------------------------+
|
* Initial ----> Norm <--------> CheckStuck -------> Evade ---+
|
||||||
* | d|
|
* ^ ^ | f ^ | t ^ | |
|
||||||
* | |
|
* | | | | | | | |
|
||||||
* +-> State_Norm <---> State_CheckStuck --> State_Evade
|
* | +-+ +---+ +---+ | u
|
||||||
* ^ ^ | f ^ | t ^ | |
|
* | any < t < u |
|
||||||
* | | | | | | | |
|
* +---------------------------------------------+
|
||||||
* | +---+ +---+ +---+ | u
|
|
||||||
* | any < t < u |
|
|
||||||
* +--------------------------------------------+
|
|
||||||
*
|
*
|
||||||
* f = one reaction time
|
* f = one reaction time
|
||||||
* d = proximity to a closed door
|
|
||||||
* t = how long before considered stuck
|
* t = how long before considered stuck
|
||||||
* u = how long to move sideways
|
* u = how long to move sideways
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration)
|
void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration)
|
||||||
{
|
{
|
||||||
const osg::Vec3f pos = actor.getRefData().getPosition().asVec3();
|
const auto position = actor.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
if (mDistSameSpot == -1)
|
if (mWalkState == WalkState::Initial)
|
||||||
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor);
|
|
||||||
|
|
||||||
const float distSameSpot = mDistSameSpot * duration;
|
|
||||||
const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
|
|
||||||
|
|
||||||
mPrev = pos;
|
|
||||||
|
|
||||||
if (mWalkState != State_Evade)
|
|
||||||
{
|
{
|
||||||
if(!samePosition)
|
mWalkState = WalkState::Norm;
|
||||||
|
mStateDuration = 0;
|
||||||
|
mPrev = position;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mWalkState != WalkState::Evade)
|
||||||
|
{
|
||||||
|
const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) * duration;
|
||||||
|
const float prevDistance = (destination - mPrev).length();
|
||||||
|
const float currentDistance = (destination - position).length();
|
||||||
|
const float movedDistance = prevDistance - currentDistance;
|
||||||
|
|
||||||
|
mPrev = position;
|
||||||
|
|
||||||
|
if (movedDistance >= distSameSpot)
|
||||||
{
|
{
|
||||||
mWalkState = State_Norm;
|
mWalkState = WalkState::Norm;
|
||||||
mStuckDuration = 0;
|
mStateDuration = 0;
|
||||||
mEvadeDuration = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mWalkState = State_CheckStuck;
|
if (mWalkState == WalkState::Norm)
|
||||||
mStuckDuration += duration;
|
|
||||||
// consider stuck only if position unchanges for a period
|
|
||||||
if(mStuckDuration < DURATION_SAME_SPOT)
|
|
||||||
return; // still checking, note duration added to timer
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
mStuckDuration = 0;
|
mWalkState = WalkState::CheckStuck;
|
||||||
mWalkState = State_Evade;
|
mStateDuration = duration;
|
||||||
chooseEvasionDirection();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mStateDuration += duration;
|
||||||
|
if (mStateDuration < DURATION_SAME_SPOT)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWalkState = WalkState::Evade;
|
||||||
|
mStateDuration = 0;
|
||||||
|
chooseEvasionDirection();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mEvadeDuration += duration;
|
mStateDuration += duration;
|
||||||
if(mEvadeDuration >= DURATION_TO_EVADE)
|
if(mStateDuration >= DURATION_TO_EVADE)
|
||||||
{
|
{
|
||||||
// tried to evade, assume all is ok and start again
|
// tried to evade, assume all is ok and start again
|
||||||
mWalkState = State_Norm;
|
mWalkState = WalkState::Norm;
|
||||||
mEvadeDuration = 0;
|
mStateDuration = 0;
|
||||||
|
mPrev = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,30 +32,27 @@ namespace MWMechanics
|
||||||
bool isEvading() const;
|
bool isEvading() const;
|
||||||
|
|
||||||
// Updates internal state, call each frame for moving actor
|
// Updates internal state, call each frame for moving actor
|
||||||
void update(const MWWorld::Ptr& actor, float duration);
|
void update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration);
|
||||||
|
|
||||||
// change direction to try to fix "stuck" actor
|
// change direction to try to fix "stuck" actor
|
||||||
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
|
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// for checking if we're stuck
|
|
||||||
osg::Vec3f mPrev;
|
osg::Vec3f mPrev;
|
||||||
|
|
||||||
// directions to try moving in when get stuck
|
// directions to try moving in when get stuck
|
||||||
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];
|
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];
|
||||||
|
|
||||||
enum WalkState
|
enum class WalkState
|
||||||
{
|
{
|
||||||
State_Norm,
|
Initial,
|
||||||
State_CheckStuck,
|
Norm,
|
||||||
State_Evade
|
CheckStuck,
|
||||||
|
Evade
|
||||||
};
|
};
|
||||||
WalkState mWalkState;
|
WalkState mWalkState;
|
||||||
|
|
||||||
float mStuckDuration; // accumulate time here while in same spot
|
float mStateDuration;
|
||||||
float mEvadeDuration;
|
|
||||||
float mDistSameSpot; // take account of actor's speed
|
|
||||||
int mEvadeDirectionIndex;
|
int mEvadeDirectionIndex;
|
||||||
|
|
||||||
void chooseEvasionDirection();
|
void chooseEvasionDirection();
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
#include <components/detournavigator/navigatorimpl.hpp>
|
#include <components/detournavigator/navigatorimpl.hpp>
|
||||||
#include <components/detournavigator/exceptions.hpp>
|
#include <components/detournavigator/exceptions.hpp>
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
||||||
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
||||||
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
||||||
|
|
||||||
|
#include <boost/optional/optional_io.hpp>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
@ -655,4 +658,32 @@ namespace
|
||||||
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
||||||
})) << mPath;
|
})) << mPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, update_then_find_random_point_around_circle_should_return_position)
|
||||||
|
{
|
||||||
|
const std::array<btScalar, 5 * 5> heightfieldData {{
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, -25, -25, -25, -25,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
}};
|
||||||
|
btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
|
||||||
|
shape.setLocalScaling(btVector3(128, 128, 1));
|
||||||
|
|
||||||
|
mNavigator->addAgent(mAgentHalfExtents);
|
||||||
|
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
|
||||||
|
mNavigator->update(mPlayerPosition);
|
||||||
|
mNavigator->wait();
|
||||||
|
|
||||||
|
Misc::Rng::init(42);
|
||||||
|
|
||||||
|
const auto result = mNavigator->findRandomPointAroundCircle(mAgentHalfExtents, mStart, 100.0, Flag_walk);
|
||||||
|
|
||||||
|
ASSERT_EQ(result, boost::optional<osg::Vec3f>(osg::Vec3f(-209.95985412597656, 129.89768981933594, -0.26253718137741089)));
|
||||||
|
|
||||||
|
const auto distance = (*result - mStart).length();
|
||||||
|
|
||||||
|
EXPECT_EQ(distance, 85.260780334472656) << distance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,8 @@ add_component_dir(detournavigator
|
||||||
recastmeshobject
|
recastmeshobject
|
||||||
navmeshtilescache
|
navmeshtilescache
|
||||||
settings
|
settings
|
||||||
|
navigator
|
||||||
|
findrandompointaroundcircle
|
||||||
)
|
)
|
||||||
|
|
||||||
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||||
|
|
45
components/detournavigator/findrandompointaroundcircle.cpp
Normal file
45
components/detournavigator/findrandompointaroundcircle.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "findrandompointaroundcircle.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
#include "findsmoothpath.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
#include <DetourCommon.h>
|
||||||
|
#include <DetourNavMesh.h>
|
||||||
|
#include <DetourNavMeshQuery.h>
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings)
|
||||||
|
{
|
||||||
|
dtNavMeshQuery navMeshQuery;
|
||||||
|
initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes);
|
||||||
|
|
||||||
|
dtQueryFilter queryFilter;
|
||||||
|
queryFilter.setIncludeFlags(includeFlags);
|
||||||
|
|
||||||
|
dtPolyRef startRef = 0;
|
||||||
|
osg::Vec3f startPolygonPosition;
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
const auto status = navMeshQuery.findNearestPoly(start.ptr(), (halfExtents * (1 << i)).ptr(), &queryFilter,
|
||||||
|
&startRef, startPolygonPosition.ptr());
|
||||||
|
if (!dtStatusFailed(status) && startRef != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startRef == 0)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
|
||||||
|
dtPolyRef resultRef = 0;
|
||||||
|
osg::Vec3f resultPosition;
|
||||||
|
navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,
|
||||||
|
&Misc::Rng::rollProbability, &resultRef, resultPosition.ptr());
|
||||||
|
|
||||||
|
if (resultRef == 0)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
|
||||||
|
return boost::optional<osg::Vec3f>(resultPosition);
|
||||||
|
}
|
||||||
|
}
|
20
components/detournavigator/findrandompointaroundcircle.hpp
Normal file
20
components/detournavigator/findrandompointaroundcircle.hpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDRANDOMPOINTAROUNDCIRCLE_H
|
||||||
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDRANDOMPOINTAROUNDCIRCLE_H
|
||||||
|
|
||||||
|
#include "flags.hpp"
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
|
class dtNavMesh;
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
struct Settings;
|
||||||
|
|
||||||
|
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
20
components/detournavigator/navigator.cpp
Normal file
20
components/detournavigator/navigator.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "findrandompointaroundcircle.hpp"
|
||||||
|
#include "navigator.hpp"
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
boost::optional<osg::Vec3f> Navigator::findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const
|
||||||
|
{
|
||||||
|
const auto navMesh = getNavMesh(agentHalfExtents);
|
||||||
|
if (!navMesh)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
const auto settings = getSettings();
|
||||||
|
const auto result = DetourNavigator::findRandomPointAroundCircle(navMesh->lockConst()->getImpl(),
|
||||||
|
toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, start),
|
||||||
|
toNavMeshCoordinates(settings, maxRadius), includeFlags, settings);
|
||||||
|
if (!result)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
return boost::optional<osg::Vec3f>(fromNavMeshCoordinates(settings, *result));
|
||||||
|
}
|
||||||
|
}
|
|
@ -157,7 +157,6 @@ namespace DetourNavigator
|
||||||
* @param out the beginning of the destination range.
|
* @param out the beginning of the destination range.
|
||||||
* @return Output iterator to the element in the destination range, one past the last element of found path.
|
* @return Output iterator to the element in the destination range, one past the last element of found path.
|
||||||
* Equal to out if no path is found.
|
* Equal to out if no path is found.
|
||||||
* @throws InvalidArgument if there is no navmesh for given agentHalfExtents.
|
|
||||||
*/
|
*/
|
||||||
template <class OutputIterator>
|
template <class OutputIterator>
|
||||||
OutputIterator findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,
|
OutputIterator findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,
|
||||||
|
@ -194,6 +193,17 @@ namespace DetourNavigator
|
||||||
virtual const Settings& getSettings() const = 0;
|
virtual const Settings& getSettings() const = 0;
|
||||||
|
|
||||||
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location.
|
||||||
|
* @param agentHalfExtents allows to find navmesh for given actor.
|
||||||
|
* @param start path from given point.
|
||||||
|
* @param maxRadius limit maximum distance from start.
|
||||||
|
* @param includeFlags setup allowed surfaces for actor to walk.
|
||||||
|
* @return not empty optional with position if point is found and empty optional if point is not found.
|
||||||
|
*/
|
||||||
|
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue