1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-07 03:12:36 +01:00

Refactor character movement.

This commit is contained in:
Daniel Evans 2014-08-26 22:54:26 +01:00
parent 668848952b
commit f7398f17f8
7 changed files with 208 additions and 210 deletions

View File

@ -41,6 +41,10 @@ protected:
bool updateActivity();
void setActivity(Activity* activity);
glm::vec3 rawMovement;
bool running;
public:
CharacterController(CharacterObject* character);
@ -82,6 +86,10 @@ public:
* @return Returns the Character Object
*/
CharacterObject* getCharacter() const;
void setRawMovement(const glm::vec3& movement);
void setRunning(bool run);
};
#define DECL_ACTIVITY( activity_name ) \

View File

@ -8,20 +8,15 @@ class PlayerController : public CharacterController
glm::quat cameraRotation;
glm::vec3 direction;
glm::vec3 _rawDirection;
glm::quat lastRotation;
bool running;
bool _enabled;
public:
PlayerController(CharacterObject* character);
void setRunning(bool run);
/**
* @brief Enables and disables player input.
* @todo actually implement input being disabled.

View File

@ -18,6 +18,14 @@ struct AnimationGroup
Animation* walk_start;
Animation* run;
Animation* walk_right;
Animation* walk_right_start;
Animation* walk_left;
Animation* walk_left_start;
Animation* walk_back;
Animation* walk_back_start;
Animation* jump_start;
Animation* jump_glide;
Animation* jump_land;
@ -50,6 +58,8 @@ private:
void createActor(const glm::vec3& size = glm::vec3(0.35f, 0.35f, 1.3f));
void destroyActor();
bool _fixedAnimation;
// Incredibly hacky "move in this direction".
bool _hasTargetPosition;
glm::vec3 _targetPosition;
@ -58,25 +68,6 @@ private:
int _activeInventoryItem;
public:
enum Action {
None,
Idle,
Walk,
Run,
Crouch,
Jump,
Falling,
Landing,
VehicleOpen,
VehicleGetIn,
VehicleGetOut,
VehicleDrive,
VehicleSit,
KnockedDown,
GettingUp,
FiringWeapon
};
std::shared_ptr<CharacterData> ped;
btKinematicCharacterController* physCharacter;
@ -99,10 +90,6 @@ public:
Type type() { return Character; }
Action currentActivity;
void enterAction(Action act);
void tick(float dt);
/**
@ -142,6 +129,7 @@ public:
void setTargetPosition( const glm::vec3& target );
void clearTargetPosition();
void playAnimation(Animation* animation, bool repeat, bool fixed);
virtual bool isAnimationFixed() const;
void addToInventory( InventoryItem* item );

View File

@ -20,12 +20,11 @@ void CharacterController::setActivity(CharacterController::Activity* activity)
if( _currentActivity == nullptr ) {
// TODO make idle an actual activity or something,
character->clearTargetPosition();
character->enterAction( CharacterObject::Idle );
}
}
CharacterController::CharacterController(CharacterObject* character)
: character(character), _currentActivity(nullptr), _nextActivity(nullptr)
: character(character), _currentActivity(nullptr), _nextActivity(nullptr), running(false)
{
character->controller = this;
}
@ -49,6 +48,79 @@ void CharacterController::setNextActivity(CharacterController::Activity* activit
void CharacterController::update(float dt)
{
auto d = rawMovement;
if( character->getCurrentVehicle() ) {
// Nevermind, the player is in a vehicle.
character->getCurrentVehicle()->setSteeringAngle(d.y);
// TODO what is handbraking.
character->getCurrentVehicle()->setThrottle(d.x);
// if the character isn't doing anything, play sitting anim.
if( _currentActivity == nullptr ) {
/// @todo play _low variant if car has low flag.
character->playAnimation(character->animations.car_sit, true, false);
}
}
else {
if( d.x > 0.001f ) {
if( running ) {
if( character->animator->getAnimation() != character->animations.run ) {
character->playAnimation(character->animations.run, true, true);
}
}
else {
if( character->animator->getAnimation() == character->animations.walk_start ) {
if( character->animator->isCompleted() ) {
character->playAnimation(character->animations.walk, true, true);
}
}
else if( character->animator->getAnimation() != character->animations.walk ) {
character->playAnimation(character->animations.walk_start, false, true);
}
}
}
else if( d.x < -0.001f ) {
if( character->animator->getAnimation() == character->animations.walk_back_start ) {
if( character->animator->isCompleted() ) {
character->playAnimation(character->animations.walk_back, true, true);
}
}
else if( character->animator->getAnimation() != character->animations.walk_back ) {
character->playAnimation(character->animations.walk_back_start, false, true);
}
}
else if( glm::abs(d.y) > 0.001f ) {
if( d.y < 0.f ) {
if( character->animator->getAnimation() == character->animations.walk_right_start ) {
if( character->animator->isCompleted() ) {
character->playAnimation(character->animations.walk_right, true, true);
}
}
else if( character->animator->getAnimation() != character->animations.walk_right ) {
character->playAnimation(character->animations.walk_right_start, false, true);
}
}
else {
if( character->animator->getAnimation() == character->animations.walk_left_start ) {
if( character->animator->isCompleted() ) {
character->playAnimation(character->animations.walk_left, true, true);
}
}
else if( character->animator->getAnimation() != character->animations.walk_left ) {
character->playAnimation(character->animations.walk_left_start, false, true);
}
}
}
if( _currentActivity == nullptr ) {
if( glm::length(d) < 0.001f ) {
character->playAnimation(character->animations.idle, true, false);
}
}
}
if( updateActivity() ) {
if( _currentActivity ) {
delete _currentActivity;
@ -78,21 +150,30 @@ CharacterObject *CharacterController::getCharacter() const
return character;
}
void CharacterController::setRawMovement(const glm::vec3 &movement)
{
rawMovement = movement;
}
void CharacterController::setRunning(bool run)
{
running = run;
}
bool Activities::GoTo::update(CharacterObject *character, CharacterController *controller)
{
/* TODO: Use the ai nodes to navigate to the position */
glm::vec3 targetDirection = target - character->getPosition();
if( glm::length(targetDirection) < 0.01f ) {
character->enterAction(CharacterObject::Idle);
return true;
}
character->setTargetPosition( target );
glm::quat r( glm::vec3{ 0.f, 0.f, atan2(targetDirection.y, targetDirection.x) - glm::half_pi<float>() } );
character->rotation = r;
character->enterAction(CharacterObject::Walk);
controller->setRawMovement({1.f, 0.f, 0.f});
return false;
}
@ -106,20 +187,25 @@ bool Activities::EnterVehicle::update(CharacterObject *character, CharacterContr
return true;
}
auto anm_open = character->animations.car_open_lhs;
auto anm_enter = character->animations.car_getin_lhs;
if( entering ) {
// TODO: decouple from the character's animator.
if( character->currentActivity == CharacterObject::VehicleGetIn ) {
character->enterVehicle(vehicle, seat);
if( character->animator->getAnimation() == anm_open ) {
if( character->animator->isCompleted() ) {
character->playAnimation(anm_enter, false, false);
character->enterVehicle(vehicle, seat);
}
else {
//character->setPosition(vehicle->getSeatEntryPosition(seat));
character->rotation = vehicle->getRotation();
}
}
else if( character->currentActivity == CharacterObject::VehicleOpen ) {
// Ensure the player remains aligned with the vehicle
character->setPosition(vehicle->getSeatEntryPosition(seat));
character->rotation = vehicle->getRotation();
}
else {
// VehicleGetIn is over, finish activity
return true;
else if( character->animator->getAnimation() == anm_enter ) {
if( character->animator->isCompleted() ) {
// VehicleGetIn is over, finish activity
return true;
}
}
}
else {
@ -132,18 +218,17 @@ bool Activities::EnterVehicle::update(CharacterObject *character, CharacterContr
if( targetDistance <= 0.4f ) {
entering = true;
// Warp character to vehicle orientation
character->controller->setRawMovement({0.f, 0.f, 0.f});
character->rotation = vehicle->getRotation();
character->enterAction(CharacterObject::VehicleOpen);
character->playAnimation(anm_open, false, false);
}
else if( targetDistance > 15.f ) {
return true; // Give up if the vehicle is too far away.
}
else {
character->setTargetPosition( target );
glm::quat r( glm::vec3{ 0.f, 0.f, atan2(targetDirection.y, targetDirection.x) - glm::half_pi<float>() } );
character->rotation = r;
character->enterAction(CharacterObject::Walk);
character->controller->setRawMovement({1.f, 0.f, 0.f});
}
}
return false;
@ -155,6 +240,7 @@ bool Activities::ExitVehicle::update(CharacterObject *character, CharacterContro
if( character->getCurrentVehicle() == nullptr ) return true;
auto vehicle = character->getCurrentVehicle();
auto anm_exit = character->animations.car_getout_lhs;
if( vehicle->vehicle->type == VehicleData::BOAT ) {
auto ppos = character->getPosition();
@ -163,16 +249,20 @@ bool Activities::ExitVehicle::update(CharacterObject *character, CharacterContro
return true;
}
if( character->currentActivity == CharacterObject::Idle ) {
auto exitpos = vehicle->getSeatEntryPosition(character->getCurrentSeat());
if( character->animator->getAnimation() == anm_exit ) {
if( character->animator->isCompleted() ) {
auto exitpos = vehicle->getSeatEntryPosition(character->getCurrentSeat());
character->enterVehicle(nullptr, 0);
character->setPosition(exitpos);
character->enterVehicle(nullptr, 0);
character->setPosition(exitpos);
return true;
return true;
}
}
else {
character->playAnimation(anm_exit, false, false);
}
character->enterAction(CharacterObject::VehicleGetOut);
return false;
}
@ -187,11 +277,12 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
if( wepdata->fireType == WeaponData::INSTANT_HIT ) {
if( _item->isFiring() ) {
character->enterAction(CharacterObject::FiringWeapon);
auto shootanim = character->engine->gameData.animations[wepdata->animation1];
if( shootanim ) {
character->animator->setAnimation(shootanim, false);
if( character->animator->getAnimation() != shootanim ) {
character->playAnimation(shootanim, false, false);
}
auto loopstart = wepdata->animLoopStart / 100.f;
auto loopend = wepdata->animLoopEnd / 100.f;
@ -211,7 +302,6 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
}
else {
if( character->animator->isCompleted() ) {
character->enterAction(CharacterObject::Idle);
return true;
}
}
@ -220,7 +310,6 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
else if( wepdata->fireType == WeaponData::PROJECTILE ) {
auto shootanim = character->engine->gameData.animations[wepdata->animation1];
auto throwanim = character->engine->gameData.animations[wepdata->animation2];
character->enterAction(CharacterObject::FiringWeapon);
if( character->animator->getAnimation() == shootanim ) {
if( character->animator->isCompleted() ) {
@ -228,7 +317,6 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
}
}
else if( character->animator->getAnimation() == throwanim ) {
auto firetime = wepdata->animCrouchFirePoint / 100.f;
auto currID = character->animator->getAnimationTime();
@ -237,7 +325,6 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
_fired = true;
}
if( character->animator->isCompleted() ) {
character->enterAction(CharacterObject::Idle);
return true;
}
}

View File

@ -3,19 +3,14 @@
#include <objects/VehicleObject.hpp>
#include <engine/GameWorld.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <engine/Animator.hpp>
PlayerController::PlayerController(CharacterObject* character)
: CharacterController(character), lastRotation(glm::vec3(0.f, 0.f, 0.f)), running(false),
_enabled(true)
: CharacterController(character), lastRotation(glm::vec3(0.f, 0.f, 0.f)), _enabled(true)
{
}
void PlayerController::setRunning(bool run)
{
running = run;
}
void PlayerController::setInputEnabled(bool enabled)
{
_enabled = enabled;
@ -28,8 +23,10 @@ void PlayerController::updateCameraDirection(const glm::quat& rot)
void PlayerController::updateMovementDirection(const glm::vec3& dir, const glm::vec3 &rawdirection)
{
direction = dir;
_rawDirection = rawdirection;
if( _currentActivity == nullptr ) {
direction = dir;
setRawMovement(rawdirection);
}
}
void PlayerController::exitVehicle()
@ -68,26 +65,22 @@ void PlayerController::update(float dt)
skipActivity();
}
if( _currentActivity == nullptr ) {
if( character->currentActivity != CharacterObject::Jump )
{
if( glm::length(direction) > 0.001f ) {
character->enterAction(running ? CharacterObject::Run : CharacterObject::Walk);
}
else {
character->enterAction(CharacterObject::Idle);
}
if( _currentActivity == nullptr && glm::length(rawMovement) > 0.0f ) {
float rotationOffset = 0.f;
if( character->getCurrentVehicle() ) {
character->getCurrentVehicle()->setSteeringAngle(_rawDirection.y);
// TODO what is handbraking.
character->getCurrentVehicle()->setThrottle(_rawDirection.x);
if( glm::abs(rawMovement.x) < 0.01f ) {
if( rawMovement.y < -0.01f ) {
rotationOffset = glm::half_pi<float>();
}
else if( glm::length(direction) > 0.001f ) {
character->rotation = cameraRotation * glm::quat(glm::vec3(0.f, 0.f, -atan2(direction.x, direction.y)));
else if( rawMovement.y > 0.01f ) {
rotationOffset = -glm::half_pi<float>();
}
}
else if( rawMovement.x < -0.01f ) {
rotationOffset = glm::pi<float>();
}
character->rotation = glm::quat(glm::vec3(0.f, 0.f, -atan2(direction.x, direction.y) + rotationOffset));
}
CharacterController::update(dt);

View File

@ -13,7 +13,7 @@ CharacterObject::CharacterObject(GameWorld* engine, const glm::vec3& pos, const
currentVehicle(nullptr), currentSeat(0),
_hasTargetPosition(false), _activeInventoryItem(0),
ped(data), physCharacter(nullptr),
controller(nullptr), currentActivity(None)
controller(nullptr)
{
mHealth = 100.f;
@ -23,6 +23,14 @@ CharacterObject::CharacterObject(GameWorld* engine, const glm::vec3& pos, const
animations.walk_start = engine->gameData.animations["walk_start"];
animations.run = engine->gameData.animations["run_player"];
animations.walk_right = engine->gameData.animations["walk_player_right"];
animations.walk_right_start = engine->gameData.animations["walk_start_right"];
animations.walk_left = engine->gameData.animations["walk_player_left"];
animations.walk_left_start = engine->gameData.animations["walk_start_left"];
animations.walk_back = engine->gameData.animations["walk_player_back"];
animations.walk_back_start = engine->gameData.animations["walk_start_back"];
animations.jump_start = engine->gameData.animations["jump_launch"];
animations.jump_glide = engine->gameData.animations["jump_glide"];
animations.jump_land = engine->gameData.animations["jump_land"];
@ -39,7 +47,6 @@ CharacterObject::CharacterObject(GameWorld* engine, const glm::vec3& pos, const
animator->setModel(model->model);
createActor();
enterAction(Idle);
}
}
@ -51,13 +58,6 @@ CharacterObject::~CharacterObject()
destroyActor();
}
void CharacterObject::enterAction(CharacterObject::Action act)
{
if(currentActivity != act) {
currentActivity = act;
}
}
void CharacterObject::createActor(const glm::vec3& size)
{
if(physCharacter) {
@ -105,76 +105,6 @@ void CharacterObject::tick(float dt)
controller->update(dt);
}
if(currentVehicle
&& currentActivity != VehicleGetIn
&& currentActivity != VehicleGetOut) {
enterAction(VehicleSit);
}
switch(currentActivity) {
case Idle: {
if(animator->getAnimation() != animations.idle) {
animator->setAnimation(animations.idle);
}
} break;
case Walk: {
if(animator->getAnimation() != animations.walk) {
if(animator->getAnimation() != animations.walk_start) {
animator->setAnimation(animations.walk_start, false);
}
else if(animator->isCompleted()) {
animator->setAnimation(animations.walk);
}
}
} break;
case Run: {
if(animator->getAnimation() != animations.run) {
animator->setAnimation(animations.run);
}
} break;
case Jump: {
if(animator->getAnimation() != animations.jump_start) {
if(animator->getAnimation() != animations.jump_glide) {
animator->setAnimation(animations.jump_start, false);
}
else if(animator->isCompleted()) {
animator->setAnimation(animations.jump_glide);
}
}
} break;
case VehicleSit: {
if(animator->getAnimation() != animations.car_sit) {
animator->setAnimation(animations.car_sit);
}
} break;
case VehicleOpen: {
if(animator->getAnimation() != animations.car_open_lhs) {
animator->setAnimation(animations.car_open_lhs, false);
}
else if(animator->isCompleted()) {
enterAction(VehicleGetIn);
}
} break;
case VehicleGetIn: {
if(animator->getAnimation() != animations.car_getin_lhs) {
animator->setAnimation(animations.car_getin_lhs, false);
}
else if( animator->isCompleted() ) {
enterAction(VehicleSit);
}
} break;
case VehicleGetOut: {
if(animator->getAnimation() != animations.car_getout_lhs) {
animator->setAnimation(animations.car_getout_lhs, false);
}
else if( animator->isCompleted() ) {
enterAction(Idle);
}
} break;
default: break;
};
animator->tick(dt);
updateCharacter(dt);
@ -237,7 +167,7 @@ void CharacterObject::updateCharacter(float dt)
if(object->type() == Vehicle) {
VehicleObject* vehicle = static_cast<VehicleObject*>(object);
if(vehicle->physBody->getLinearVelocity().length() > 0.1f) {
enterAction(KnockedDown);
/// @todo play knocked down animation.
}
}
}
@ -246,53 +176,43 @@ void CharacterObject::updateCharacter(float dt)
}
}
if(currentActivity == CharacterObject::Jump)
{
if(physCharacter->onGround() || isInWater())
{
enterAction(CharacterObject::Idle);
}
glm::vec3 walkDir;
glm::vec3 animTranslate;
if( isAnimationFixed() ) {
auto d = animator->getDurationTransform() / animator->getAnimation()->duration;
animTranslate = d * dt;
}
else
{
glm::vec3 walkDir;
glm::vec3 animTranslate;
if( isAnimationFixed() ) {
auto d = animator->getDurationTransform() / animator->getAnimation()->duration;
animTranslate = d * dt;
}
position = getPosition();
position = getPosition();
if( _hasTargetPosition && false ) {
auto beforedelta = _targetPosition - position;
if( _hasTargetPosition ) {
auto beforedelta = _targetPosition - position;
glm::quat faceDir( glm::vec3( 0.f, 0.f, atan2(beforedelta.y, beforedelta.x) - glm::half_pi<float>() ) );
glm::vec3 direction = faceDir * animTranslate;
glm::quat faceDir( glm::vec3( 0.f, 0.f, atan2(beforedelta.y, beforedelta.x) - glm::half_pi<float>() ) );
glm::vec3 direction = faceDir * animTranslate;
auto positiondelta = _targetPosition - (position + direction);
if( glm::length(beforedelta) < glm::length(positiondelta) ) {
// Warp the character to the target position if we are about to overstep.
physObject->getWorldTransform().setOrigin(btVector3(
_targetPosition.x,
_targetPosition.y,
_targetPosition.z));
_hasTargetPosition = false;
}
else {
walkDir = direction;
}
auto positiondelta = _targetPosition - (position + direction);
if( glm::length(beforedelta) < glm::length(positiondelta) ) {
// Warp the character to the target position if we are about to overstep.
physObject->getWorldTransform().setOrigin(btVector3(
_targetPosition.x,
_targetPosition.y,
_targetPosition.z));
_hasTargetPosition = false;
}
else {
walkDir = rotation * animTranslate;
walkDir = direction;
}
physCharacter->setWalkDirection(btVector3(walkDir.x, walkDir.y, walkDir.z));
auto Pos = physCharacter->getGhostObject()->getWorldTransform().getOrigin();
position = glm::vec3(Pos.x(), Pos.y(), Pos.z());
}
else {
walkDir = rotation * animTranslate;
}
physCharacter->setWalkDirection(btVector3(walkDir.x, walkDir.y, walkDir.z));
auto Pos = physCharacter->getGhostObject()->getWorldTransform().getOrigin();
position = glm::vec3(Pos.x(), Pos.y(), Pos.z());
// Handle above waist height water.
auto wi = engine->gameData.getWaterIndexAt(getPosition());
@ -337,9 +257,11 @@ glm::vec3 CharacterObject::getPosition() const
return glm::vec3(Pos.x(), Pos.y(), Pos.z());
}
if(currentVehicle) {
if( currentActivity == VehicleGetOut ) {
/// @todo this is hacky.
if( animator->getAnimation() == animations.car_getout_lhs ) {
return currentVehicle->getSeatEntryPosition(currentSeat);
}
auto v = getCurrentVehicle();
auto R = glm::mat3_cast(v->getRotation());
glm::vec3 offset;
@ -427,7 +349,6 @@ void CharacterObject::jump()
{
if( physCharacter ) {
physCharacter->jump();
enterAction(CharacterObject::Jump);
}
}
@ -472,9 +393,15 @@ void CharacterObject::clearTargetPosition()
_hasTargetPosition = false;
}
void CharacterObject::playAnimation(Animation *animation, bool repeat, bool fixed)
{
_fixedAnimation = fixed;
animator->setAnimation(animation, repeat);
}
bool CharacterObject::isAnimationFixed() const
{
return currentActivity == Walk || currentActivity == Run;
return _fixedAnimation;
}
void CharacterObject::addToInventory(InventoryItem *item)

View File

@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(test_activities)
}
// This check will undoubtably break in the future, please improve.
BOOST_CHECK_CLOSE( glm::distance(character->getPosition(), {10.f, 10.f, 0.f}), 0.2f, 100.0f );
BOOST_CHECK_CLOSE( glm::distance(character->getPosition(), {10.f, 10.f, 0.f}), 1.0f, 100.0f );
Global::get().e->destroyObject(character);
delete controller;