1
0
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:
Daniel Evans 2014-05-29 11:12:40 +01:00
parent 532738077e
commit d7b738cdc1
6 changed files with 179 additions and 62 deletions

View File

@ -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

View File

@ -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);

View File

@ -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 &parameter)
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 &parameter)
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;
}

View File

@ -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()

View File

@ -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:

View File

@ -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()