diff --git a/rwengine/include/ai/CharacterController.hpp b/rwengine/include/ai/CharacterController.hpp index acccbe28..be78f331 100644 --- a/rwengine/include/ai/CharacterController.hpp +++ b/rwengine/include/ai/CharacterController.hpp @@ -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 ) \ diff --git a/rwengine/include/ai/PlayerController.hpp b/rwengine/include/ai/PlayerController.hpp index 455b0aea..def3c433 100644 --- a/rwengine/include/ai/PlayerController.hpp +++ b/rwengine/include/ai/PlayerController.hpp @@ -8,19 +8,14 @@ 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. diff --git a/rwengine/include/objects/CharacterObject.hpp b/rwengine/include/objects/CharacterObject.hpp index ae4eac52..cca1dd06 100644 --- a/rwengine/include/objects/CharacterObject.hpp +++ b/rwengine/include/objects/CharacterObject.hpp @@ -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 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 ); diff --git a/rwengine/src/ai/CharacterController.cpp b/rwengine/src/ai/CharacterController.cpp index 713e3050..883418df 100644 --- a/rwengine/src/ai/CharacterController.cpp +++ b/rwengine/src/ai/CharacterController.cpp @@ -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() } ); 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() } ); 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; } } diff --git a/rwengine/src/ai/PlayerController.cpp b/rwengine/src/ai/PlayerController.cpp index a89d0b07..de923352 100644 --- a/rwengine/src/ai/PlayerController.cpp +++ b/rwengine/src/ai/PlayerController.cpp @@ -3,19 +3,14 @@ #include #include #include +#include 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(); } - 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(); } } + else if( rawMovement.x < -0.01f ) { + rotationOffset = glm::pi(); + } + + character->rotation = glm::quat(glm::vec3(0.f, 0.f, -atan2(direction.x, direction.y) + rotationOffset)); } CharacterController::update(dt); diff --git a/rwengine/src/objects/CharacterObject.cpp b/rwengine/src/objects/CharacterObject.cpp index 285031db..6b9b7832 100644 --- a/rwengine/src/objects/CharacterObject.cpp +++ b/rwengine/src/objects/CharacterObject.cpp @@ -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(object); if(vehicle->physBody->getLinearVelocity().length() > 0.1f) { - enterAction(KnockedDown); + /// @todo play knocked down animation. } } } @@ -245,54 +175,44 @@ 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() ) ); + glm::vec3 direction = faceDir * animTranslate; - glm::quat faceDir( glm::vec3( 0.f, 0.f, atan2(beforedelta.y, beforedelta.x) - glm::half_pi() ) ); - 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) diff --git a/tests/test_character.cpp b/tests/test_character.cpp index 9b501efe..2423a49f 100644 --- a/tests/test_character.cpp +++ b/tests/test_character.cpp @@ -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;