1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-28 23:36:41 +00:00

Consider moved distance in direction to destination for obstacle check

Assume actor is stuck when it's not able to move in the destination
direction with maximum speed. Approach to check moved distance from the
previous point doesn't work good for slow and big actors. When they face
obstacle they're trying to move along with oscillation so check is
passing but they don't get any closer to the destination.
This commit is contained in:
elsid 2020-01-20 22:08:25 +01:00
parent 3e4cedb7a8
commit e323e6e7e6
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
3 changed files with 61 additions and 58 deletions

View file

@ -197,7 +197,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
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
evadeObstacles(actor);

View file

@ -77,24 +77,20 @@ namespace MWMechanics
}
ObstacleCheck::ObstacleCheck()
: mWalkState(State_Norm)
, mStuckDuration(0)
, mEvadeDuration(0)
, mDistSameSpot(-1) // avoid calculating it each time
: mWalkState(WalkState::Initial)
, mStateDuration(0)
, mEvadeDirectionIndex(0)
{
}
void ObstacleCheck::clear()
{
mWalkState = State_Norm;
mStuckDuration = 0;
mEvadeDuration = 0;
mWalkState = WalkState::Initial;
}
bool ObstacleCheck::isEvading() const
{
return mWalkState == State_Evade;
return mWalkState == WalkState::Evade;
}
/*
@ -103,63 +99,72 @@ namespace MWMechanics
*
* Walking state transitions (player greeting check not shown):
*
* MoveNow <------------------------------------+
* | d|
* | |
* +-> State_Norm <---> State_CheckStuck --> State_Evade
* Initial ----> Norm <--------> CheckStuck -------> Evade ---+
* ^ ^ | f ^ | t ^ | |
* | | | | | | | |
* | +---+ +---+ +---+ | u
* | +-+ +---+ +---+ | u
* | any < t < u |
* +--------------------------------------------+
* +---------------------------------------------+
*
* f = one reaction time
* d = proximity to a closed door
* t = how long before considered stuck
* 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)
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 (mWalkState == WalkState::Initial)
{
if(!samePosition)
{
mWalkState = State_Norm;
mStuckDuration = 0;
mEvadeDuration = 0;
mWalkState = WalkState::Norm;
mStateDuration = 0;
mPrev = position;
return;
}
mWalkState = State_CheckStuck;
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
if (mWalkState != WalkState::Evade)
{
mStuckDuration = 0;
mWalkState = State_Evade;
chooseEvasionDirection();
}
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 = WalkState::Norm;
mStateDuration = 0;
return;
}
mEvadeDuration += duration;
if(mEvadeDuration >= DURATION_TO_EVADE)
if (mWalkState == WalkState::Norm)
{
mWalkState = WalkState::CheckStuck;
mStateDuration = duration;
return;
}
mStateDuration += duration;
if (mStateDuration < DURATION_SAME_SPOT)
{
return;
}
mWalkState = WalkState::Evade;
mStateDuration = 0;
chooseEvasionDirection();
return;
}
mStateDuration += duration;
if(mStateDuration >= DURATION_TO_EVADE)
{
// tried to evade, assume all is ok and start again
mWalkState = State_Norm;
mEvadeDuration = 0;
mWalkState = WalkState::Norm;
mStateDuration = 0;
mPrev = position;
}
}

View file

@ -32,30 +32,27 @@ namespace MWMechanics
bool isEvading() const;
// 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
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
private:
// for checking if we're stuck
osg::Vec3f mPrev;
// directions to try moving in when get stuck
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];
enum WalkState
enum class WalkState
{
State_Norm,
State_CheckStuck,
State_Evade
Initial,
Norm,
CheckStuck,
Evade
};
WalkState mWalkState;
float mStuckDuration; // accumulate time here while in same spot
float mEvadeDuration;
float mDistSameSpot; // take account of actor's speed
float mStateDuration;
int mEvadeDirectionIndex;
void chooseEvasionDirection();