mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-01 03:45:35 +00:00
Merge branch 'avoid_collision_update' into 'master'
Update logic of "NPCs avoid collisions" See merge request OpenMW/openmw!318
This commit is contained in:
commit
0e8c5c5034
1 changed files with 19 additions and 7 deletions
|
@ -1668,8 +1668,9 @@ namespace MWMechanics
|
|||
void Actors::predictAndAvoidCollisions()
|
||||
{
|
||||
const float minGap = 10.f;
|
||||
const float maxDistToCheck = 100.f;
|
||||
const float maxTimeToCheck = 1.f;
|
||||
const float maxDistForPartialAvoiding = 200.f;
|
||||
const float maxDistForStrictAvoiding = 100.f;
|
||||
const float maxTimeToCheck = 2.0f;
|
||||
static const bool giveWayWhenIdle = Settings::Manager::getBool("NPCs give way", "Game");
|
||||
|
||||
MWWorld::Ptr player = getPlayer();
|
||||
|
@ -1680,9 +1681,15 @@ namespace MWMechanics
|
|||
if (ptr == player)
|
||||
continue; // Don't interfere with player controls.
|
||||
|
||||
float maxSpeed = ptr.getClass().getMaxSpeed(ptr);
|
||||
if (maxSpeed == 0.0)
|
||||
continue; // Can't move, so there is no sense to predict collisions.
|
||||
|
||||
Movement& movement = ptr.getClass().getMovementSettings(ptr);
|
||||
osg::Vec2f origMovement(movement.mPosition[0], movement.mPosition[1]);
|
||||
bool isMoving = origMovement.length2() > 0.01;
|
||||
if (movement.mPosition[1] < 0)
|
||||
continue; // Actors can not see others when move backward.
|
||||
|
||||
// Moving NPCs always should avoid collisions.
|
||||
// Standing NPCs give way to moving ones if they are not in combat (or pursue) mode and either
|
||||
|
@ -1710,11 +1717,11 @@ namespace MWMechanics
|
|||
if (!shouldAvoidCollision)
|
||||
continue;
|
||||
|
||||
float maxSpeed = ptr.getClass().getMaxSpeed(ptr);
|
||||
osg::Vec2f baseSpeed = origMovement * maxSpeed;
|
||||
osg::Vec3f basePos = ptr.getRefData().getPosition().asVec3();
|
||||
float baseRotZ = ptr.getRefData().getPosition().rot[2];
|
||||
osg::Vec3f halfExtents = world->getHalfExtents(ptr);
|
||||
float maxDistToCheck = isMoving ? maxDistForPartialAvoiding : maxDistForStrictAvoiding;
|
||||
|
||||
float timeToCollision = maxTimeToCheck;
|
||||
osg::Vec2f movementCorrection(0, 0);
|
||||
|
@ -1730,9 +1737,10 @@ namespace MWMechanics
|
|||
osg::Vec3f otherHalfExtents = world->getHalfExtents(otherPtr);
|
||||
osg::Vec3f deltaPos = otherPtr.getRefData().getPosition().asVec3() - basePos;
|
||||
osg::Vec2f relPos = Misc::rotateVec2f(osg::Vec2f(deltaPos.x(), deltaPos.y()), baseRotZ);
|
||||
float dist = deltaPos.length();
|
||||
|
||||
// Ignore actors which are not close enough or come from behind.
|
||||
if (deltaPos.length2() > maxDistToCheck * maxDistToCheck || relPos.y() < 0)
|
||||
if (dist > maxDistToCheck || relPos.y() < 0)
|
||||
continue;
|
||||
|
||||
// Don't check for a collision if vertical distance is greater then the actor's height.
|
||||
|
@ -1767,16 +1775,20 @@ namespace MWMechanics
|
|||
timeToCollision = t;
|
||||
angleToApproachingActor = std::atan2(deltaPos.x(), deltaPos.y());
|
||||
osg::Vec2f posAtT = relPos + relSpeed * t;
|
||||
float coef = (posAtT.x() * relSpeed.x() + posAtT.y() * relSpeed.y()) / (collisionDist * maxSpeed);
|
||||
float coef = (posAtT.x() * relSpeed.x() + posAtT.y() * relSpeed.y()) / (collisionDist * collisionDist * maxSpeed);
|
||||
coef *= osg::clampBetween((maxDistForPartialAvoiding - dist) / (maxDistForPartialAvoiding - maxDistForStrictAvoiding), 0.f, 1.f);
|
||||
movementCorrection = posAtT * coef;
|
||||
// Step to the side rather than backward. Otherwise player will be able to push the NPC far away from it's original location.
|
||||
movementCorrection.y() = std::max(0.f, movementCorrection.y());
|
||||
if (otherPtr.getClass().getCreatureStats(otherPtr).isDead())
|
||||
// In case of dead body still try to go around (it looks natural), but reduce the correction twice.
|
||||
movementCorrection.y() *= 0.5f;
|
||||
}
|
||||
|
||||
if (timeToCollision < maxTimeToCheck)
|
||||
{
|
||||
// Try to evade the nearest collision.
|
||||
osg::Vec2f newMovement = origMovement + movementCorrection;
|
||||
// Step to the side rather than backward. Otherwise player will be able to push the NPC far away from it's original location.
|
||||
newMovement.y() = std::max(newMovement.y(), 0.f);
|
||||
if (isMoving)
|
||||
{ // Keep the original speed.
|
||||
newMovement.normalize();
|
||||
|
|
Loading…
Reference in a new issue