mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 03:12:36 +01:00
Replace Activity switch with objects
This commit is contained in:
parent
532738077e
commit
d7b738cdc1
@ -3,8 +3,11 @@
|
||||
#define _GTAAICONTROLLER_HPP_
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <string>
|
||||
|
||||
struct GTACharacter;
|
||||
struct GTAVehicle;
|
||||
|
||||
/**
|
||||
* @class GTAAIController
|
||||
* Character Controller Interface, translates high-level behaviours into low level actions.
|
||||
@ -13,18 +16,16 @@ class GTAAIController
|
||||
{
|
||||
public:
|
||||
|
||||
enum Activity {
|
||||
Idle,
|
||||
GoTo,
|
||||
};
|
||||
/**
|
||||
* @brief The Activity struct interface
|
||||
*/
|
||||
struct Activity {
|
||||
|
||||
struct ActivityParameter {
|
||||
glm::vec3 position;
|
||||
virtual ~Activity() {}
|
||||
|
||||
ActivityParameter( ) {}
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
ActivityParameter( const glm::vec3& position ) :
|
||||
position( position ) {}
|
||||
virtual bool update(GTACharacter* character, GTAAIController* controller) = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -34,31 +35,26 @@ protected:
|
||||
*/
|
||||
GTACharacter* character;
|
||||
|
||||
Activity _currentActivity;
|
||||
Activity _nextActivity;
|
||||
|
||||
ActivityParameter _currentParameter;
|
||||
ActivityParameter _nextParameter;
|
||||
Activity* _currentActivity;
|
||||
Activity* _nextActivity;
|
||||
|
||||
bool updateActivity();
|
||||
void setActivity(Activity activity, const ActivityParameter& _parameter );
|
||||
void setActivity(Activity* activity);
|
||||
|
||||
public:
|
||||
|
||||
GTAAIController(GTACharacter* character);
|
||||
|
||||
Activity getCurrentActivity() const { return _currentActivity; }
|
||||
const ActivityParameter& getCurrentActivityParameter() const { return _currentParameter; }
|
||||
Activity* getCurrentActivity() { return _currentActivity; }
|
||||
|
||||
Activity getNextActivity() const { return _nextActivity; }
|
||||
const ActivityParameter& getNextActivityParameter() const { return _nextParameter; }
|
||||
Activity* getNextActivity() { return _nextActivity; }
|
||||
|
||||
/**
|
||||
* @brief setNextActivity Sets the next Activity with a parameter.
|
||||
* @param activity
|
||||
* @param position
|
||||
*/
|
||||
void setNextActivity( Activity activity, const ActivityParameter& parameter );
|
||||
void setNextActivity( Activity* activity );
|
||||
|
||||
/**
|
||||
* @brief update Updates the controller.
|
||||
@ -69,4 +65,34 @@ public:
|
||||
virtual glm::vec3 getTargetPosition() = 0;
|
||||
};
|
||||
|
||||
#define DECL_ACTIVITY( activity_name ) \
|
||||
std::string name() const { return #activity_name; }
|
||||
|
||||
// TODO: Refactor this with an ugly macro to reduce code dup.
|
||||
|
||||
namespace Activities {
|
||||
struct GoTo : public GTAAIController::Activity {
|
||||
DECL_ACTIVITY( GoTo )
|
||||
|
||||
glm::vec3 target;
|
||||
|
||||
GoTo( const glm::vec3& target )
|
||||
: target( target ) {}
|
||||
|
||||
bool update(GTACharacter* character, GTAAIController* controller);
|
||||
};
|
||||
|
||||
struct EnterVehicle : public GTAAIController::Activity {
|
||||
DECL_ACTIVITY( EnterVehicle )
|
||||
|
||||
GTAVehicle* vehicle;
|
||||
unsigned int seat;
|
||||
|
||||
EnterVehicle( GTAVehicle* vehicle, unsigned int seat = 0 )
|
||||
: vehicle( vehicle ), seat( seat ) {}
|
||||
|
||||
bool update(GTACharacter *character, GTAAIController *controller);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -91,6 +91,10 @@ public:
|
||||
GameObject* getOccupant(size_t seat);
|
||||
|
||||
void setOccupant(size_t seat, GameObject* occupant);
|
||||
|
||||
glm::vec3 getSeatEntryPosition(size_t seat) const {
|
||||
return getPosition() + getRotation() * info->seats[seat].offset * glm::vec3(2.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
virtual bool takeDamage(const DamageInfo& damage);
|
||||
|
||||
|
@ -3,57 +3,93 @@
|
||||
|
||||
bool GTAAIController::updateActivity()
|
||||
{
|
||||
switch( _currentActivity ) {
|
||||
default: break;
|
||||
|
||||
case GoTo: {
|
||||
/* TODO: Use the ai nodes to navigate to the position */
|
||||
glm::vec3 targetDirection = _currentParameter.position - character->getPosition();
|
||||
|
||||
if( glm::length(targetDirection) < 0.01f ) {
|
||||
character->enterAction(GTACharacter::Idle);
|
||||
return true;
|
||||
}
|
||||
|
||||
character->setTargetPosition( _currentParameter.position );
|
||||
|
||||
glm::quat r( glm::vec3{ 0.f, 0.f, atan2(targetDirection.y, targetDirection.x) - glm::half_pi<float>() } );
|
||||
character->rotation = r;
|
||||
character->enterAction(GTACharacter::Walk);
|
||||
} break;
|
||||
|
||||
if( _currentActivity ) {
|
||||
return _currentActivity->update(character, this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GTAAIController::setActivity(GTAAIController::Activity activity, const ActivityParameter ¶meter)
|
||||
void GTAAIController::setActivity(GTAAIController::Activity* activity)
|
||||
{
|
||||
_currentActivity = activity;
|
||||
_currentParameter = parameter;
|
||||
if( _currentActivity == nullptr ) {
|
||||
character->enterAction( GTACharacter::Idle );
|
||||
}
|
||||
}
|
||||
|
||||
GTAAIController::GTAAIController(GTACharacter* character)
|
||||
: character(character), _currentActivity(Idle), _nextActivity(Idle)
|
||||
: character(character), _currentActivity(nullptr), _nextActivity(nullptr)
|
||||
{
|
||||
character->controller = this;
|
||||
}
|
||||
|
||||
void GTAAIController::setNextActivity(GTAAIController::Activity activity, const ActivityParameter ¶meter)
|
||||
void GTAAIController::setNextActivity(GTAAIController::Activity* activity)
|
||||
{
|
||||
if( _currentActivity == Idle ) {
|
||||
setActivity(activity, parameter);
|
||||
_nextActivity = Idle;
|
||||
if( _currentActivity == nullptr ) {
|
||||
setActivity(activity);
|
||||
_nextActivity = nullptr;
|
||||
}
|
||||
else {
|
||||
_nextParameter = parameter;
|
||||
if(_nextActivity) delete _nextActivity;
|
||||
_nextActivity = activity;
|
||||
}
|
||||
}
|
||||
|
||||
void GTAAIController::update(float dt)
|
||||
{
|
||||
if(updateActivity()) {
|
||||
setActivity(Idle, {});
|
||||
if( updateActivity() ) {
|
||||
if( _currentActivity ) {
|
||||
delete _currentActivity;
|
||||
_currentActivity = nullptr;
|
||||
}
|
||||
if( _nextActivity ) {
|
||||
setActivity( _nextActivity );
|
||||
_nextActivity = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Activities::GoTo::update(GTACharacter *character, GTAAIController *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(GTACharacter::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(GTACharacter::Walk);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#include <objects/GTAVehicle.hpp>
|
||||
|
||||
bool Activities::EnterVehicle::update(GTACharacter *character, GTAAIController *controller)
|
||||
{
|
||||
glm::vec3 target = vehicle->getSeatEntryPosition(seat);
|
||||
glm::vec3 targetDirection = target - character->getPosition();
|
||||
|
||||
if( glm::length(targetDirection) < 0.01f ) {
|
||||
// TODO: play enter vehicle animation instead of teleporting.
|
||||
// The correct Action is handled by the character
|
||||
character->enterVehicle(vehicle, seat);
|
||||
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(GTACharacter::Walk);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <objects/GTACharacter.hpp>
|
||||
#include <objects/GTAVehicle.hpp>
|
||||
#include <ai/GTADefaultAIController.hpp>
|
||||
#include "test_globals.hpp"
|
||||
|
||||
@ -14,22 +15,19 @@ BOOST_AUTO_TEST_CASE(test_create)
|
||||
|
||||
auto controller = new GTADefaultAIController(character);
|
||||
|
||||
|
||||
// Check the initial activity is Idle.
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivity(), GTAAIController::Idle );
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivity(), nullptr );
|
||||
|
||||
// Check that Idle activities are instantly displaced.
|
||||
controller->setNextActivity(GTAAIController::GoTo, glm::vec3{ 1000.f, 0.f, 0.f });
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivity(), GTAAIController::GoTo );
|
||||
BOOST_CHECK_EQUAL( controller->getNextActivity(), GTAAIController::Idle );
|
||||
controller->setNextActivity( new Activities::GoTo( glm::vec3{ 1000.f, 0.f, 0.f } ) );
|
||||
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivity()->name(), "GoTo" );
|
||||
BOOST_CHECK_EQUAL( controller->getNextActivity(), nullptr );
|
||||
|
||||
Global::get().e->destroyObject(character);
|
||||
delete controller;
|
||||
}
|
||||
{
|
||||
GTAAIController::ActivityParameter ap { glm::vec3 { 1.f, 2.f, 3.f } };
|
||||
|
||||
BOOST_CHECK_EQUAL( ap.position, glm::vec3( 1.f, 2.f, 3.f ) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_activities)
|
||||
@ -41,10 +39,9 @@ BOOST_AUTO_TEST_CASE(test_activities)
|
||||
|
||||
auto controller = new GTADefaultAIController(character);
|
||||
|
||||
controller->setNextActivity( GTAAIController::GoTo, { glm::vec3{ 10.f, 0.f, 0.f } } );
|
||||
controller->setNextActivity( new Activities::GoTo( glm::vec3{ 10.f, 0.f, 0.f } ) );
|
||||
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivity(), GTAAIController::GoTo );
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivityParameter().position, glm::vec3( 10.f, 0.f, 0.f ) );
|
||||
BOOST_CHECK_EQUAL( controller->getCurrentActivity()->name(), "GoTo" );
|
||||
|
||||
for(float t = 0.f; t < 11.f; t+=(1.f/60.f)) {
|
||||
controller->update(1.f/60.f);
|
||||
@ -58,6 +55,34 @@ BOOST_AUTO_TEST_CASE(test_activities)
|
||||
Global::get().e->destroyObject(character);
|
||||
delete controller;
|
||||
}
|
||||
{
|
||||
GTAVehicle* vehicle = Global::get().e->createVehicle(90u, glm::vec3(10.f, 0.f, 0.f), glm::quat());
|
||||
BOOST_REQUIRE(vehicle != nullptr);
|
||||
|
||||
auto character = Global::get().e->createPedestrian(1, {0.f, 0.f, 0.f});
|
||||
|
||||
BOOST_REQUIRE( character != nullptr );
|
||||
|
||||
auto controller = new GTADefaultAIController(character);
|
||||
|
||||
controller->setNextActivity( new Activities::EnterVehicle( vehicle, 0 ) );
|
||||
|
||||
for(float t = 0.f; t < 2.f; t+=(1.f/60.f)) {
|
||||
controller->update(1.f/60.f);
|
||||
character->tick(1.f/60.f);
|
||||
Global::get().e->dynamicsWorld->stepSimulation(1.f/60.f);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( nullptr, character->getCurrentVehicle() );
|
||||
|
||||
for(float t = 0.f; t < 10.f; t+=(1.f/60.f)) {
|
||||
controller->update(1.f/60.f);
|
||||
character->tick(1.f/60.f);
|
||||
Global::get().e->dynamicsWorld->stepSimulation(1.f/60.f);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( vehicle, character->getCurrentVehicle() );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -19,6 +19,16 @@ struct print_log_value<glm::vec3> {
|
||||
};
|
||||
}}
|
||||
|
||||
namespace boost { namespace test_tools {
|
||||
template<>
|
||||
struct print_log_value<nullptr_t> {
|
||||
void operator()( std::ostream& s , nullptr_t )
|
||||
{
|
||||
s << "nullptr";
|
||||
}
|
||||
};
|
||||
}}
|
||||
|
||||
class Global
|
||||
{
|
||||
public:
|
||||
|
@ -20,6 +20,8 @@ BOOST_AUTO_TEST_CASE(test_create_vehicle)
|
||||
BOOST_CHECK_EQUAL(vehicle->info->wheels.size(), 4);
|
||||
|
||||
BOOST_CHECK_EQUAL(vehicle->info->seats.size(), 4);
|
||||
|
||||
Global::get().e->destroyObject(vehicle);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(vehicle_frame_flags)
|
||||
@ -47,8 +49,22 @@ BOOST_AUTO_TEST_CASE(vehicle_frame_flags)
|
||||
BOOST_CHECK(vehicle->isFrameVisible(bonnet_ok));
|
||||
BOOST_CHECK(!vehicle->isFrameVisible(bonnet_dam));
|
||||
|
||||
|
||||
Global::get().e->destroyObject(vehicle);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_door_position)
|
||||
{
|
||||
GTAVehicle* vehicle = Global::get().e->createVehicle(90u, glm::vec3(10.f, 0.f, 0.f), glm::quat());
|
||||
|
||||
BOOST_REQUIRE(vehicle != nullptr);
|
||||
|
||||
BOOST_REQUIRE(vehicle->info != nullptr);
|
||||
BOOST_REQUIRE(vehicle->vehicle != nullptr);
|
||||
|
||||
BOOST_CHECK( vehicle->getSeatEntryPosition(0).x > 5.f );
|
||||
|
||||
|
||||
Global::get().e->destroyObject(vehicle);
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user