Smooth turning; smooth stopping; combat headtracking

pull/3009/head
Petr Mikheev 4 years ago
parent acdf4cb5e3
commit 71ba94a89a

@ -473,6 +473,10 @@ namespace MWMechanics
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
{
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
if (smoothMovement)
return;
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
MWMechanics::AiSequence& seq = stats.getAiSequence();
@ -481,9 +485,10 @@ namespace MWMechanics
osg::Vec3f targetPos = seq.getActivePackage().getDestination();
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
float distance = (targetPos - actorPos).length();
if (distance < DECELERATE_DISTANCE)
{
float speedCoef = std::max(0.7f, 0.1f * (distance/64.f + 2.f));
float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE);
auto& movement = actor.getClass().getMovementSettings(actor);
movement.mPosition[0] *= speedCoef;
movement.mPosition[1] *= speedCoef;
@ -1769,14 +1774,12 @@ namespace MWMechanics
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);
// 1. Unconsious actor can not track target
// 2. Actors in combat and pursue mode do not bother to headtrack
// 3. Player character does not use headtracking in the 1st-person view
if (!stats.getKnockedDown() &&
!stats.getAiSequence().isInCombat() &&
!stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) &&
!firstPersonPlayer)
if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue)
{
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{
@ -1786,6 +1789,17 @@ namespace MWMechanics
}
}
if (!stats.getKnockedDown() && !isPlayer && inCombatOrPursue)
{
// Actors in combat and pursue mode always look at their target.
for (const auto& package : stats.getAiSequence())
{
headTrackTarget = package->getTarget();
if (!headTrackTarget.isEmpty())
break;
}
}
ctrl->setHeadTrackTarget(headTrackTarget);
}

@ -23,7 +23,7 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
actorClass.getMovementSettings(actor).mPosition[1] = 1;
smoothTurn(actor, -180, 0);
smoothTurn(actor, -osg::PI / 2, 0);
return false;
}

@ -5,6 +5,7 @@
#include <components/esm/loadmgef.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/misc/coordinateconverter.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
@ -87,6 +88,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
//... But AI processing distance may increase in the future.
if (isNearInactiveCell(position))
{
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
return false;
@ -169,12 +171,34 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
}
// turn to next path point by X,Z axes
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
float zAngleToNext = mPathFinder.getZAngleToNext(position.x(), position.y());
zTurn(actor, zAngleToNext);
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
mObstacleCheck.update(actor, destination, duration);
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
if (smoothMovement)
{
const float smoothTurnReservedDist = 150;
auto& movement = actor.getClass().getMovementSettings(actor);
float distToNextSqr = osg::Vec2f(destination.x() - position.x(), destination.y() - position.y()).length2();
float diffAngle = zAngleToNext - actor.getRefData().getPosition().rot[2];
if (std::cos(diffAngle) < -0.1)
movement.mPosition[0] = movement.mPosition[1] = 0;
else if (distToNextSqr > smoothTurnReservedDist * smoothTurnReservedDist)
{ // Go forward (and slowly turn towards the next path point)
movement.mPosition[0] = 0;
movement.mPosition[1] = 1;
}
else
{ // Next path point is near, so use diagonal movement to follow the path precisely.
movement.mPosition[0] = std::sin(diffAngle);
movement.mPosition[1] = std::max(std::cos(diffAngle), 0.f);
}
}
// handle obstacles on the way
evadeObstacles(actor);

@ -96,6 +96,7 @@ namespace MWMechanics
void stopMovement(const MWWorld::Ptr& actor)
{
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
}

@ -88,6 +88,24 @@ namespace
const auto halfExtents = world->getHalfExtents(actor);
return 2.0 * halfExtents.z();
}
// Returns true if turn in `p2` is less than 10 degrees and all the 3 points are almost on one line.
bool isAlmostStraight(const osg::Vec3f& p1, const osg::Vec3f& p2, const osg::Vec3f& p3, float pointTolerance) {
osg::Vec3f v1 = p1 - p2;
osg::Vec3f v3 = p3 - p2;
v1.z() = v3.z() = 0;
float dotProduct = v1.x() * v3.x() + v1.y() * v3.y();
float crossProduct = v1.x() * v3.y() - v1.y() * v3.x();
// Check that the angle between v1 and v3 is less or equal than 10 degrees.
static const float cos170 = std::cos(osg::PI / 180 * 170);
bool checkAngle = dotProduct <= cos170 * v1.length() * v3.length();
// Check that distance from p2 to the line (p1, p3) is less or equal than `pointTolerance`.
bool checkDist = std::abs(crossProduct) <= pointTolerance * (p3 - p1).length() * 2;
return checkAngle && checkDist;
}
}
namespace MWMechanics
@ -286,6 +304,11 @@ namespace MWMechanics
while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance)
mPath.pop_front();
while (mPath.size() > 2 && isAlmostStraight(mPath[0], mPath[1], mPath[2], pointTolerance))
mPath.erase(mPath.begin() + 1);
if (mPath.size() > 1 && isAlmostStraight(position, mPath[0], mPath[1], pointTolerance))
mPath.pop_front();
if (mPath.size() == 1 && sqrDistanceIgnoreZ(mPath.front(), position) < destinationTolerance * destinationTolerance)
mPath.pop_front();
}

@ -1,5 +1,8 @@
#include "steering.hpp"
#include <components/misc/mathutil.hpp>
#include <components/settings/settings.hpp>
#include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp"
@ -12,19 +15,8 @@ namespace MWMechanics
bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians)
{
float currentAngle (actor.getRefData().getPosition().rot[axis]);
float diff (targetAngleRadians - currentAngle);
if (std::abs(diff) >= osg::DegreesToRadians(180.f))
{
if (diff >= 0)
{
diff = diff - osg::DegreesToRadians(360.f);
}
else
{
diff = osg::DegreesToRadians(360.f) + diff;
}
}
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
float diff = Misc::normalizeAngle(targetAngleRadians - actor.getRefData().getPosition().rot[axis]);
float absDiff = std::abs(diff);
// The turning animation actually moves you slightly, so the angle will be wrong again.
@ -33,10 +25,14 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
return true;
float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
if (smoothMovement)
limit *= std::min(absDiff / osg::PI + 0.1, 0.5);
if (absDiff > limit)
diff = osg::sign(diff) * limit;
actor.getClass().getMovementSettings(actor).mRotation[axis] = diff;
movement.mRotation[axis] = diff;
return false;
}

@ -325,6 +325,9 @@ uncapped damage fatigue = false
# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.
turn to movement direction = false
# Makes all movements of NPCs and player more smooth.
smooth movement = false
# Makes player swim a bit upward from the line of sight.
swim upward correction = false

Loading…
Cancel
Save