Improvements to smooth NPC steering

actorid
scrawl 11 years ago
parent 2b15b8b484
commit 39d86a9468

@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
disease pickpocket levelledlist combat
disease pickpocket levelledlist combat steering
)
add_openmw_dir (mwbase

@ -574,8 +574,6 @@ namespace MWInput
double x = arg.xrel * mCameraSensitivity * (1.0f/256.f);
double y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier;
float scale = MWBase::Environment::get().getFrameDuration();
if(scale <= 0.0f) scale = 1.0f;
float rot[3];
rot[0] = -y;
@ -585,8 +583,8 @@ namespace MWInput
// Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot))
{
mPlayer->yaw(x/scale);
mPlayer->pitch(-y/scale);
mPlayer->yaw(x);
mPlayer->pitch(-y);
}
if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change

@ -1,23 +1,22 @@
#include "aicombat.hpp"
#include "aifollow.hpp"
#include "movement.hpp"
#include <OgreMath.h>
#include <OgreVector3.h>
#include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "character.hpp"
#include "../mwworld/inventorystore.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
#include <OgreMath.h>
#include <OgreVector3.h>
#include "steering.hpp"
#include "movement.hpp"
#include "character.hpp" // fixme: for getActiveWeapon
namespace
{
@ -43,7 +42,9 @@ namespace MWMechanics
mReadyToAttack(false),
mStrike(false),
mCombatMove(false),
mMovement()
mRotate(false),
mMovement(),
mTargetAngle(0)
{
}
@ -68,10 +69,16 @@ namespace MWMechanics
mCombatMove = false;
}
}
actor.getClass().getMovementSettings(actor) = mMovement;
if (mRotate)
{
if (zTurn(actor, Ogre::Degree(mTargetAngle)))
mRotate = false;
}
//actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack);
mTimerAttack -= duration;
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike);
@ -156,12 +163,7 @@ namespace MWMechanics
weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
}
//MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false);
ESM::Position pos = actor.getRefData().getPosition();
float zAngle;
float rangeMelee;
float rangeCloseUp;
@ -189,12 +191,8 @@ namespace MWMechanics
//Melee and Close-up combat
vDir.z = 0;
float dirLen = vDir.length();
zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees();
// TODO: use movement settings instead of rotating directly
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
//MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees();
mRotate = true;
//bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
if (mFollowTarget && distBetween > rangeMelee)
@ -237,12 +235,6 @@ namespace MWMechanics
else
{
//target is at far distance: build path to target OR follow target (if previously actor had reached it once)
/*
//apply when AIFOLLOW package implementation will be existent
if(mFollowTarget)
actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/
mFollowTarget = false;
buildNewPath(actor);
@ -252,13 +244,10 @@ namespace MWMechanics
//try shortcut
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
else
zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
// TODO: use movement settings instead of rotating directly
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
//mMovement.mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]);
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
mRotate = true;
mMovement.mPosition[1] = 1;
mReadyToAttack = false;
@ -294,6 +283,8 @@ namespace MWMechanics
}
}
actor.getClass().getMovementSettings(actor) = mMovement;
return false;
}

@ -29,16 +29,21 @@ namespace MWMechanics
private:
PathFinder mPathFinder;
//controls duration of the actual strike
// controls duration of the actual strike
float mTimerAttack;
float mTimerReact;
//controls duration of the sideway & forward moves
//when mCombatMove is true
// controls duration of the sideway & forward moves
// when mCombatMove is true
float mTimerCombatMove;
// the z rotation angle (degrees) we want to reach
// used every frame when mRotate is true
float mTargetAngle;
bool mReadyToAttack, mStrike;
bool mFollowTarget;
bool mCombatMove;
bool mRotate;
MWMechanics::Movement mMovement;
MWWorld::Ptr mTarget;

@ -8,6 +8,8 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "steering.hpp"
namespace
{
float sgn(float a)
@ -33,7 +35,7 @@ namespace MWMechanics
{
mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long
// The CS Help File states that if a duration is given, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0;
@ -52,7 +54,7 @@ namespace MWMechanics
{
mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long
// The CS Help File states that if a duration is given, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0;
@ -89,25 +91,23 @@ namespace MWMechanics
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
// Check if actor is near the border of an inactive cell. If so, pause walking.
if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
return false;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
// Check if actor is near the border of an inactive cell. If so, pause walking.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
return false;
}
}
@ -151,8 +151,7 @@ namespace MWMechanics
if(distanceBetweenResult <= mMaxDist * mMaxDist)
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
// TODO: use movement settings instead of rotating directly
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470;
}

@ -6,15 +6,17 @@
#include "movement.hpp"
#include <OgreMath.h>
#include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
{
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
{
}
{
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
{
}
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{
@ -45,14 +47,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
}
}
ESM::Pathgrid::Point dest;
dest.mX = target.getRefData().getPosition().pos[0];
dest.mY = target.getRefData().getPosition().pos[1];
ESM::Pathgrid::Point dest;
dest.mX = target.getRefData().getPosition().pos[0];
dest.mY = target.getRefData().getPosition().pos[1];
dest.mZ = target.getRefData().getPosition().pos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
if(mPathFinder.getPath().empty())
@ -88,18 +90,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
//MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]);
//std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n";
//MWWorld::Class::get(actor).get
}
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
< 100*100)
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
}
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
< 100*100)
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
else
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
return false;
}
@ -109,12 +107,12 @@ std::string MWMechanics::AiFollow::getFollowedActor()
return mActorId;
}
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
{
return new AiFollow(*this);
}
int MWMechanics::AiFollow::getTypeId() const
{
return TypeIdFollow;
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
{
return new AiFollow(*this);
}
int MWMechanics::AiFollow::getTypeId() const
{
return TypeIdFollow;
}

@ -1,11 +1,12 @@
#include "aitravel.hpp"
#include "movement.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "steering.hpp"
#include "movement.hpp"
namespace
{
float sgn(float a)
@ -86,9 +87,7 @@ namespace MWMechanics
return true;
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
// TODO: use movement settings instead of rotating directly
world->rotateObject(actor, 0, 0, zAngle, false);
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
movement.mPosition[1] = 1;
return false;

@ -11,6 +11,8 @@
#include "creaturestats.hpp"
#include <OgreVector3.h>
#include "steering.hpp"
namespace
{
float sgn(float a)
@ -282,11 +284,6 @@ namespace MWMechanics
if(mWalking)
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
// TODO: use movement settings instead of rotating directly
world->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{
stopWalking(actor);
@ -294,6 +291,12 @@ namespace MWMechanics
mWalking = false;
mChooseAction = true;
}
else
{
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
}
}
return false;

@ -1111,9 +1111,9 @@ void CharacterController::update(float duration)
if (!mSkipAnim)
{
rot *= Ogre::Math::RadiansToDegrees(1.0f);
if(mHitState != CharState_KnockDown)
{
rot *= duration * Ogre::Math::RadiansToDegrees(1.0f);
world->rotateObject(mPtr, rot.x, rot.y, rot.z, true);
}
else //avoid z-rotating for knockdown

@ -3,7 +3,6 @@
#include <components/esm/loadpgrd.hpp>
#include <list>
#include <boost/graph/adjacency_list.hpp>
namespace MWWorld
{
@ -26,8 +25,10 @@ namespace MWMechanics
bool checkPathCompleted(float x, float y, float z);
///< \Returns true if the last point of the path has been reached.
bool checkWaypoint(float x, float y, float z);
///< \Returns true if a way point was reached
float getZAngleToNext(float x, float y) const;
float getDistToNext(float x, float y, float z);

@ -0,0 +1,43 @@
#include "steering.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwbase/environment.hpp"
#include "movement.hpp"
namespace MWMechanics
{
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle)
{
Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]);
Ogre::Radian diff (targetAngle - currentAngle);
if (diff >= Ogre::Degree(180))
{
// Turning the other way would be a better idea
diff = diff-Ogre::Degree(360);
}
else if (diff <= Ogre::Degree(-180))
{
diff = Ogre::Degree(360)-diff;
}
Ogre::Radian absDiff = Ogre::Math::Abs(diff);
// The turning animation actually moves you slightly, so the angle will be wrong again.
// Use epsilon to prevent jerkiness.
const Ogre::Degree epsilon (0.5);
if (absDiff < epsilon)
return true;
// Max. speed of 10 radian per sec
Ogre::Radian limit = Ogre::Radian(10) * MWBase::Environment::get().getFrameDuration();
if (absDiff > limit)
diff = Ogre::Math::Sign(diff) * limit;
actor.getClass().getMovementSettings(actor).mRotation[2] = diff.valueRadians();
return false;
}
}

@ -0,0 +1,19 @@
#ifndef OPENMW_MECHANICS_STEERING_H
#include <OgreMath.h>
namespace MWWorld
{
class Ptr;
}
namespace MWMechanics
{
/// configure rotation settings for an actor to reach this target angle (eventually)
/// @return have we reached the target angle?
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle);
}
#endif
Loading…
Cancel
Save