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:
parent
3e4cedb7a8
commit
e323e6e7e6
3 changed files with 61 additions and 58 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue