Skidding AI now estimates the turn radius of a curve, and is
able to brake if it should be too fast. Avoid using nitro when braking (otherwise AI might be pushed out of curve). Added new --ai-debug command line option that will display the name of the AI on top of each kart (when compiled with debug). See world.cpp lines 249 on how to select the new (or any other AI, including the option to use all AIs, which is nice to compare them). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11368 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
c311cf937c
commit
b3c0eeab80
@ -425,7 +425,8 @@ void Camera::update(float dt)
|
||||
{
|
||||
core::vector3df xyz = m_kart->getXYZ().toIrrVector();
|
||||
m_camera->setTarget(xyz);
|
||||
xyz.Y = xyz.Y+15;
|
||||
xyz.Y = xyz.Y+55;
|
||||
xyz.Z -= 5.0f;
|
||||
m_camera->setPosition(xyz);
|
||||
// To view inside tunnels (FIXME 27>15 why??? makes no sense
|
||||
// - the kart should not be visible, but it works)
|
||||
|
@ -99,6 +99,7 @@ void ShowCurve::addPoint(const Vec3 &pnt)
|
||||
if(n==0)
|
||||
{
|
||||
m_buffer->append(vertices, 4, NULL, 0);
|
||||
m_mesh->setDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -131,6 +132,7 @@ void ShowCurve::addPoint(const Vec3 &pnt)
|
||||
}
|
||||
m_buffer->recalculateBoundingBox();
|
||||
m_mesh->setBoundingBox(m_buffer->getBoundingBox());
|
||||
m_mesh->setDirty();
|
||||
} // addPoint
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -140,8 +142,27 @@ void ShowCurve::clear()
|
||||
m_mesh->drop();
|
||||
addEmptyMesh();
|
||||
m_scene_node->setMesh(m_mesh);
|
||||
m_mesh->setDirty();
|
||||
} // clear
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Makes this curve to show a circle with given center point and radius.
|
||||
* \param center Center point of the circle.
|
||||
* \param radius Radius of the circle.
|
||||
*/
|
||||
void ShowCurve::makeCircle(const Vec3 ¢er, float radius)
|
||||
{
|
||||
clear();
|
||||
|
||||
const float num_segs = 40.0f;
|
||||
float dx = 2.0f*M_PI/num_segs;
|
||||
for(float x = 0; x <=2.0f*M_PI; x+=dx)
|
||||
{
|
||||
Vec3 xyz(radius*sin(x), 0.2f, radius*cos(x));
|
||||
addPoint(xyz+center);
|
||||
}
|
||||
} // makeCircle
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Sets the heading for the curve.
|
||||
* \param heading The heading (in rad).
|
||||
|
@ -68,6 +68,7 @@ public:
|
||||
const irr::video::SColor &color = video::SColor(77, 0, 179, 0));
|
||||
~ShowCurve();
|
||||
void addPoint(const Vec3 &pnt);
|
||||
void makeCircle(const Vec3 ¢er, float radius);
|
||||
void update(float dt);
|
||||
void setVisible(bool v);
|
||||
bool isVisible() const;
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
|
||||
bool AIBaseController::m_ai_debug = false;
|
||||
|
||||
AIBaseController::AIBaseController(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player)
|
||||
: Controller(kart, player)
|
||||
@ -52,7 +54,9 @@ AIBaseController::AIBaseController(AbstractKart *kart,
|
||||
m_all_look_aheads.clear();
|
||||
m_successor_index.clear();
|
||||
} // if battle mode
|
||||
setControllerName("AIBaseController");
|
||||
// Don't call our own setControllerName, since this will add a
|
||||
// billboard showing 'aibasecontroller' to the kar.
|
||||
Controller::setControllerName("AIBaseController");
|
||||
} // AIBaseController
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -62,6 +66,21 @@ void AIBaseController::reset()
|
||||
m_collision_times.clear();
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** In debug mode when the user specified --ai-debug on the command line set
|
||||
* the name of the controller as on-screen text, so that the different AI
|
||||
* controllers can be distinguished.
|
||||
* \param name Name of the controller.
|
||||
*/
|
||||
void AIBaseController::setControllerName(const std::string &name)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(m_ai_debug)
|
||||
m_kart->setOnScreenText(core::stringw(name.c_str()).c_str());
|
||||
#endif
|
||||
Controller::setControllerName(name);
|
||||
} // setControllerName
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Triggers a recomputation of the path to use, so that the AI does not
|
||||
* always use the same way.
|
||||
|
@ -82,6 +82,7 @@ protected:
|
||||
virtual void update (float delta) ;
|
||||
virtual unsigned int getNextSector(unsigned int index);
|
||||
virtual void newLap (int lap);
|
||||
virtual void setControllerName(const std::string &name);
|
||||
float steerToAngle (const unsigned int sector, const float angle);
|
||||
float steerToPoint (const Vec3 &point);
|
||||
float normalizeAngle(float angle);
|
||||
@ -93,11 +94,13 @@ protected:
|
||||
* hitting part of the track). */
|
||||
bool isStuck() const { return m_stuck_trigger_rescue; }
|
||||
|
||||
static bool m_ai_debug;
|
||||
public:
|
||||
AIBaseController(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player=NULL);
|
||||
virtual ~AIBaseController() {};
|
||||
virtual void reset();
|
||||
static void enableDebug() {m_ai_debug = true; }
|
||||
virtual void crashed(const AbstractKart *k) {};
|
||||
virtual void crashed(const Material *m);
|
||||
virtual void handleZipper(bool play_sound) {};
|
||||
|
@ -74,7 +74,8 @@ public:
|
||||
virtual bool isNetworkController() const = 0;
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Sets the controller name for this controller. */
|
||||
void setControllerName(const std::string &name) {m_controller_name = name; }
|
||||
virtual void setControllerName(const std::string &name)
|
||||
{ m_controller_name = name; }
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Returns the name of this controller. */
|
||||
const std::string &getControllerName() const { return m_controller_name; }
|
||||
|
@ -38,6 +38,7 @@
|
||||
#ifdef AI_DEBUG
|
||||
# include "graphics/irr_driver.hpp"
|
||||
#endif
|
||||
#include "graphics/show_curve.hpp"
|
||||
#include "graphics/slip_stream.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/controller/kart_control.hpp"
|
||||
@ -106,7 +107,20 @@ SkiddingAI::SkiddingAI(AbstractKart *kart)
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere = irr_driver->getSceneManager()->addSphereSceneNode(1);
|
||||
#define CURVE_PREDICT1 0
|
||||
#define CURVE_PREDICT2 1
|
||||
#define CURVE_QG 2
|
||||
#define NUM_CURVES (CURVE_QG+1)
|
||||
|
||||
m_curve = new ShowCurve*[NUM_CURVES];
|
||||
m_curve[CURVE_PREDICT1] = new ShowCurve(0.05f, 0.5f,
|
||||
irr::video::SColor(128, 0, 0, 128));
|
||||
m_curve[CURVE_PREDICT2] = new ShowCurve(0.05f, 0.5f,
|
||||
irr::video::SColor(128, 0, 0, 64));
|
||||
m_curve[CURVE_QG] = new ShowCurve(0.5f, 0.5f,
|
||||
irr::video::SColor(128, 0, 128, 0));
|
||||
#endif
|
||||
setControllerName("Skidding");
|
||||
} // SkiddingAI
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -117,6 +131,11 @@ SkiddingAI::~SkiddingAI()
|
||||
{
|
||||
#ifdef AI_DEBUG
|
||||
irr_driver->removeNode(m_debug_sphere);
|
||||
for(unsigned int i=0; i<NUM_CURVES; i++)
|
||||
{
|
||||
delete m_curve[i];
|
||||
}
|
||||
delete m_curve;
|
||||
#endif
|
||||
} // ~SkiddingAI
|
||||
|
||||
@ -133,6 +152,8 @@ void SkiddingAI::reset()
|
||||
m_distance_ahead = 0.0f;
|
||||
m_kart_behind = NULL;
|
||||
m_distance_behind = 0.0f;
|
||||
m_current_curve_radius = 0.0f;
|
||||
m_current_track_direction = GraphNode::DIR_STRAIGHT;
|
||||
|
||||
AIBaseController::reset();
|
||||
m_track_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
@ -216,7 +237,7 @@ void SkiddingAI::update(float dt)
|
||||
|
||||
//Detect if we are going to crash with the track and/or kart
|
||||
checkCrashes(m_kart->getXYZ());
|
||||
|
||||
determineTrackDirection();
|
||||
findCurve();
|
||||
|
||||
// Special behaviour if we have a bomb attach: try to hit the kart ahead
|
||||
@ -285,6 +306,7 @@ void SkiddingAI::update(float dt)
|
||||
//-----------------------------------------------------------------------------
|
||||
void SkiddingAI::handleBraking()
|
||||
{
|
||||
m_controls->m_brake = false;
|
||||
// In follow the leader mode, the kart should brake if they are ahead of
|
||||
// the leader (and not the leader, i.e. don't have initial position 1)
|
||||
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
|
||||
@ -295,6 +317,27 @@ void SkiddingAI::handleBraking()
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_current_track_direction!=GraphNode::DIR_STRAIGHT)
|
||||
{
|
||||
float max_turn_speed =
|
||||
m_kart->getKartProperties()
|
||||
->getSpeedForTurnRadius(m_current_curve_radius);
|
||||
|
||||
if(m_kart->getSpeed() > 1.5f*max_turn_speed &&
|
||||
m_kart->getSpeed()>7.0f &&
|
||||
fabsf(m_controls->m_steer) > 0.95f )
|
||||
{
|
||||
m_controls->m_brake = true;
|
||||
#ifdef AI_DEBUG
|
||||
std::cout << "BRAKING" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
|
||||
const float MIN_SPEED = 5.0f;
|
||||
//We may brake if we are about to get out of the road, but only if the
|
||||
//kart is on top of the road, and if we won't slow down below a certain
|
||||
@ -754,6 +797,9 @@ void SkiddingAI::handleNitroAndZipper()
|
||||
// Don't use nitro when the AI has a plunger in the face!
|
||||
if(m_kart->hasViewBlockedByPlunger()) return;
|
||||
|
||||
// Don't use nitro if we are braking
|
||||
if(m_controls->m_brake) return;
|
||||
|
||||
// Don't use nitro if the kart doesn't have any or is not on ground.
|
||||
if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return;
|
||||
|
||||
@ -1013,3 +1059,125 @@ void SkiddingAI::findCurve()
|
||||
|
||||
m_curve_target_speed = m_kart->getCurrentMaxSpeed();
|
||||
} // findCurve
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Determines the direction of the track ahead of the kart: 0 indicates
|
||||
* straight, +1 right turn, -1 left turn.
|
||||
*/
|
||||
int SkiddingAI::determineTrackDirection()
|
||||
{
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
unsigned int succ = m_successor_index[m_track_node];
|
||||
unsigned int next = qg->getNode(m_track_node).getSuccessor(succ);
|
||||
|
||||
unsigned int last;
|
||||
//qg->getNode(m_track_node).getDirectionData(succ, &dir, &last);
|
||||
qg->getNode(next).getDirectionData(m_successor_index[next],
|
||||
&m_current_track_direction, &last);
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
m_curve[CURVE_QG]->clear();
|
||||
for(unsigned int i=m_track_node; i<=last; i++)
|
||||
{
|
||||
m_curve[CURVE_QG]->addPoint(qg->getNode(i).getCenter());
|
||||
}
|
||||
#endif
|
||||
|
||||
if(m_current_track_direction!=GraphNode::DIR_STRAIGHT)
|
||||
{
|
||||
Vec3 center;
|
||||
Vec3 xyz = m_kart->getXYZ();
|
||||
Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz;
|
||||
Vec3 last_xyz = qg->getNode(last).getCenter();
|
||||
// Ideally we would like to have a circle that:
|
||||
// 1) goes through the kart position
|
||||
// 2) has the current heading of the kart as tangent in that point
|
||||
// 3) goes through the last point
|
||||
// 4) has a tangent at the last point that faces towards the next node
|
||||
// Unfortunately conditions 1 to 3 already fully determine the circle,
|
||||
// i.e. it is not always possible to find an appropriate circle.
|
||||
// Using the first three conditions is mostly a good choice (since the
|
||||
// kart will already point towards the direction of the circle), but
|
||||
// it can be incorrect in certain circumstances, e.g. consider the
|
||||
// case that the kart is on the 'right' circle for curve ahead, but
|
||||
// already facing towards the center of the circle (i.e. it is nearly
|
||||
// 90 degrees rotated away from the driving direction). In this case
|
||||
// a way too large circle will be computed (going through the end
|
||||
// point, but again neing 90 degrees wrong - 'across' the track and
|
||||
// not along). To handle this case we determine two circles:
|
||||
// one using conditions 1,2,3; and a second one using 1,3,4
|
||||
// The latter will be more appropriate for the case desrcribed above,
|
||||
// i.e. making sure that the kart will face the right direction at
|
||||
// the end of the curve.
|
||||
// The turn radius is then the minimum of the two radii.
|
||||
float radius1;
|
||||
determineTurnRadius(xyz, tangent, last_xyz,
|
||||
¢er, &radius1);
|
||||
#ifdef AI_DEBUG
|
||||
m_curve[CURVE_PREDICT1]->makeCircle(center, radius1);
|
||||
#endif
|
||||
int next_last = m_next_node_index[last];
|
||||
Vec3 tangent_end = qg->getNode(next_last).getCenter() - last_xyz;
|
||||
float radius2;
|
||||
determineTurnRadius(last_xyz, tangent_end, xyz,
|
||||
¢er, &radius2);
|
||||
#ifdef AI_DEBUG
|
||||
m_curve[CURVE_PREDICT2]->makeCircle(center, radius2);
|
||||
#endif
|
||||
m_current_curve_radius = std::min(radius1,radius2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // determineTrackDirection
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determine the center point and radius of a circle given two points on
|
||||
* the ccircle and the tangent at the first point. This is done as follows:
|
||||
* 1) Determine the line going through the center point start+end, which is
|
||||
* orthogonal to the vector from start to end. This line goes through the
|
||||
* center of the circle.
|
||||
* 2) Determine the line going through the first point and is orthogonal
|
||||
* to the given tangent.
|
||||
* 3) The intersection of these two lines is the center of the circle.
|
||||
* \param start First point.
|
||||
* \param tangent Tangent at first point.
|
||||
* \param end Second point on circle.
|
||||
* \return center Center point of the circle.
|
||||
* \return radius Radius of the circle.
|
||||
*/
|
||||
void SkiddingAI::determineTurnRadius(const Vec3 &start,
|
||||
const Vec3 &tangent,
|
||||
const Vec3 &end,
|
||||
Vec3 *center,
|
||||
float *radius)
|
||||
{
|
||||
// 1) Line through middle of start+end
|
||||
Vec3 mid = 0.5f*(start+end);
|
||||
Vec3 direction = end-start;
|
||||
|
||||
Vec3 orthogonal(direction.getZ(), 0, -direction.getX());
|
||||
Vec3 q1 = mid + orthogonal;
|
||||
irr::core::line2df line1(mid.getX(), mid.getZ(),
|
||||
q1.getX(), q1.getZ() );
|
||||
|
||||
Vec3 ortho_tangent(tangent.getZ(), 0, -tangent.getX());
|
||||
Vec3 q2 = start + ortho_tangent;
|
||||
irr::core::line2df line2(start.getX(), start.getZ(),
|
||||
q2.getX(), q2.getZ());
|
||||
|
||||
|
||||
irr::core::vector2df result;
|
||||
if(line1.intersectWith(line2, result, /*checkOnlySegments*/false))
|
||||
{
|
||||
*center = Vec3(result.X, start.getY(), result.Y);
|
||||
*radius = (start - *center).length();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No intersection. In this case assume that the two points are
|
||||
// on a semicircle, in which case the center is at 0.5*(start+end):
|
||||
*center = 0.5f*(start+end);
|
||||
*radius = 0.5f*(end-start).length();
|
||||
}
|
||||
return;
|
||||
} // determineTurnRadius
|
||||
|
@ -22,10 +22,12 @@
|
||||
#define HEADER_SKIDDING_AI__HPP
|
||||
|
||||
#include "karts/controller/ai_base_controller.hpp"
|
||||
#include "tracks/graph_node.hpp"
|
||||
|
||||
class Track;
|
||||
class LinearWorld;
|
||||
class QuadGraph;
|
||||
class ShowCurve;
|
||||
class Track;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -118,6 +120,17 @@ private:
|
||||
|
||||
int m_start_kart_crash_direction; //-1 = left, 1 = right, 0 = no crash.
|
||||
|
||||
/** The direction of the track where the kart is on atm. */
|
||||
GraphNode::DirectionType m_current_track_direction;
|
||||
|
||||
/** The radius of the curve the kart is currently driving. Undefined
|
||||
* when being on a straigt section. */
|
||||
float m_current_curve_radius;
|
||||
|
||||
#ifdef DEBUG
|
||||
/** For skidding debugging: shows the estimated turn shape. */
|
||||
ShowCurve **m_curve;
|
||||
#endif
|
||||
/** For debugging purpose: a sphere indicating where the AI
|
||||
* is targeting at. */
|
||||
irr::scene::ISceneNode *m_debug_sphere;
|
||||
@ -140,6 +153,12 @@ private:
|
||||
void findNonCrashingPoint(Vec3 *result);
|
||||
|
||||
void findCurve();
|
||||
int determineTrackDirection();
|
||||
void determineTurnRadius(const Vec3 &start,
|
||||
const Vec3 &start_direction,
|
||||
const Vec3 &end,
|
||||
Vec3 *center,
|
||||
float *radius);
|
||||
protected:
|
||||
virtual unsigned int getNextSector(unsigned int index);
|
||||
|
||||
|
@ -2234,7 +2234,10 @@ void Kart::setOnScreenText(const wchar_t *text)
|
||||
{
|
||||
core::dimension2d<u32> textsize = GUIEngine::getFont()->getDimension(text);
|
||||
scene::ISceneManager* sm = irr_driver->getSceneManager();
|
||||
sm->addBillboardTextSceneNode(GUIEngine::getFont(),
|
||||
// FIXME: Titlefont is the only font guaranteed to be loaded if STK
|
||||
// is started without splash screen (since "Loading" is shown even in this
|
||||
// case). A smaller font would be better
|
||||
sm->addBillboardTextSceneNode(GUIEngine::getTitleFont(),
|
||||
text,
|
||||
getNode(),
|
||||
core::dimension2df(textsize.Width/55.0f,
|
||||
|
@ -56,6 +56,7 @@ KartProperties::KartProperties(const std::string &filename)
|
||||
|
||||
m_groups.clear();
|
||||
m_turn_angle_at_speed.clear();
|
||||
m_turn_radius_at_speed.clear();
|
||||
m_turn_speed.clear();
|
||||
m_speed_angle_increase.clear();
|
||||
m_custom_sfx_id.resize(SFXManager::NUM_CUSTOMS);
|
||||
@ -234,9 +235,11 @@ void KartProperties::load(const std::string &filename, const std::string &node)
|
||||
m_wheel_radius );
|
||||
m_wheel_base = fabsf( m_kart_model->getWheelPhysicsPosition(0).getZ()
|
||||
-m_kart_model->getWheelPhysicsPosition(2).getZ());
|
||||
for(unsigned int i=0; i<m_turn_angle_at_speed.size(); i++)
|
||||
for(unsigned int i=0; i<m_turn_radius_at_speed.size(); i++)
|
||||
{
|
||||
m_turn_angle_at_speed[i] = sin(m_wheel_base/m_turn_angle_at_speed[i]);
|
||||
m_turn_angle_at_speed.push_back(
|
||||
sin(m_wheel_base/m_turn_radius_at_speed[i])
|
||||
);
|
||||
}
|
||||
for(unsigned int i=0; i<m_turn_speed.size()-1; i++)
|
||||
{
|
||||
@ -335,14 +338,14 @@ void KartProperties::getAllData(const XMLNode * root)
|
||||
|
||||
if(const XMLNode *turn_node = root->getNode("turn"))
|
||||
{
|
||||
turn_node->get("time-full-steer", &m_time_full_steer );
|
||||
turn_node->get("time-full-steer-ai", &m_time_full_steer_ai );
|
||||
turn_node->get("turn-speed", &m_turn_speed );
|
||||
turn_node->get("turn-radius", &m_turn_angle_at_speed);
|
||||
turn_node->get("time-full-steer", &m_time_full_steer );
|
||||
turn_node->get("time-full-steer-ai", &m_time_full_steer_ai );
|
||||
turn_node->get("turn-speed", &m_turn_speed );
|
||||
turn_node->get("turn-radius", &m_turn_radius_at_speed);
|
||||
// For now store the turn radius in turn angle, the correct
|
||||
// value can only be determined later in ::load
|
||||
if(m_turn_speed.size()==0 ||
|
||||
m_turn_angle_at_speed.size() != m_turn_speed.size())
|
||||
m_turn_radius_at_speed.size() != m_turn_speed.size())
|
||||
{
|
||||
printf("Inconsistent turn-speed and turn-radius "
|
||||
"settings for kart %s\n", getIdent().c_str());
|
||||
@ -356,9 +359,9 @@ void KartProperties::getAllData(const XMLNode * root)
|
||||
"values for kart %s.\n", getIdent().c_str());
|
||||
exit(-1);
|
||||
}
|
||||
if(m_turn_angle_at_speed[i]>m_turn_angle_at_speed[i+1])
|
||||
if(m_turn_radius_at_speed[i]>m_turn_radius_at_speed[i+1])
|
||||
{
|
||||
printf("The turn-angle must be increasing for kart %s.\n",
|
||||
printf("The turn-radius must be increasing for kart %s.\n",
|
||||
getIdent().c_str());
|
||||
exit(-1);
|
||||
}
|
||||
@ -696,6 +699,29 @@ float KartProperties::getMaxSteerAngle(float speed) const
|
||||
return 0; // avoid compiler warning
|
||||
} // getMaxSteerAngle
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the (maximum) speed for a given turn radius.
|
||||
* \param radius The radius for which the speed needs to be computed.
|
||||
*/
|
||||
float KartProperties::getSpeedForTurnRadius(float radius) const
|
||||
{
|
||||
if(radius < m_turn_radius_at_speed[0] )
|
||||
return m_turn_speed[0];
|
||||
|
||||
const unsigned int last = m_turn_speed.size();
|
||||
|
||||
for(unsigned int i=1; i<last; i++)
|
||||
{
|
||||
if(radius < m_turn_radius_at_speed[i])
|
||||
{
|
||||
return m_turn_speed[i-1]
|
||||
+ (m_turn_speed[i]-m_turn_speed[i-1]) * (radius-m_turn_radius_at_speed[i-1])
|
||||
/ (m_turn_radius_at_speed[i]-m_turn_radius_at_speed[i-1]);
|
||||
}
|
||||
} // for i < last
|
||||
return m_turn_speed[last-1];
|
||||
} // getSpeedForTurnRadius
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called the first time a kart accelerates after 'ready-set-go'. It searches
|
||||
* through m_startup_times to find the appropriate slot, and returns the
|
||||
|
@ -127,6 +127,9 @@ private:
|
||||
/** Stores the turn angle at the corresponding turn speed. */
|
||||
std::vector<float> m_turn_angle_at_speed;
|
||||
|
||||
/** Stores the turn radius at the corresponding turn speed. */
|
||||
std::vector<float> m_turn_radius_at_speed;
|
||||
|
||||
/** Increase of turn angle with speed. */
|
||||
std::vector<float> m_speed_angle_increase;
|
||||
|
||||
@ -339,6 +342,9 @@ public:
|
||||
/** Returns the maximum steering angle (depending on speed). */
|
||||
float getMaxSteerAngle (float speed) const;
|
||||
|
||||
/** Returns the (maximum) speed for a given turn radius. */
|
||||
float getSpeedForTurnRadius(float radius) const;
|
||||
|
||||
/** Returns the material for the kart icons. */
|
||||
Material* getIconMaterial () const {return m_icon_material; }
|
||||
|
||||
|
@ -164,6 +164,7 @@
|
||||
#include "items/attachment_manager.hpp"
|
||||
#include "items/item_manager.hpp"
|
||||
#include "items/projectile_manager.hpp"
|
||||
#include "karts/controller/ai_base_controller.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/kart_properties_manager.hpp"
|
||||
#include "modes/demo_world.hpp"
|
||||
@ -388,7 +389,7 @@ void cmdLineHelp (char* invocation)
|
||||
"as --gfx).\n"
|
||||
" --camera-style=n Flexible (0) or hard like v0.6 (1) kart-camera "
|
||||
"link.\n"
|
||||
" --profile-laps=n Enable automatic driven profile mode for n "
|
||||
" -=n Enable automatic driven profile mode for n "
|
||||
"laps.\n"
|
||||
" --profile-time=n Enable automatic driven profile mode for n "
|
||||
"seconds.\n"
|
||||
@ -663,6 +664,10 @@ int handleCmdLine(int argc, char **argv)
|
||||
{
|
||||
UserConfigParams::m_rendering_debug=true;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--ai-debug"))
|
||||
{
|
||||
AIBaseController::enableDebug();
|
||||
}
|
||||
else if(sscanf(argv[i], "--server=%d",&n)==1)
|
||||
{
|
||||
network_manager->setMode(NetworkManager::NW_SERVER);
|
||||
|
@ -245,11 +245,21 @@ Controller* World::loadAIController(AbstractKart *kart)
|
||||
// const int NUM_ROBOTS = 1;
|
||||
// For now: instead of random switching, use each
|
||||
// robot in turns: switch(m_random.get(NUM_ROBOTS))
|
||||
//static int turn=0;
|
||||
//turn=2-turn;
|
||||
#define USE_PRESENT_AI
|
||||
#undef USE_SKIDDING_AI
|
||||
#undef USE_ALL_AIS
|
||||
|
||||
#ifdef USE_PRESENT_AI
|
||||
int turn = 2;
|
||||
#elif defined(USE_SKIDDING_AI)
|
||||
int turn = 1;
|
||||
#elif defined(USE_ALL_AIS)
|
||||
static int turn=3;
|
||||
turn=(turn++) % 3;
|
||||
#else
|
||||
int turn=0; // use default AU
|
||||
#endif
|
||||
|
||||
// For now disable the new AI.
|
||||
int turn=2;
|
||||
switch(turn)
|
||||
{
|
||||
case 0:
|
||||
|
@ -158,6 +158,19 @@ void GraphNode::markAllSuccessorsToUse(unsigned int n,
|
||||
}
|
||||
} // markAllSuccesorsToUse
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void GraphNode::setDirectionData(unsigned int successor, DirectionType dir,
|
||||
unsigned int last_node_index)
|
||||
{
|
||||
if(m_direction.size()<successor+1)
|
||||
{
|
||||
m_direction.resize(successor+1);
|
||||
m_last_index_same_direction.resize(successor+1);
|
||||
}
|
||||
m_direction[successor] = dir;
|
||||
m_last_index_same_direction[successor] = last_node_index;
|
||||
} // setDirectionData
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the distance a point has from this quad in forward and sidewards
|
||||
* direction, i.e. how far forwards the point is from the beginning of the
|
||||
|
@ -38,6 +38,12 @@ class QuadGraph;
|
||||
*/
|
||||
class GraphNode
|
||||
{
|
||||
public:
|
||||
/** To indiciate in which direction the track is going:
|
||||
* straight, left, right. */
|
||||
enum DirectionType {DIR_STRAIGHT, DIR_LEFT, DIR_RIGHT};
|
||||
|
||||
private:
|
||||
/** Index of this node in the set of quads. Several graph nodes can use
|
||||
* the same quad, meaning it is possible to use a quad more than once,
|
||||
* e.g. a figure 8 like track. */
|
||||
@ -98,8 +104,14 @@ class GraphNode
|
||||
* graph nodes. */
|
||||
PathToNodeVector m_path_to_node;
|
||||
|
||||
void markAllSuccessorsToUse(unsigned int n,
|
||||
PathToNodeVector *m_path_to_node);
|
||||
/** The direction for each of the successors. */
|
||||
std::vector<DirectionType> m_direction;
|
||||
|
||||
/** Stores for each successor the index of the last graph node that
|
||||
* has the same direction (i.e. if index 0 curves left, this vector
|
||||
* will store the index of the last graph node that is still turning
|
||||
* left. */
|
||||
std::vector<unsigned int> m_last_index_same_direction;
|
||||
|
||||
/**
|
||||
* Sets of checklines you should have activated when you are driving on
|
||||
@ -108,6 +120,9 @@ class GraphNode
|
||||
*/
|
||||
std::vector< int > m_checkline_requirements;
|
||||
|
||||
void markAllSuccessorsToUse(unsigned int n,
|
||||
PathToNodeVector *m_path_to_node);
|
||||
|
||||
public:
|
||||
GraphNode(unsigned int quad_index, unsigned int node_index);
|
||||
void addSuccessor (unsigned int to);
|
||||
@ -115,6 +130,8 @@ public:
|
||||
float getDistance2FromPoint(const Vec3 &xyz);
|
||||
void setupPathsToNode();
|
||||
void setChecklineRequirements(int latest_checkline);
|
||||
void setDirectionData(unsigned int successor, DirectionType dir,
|
||||
unsigned int last_node_index);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the i-th successor node. */
|
||||
unsigned int getSuccessor(unsigned int i) const
|
||||
@ -196,6 +213,14 @@ public:
|
||||
/** Returns the checkline requirements of this graph node. */
|
||||
const std::vector<int>& getChecklineRequirements() const
|
||||
{ return m_checkline_requirements; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the direction in which the successor n is. */
|
||||
void getDirectionData(unsigned int succ, DirectionType *dir,
|
||||
unsigned int *last) const
|
||||
{
|
||||
*dir = m_direction[succ]; *last = m_last_index_same_direction[succ];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
}; // GraphNode
|
||||
|
||||
#endif
|
||||
|
@ -95,7 +95,8 @@ void QuadGraph::load(const std::string &filename)
|
||||
m_all_nodes.push_back(new GraphNode(i, m_all_nodes.size()));
|
||||
// Then set the default loop:
|
||||
setDefaultSuccessors();
|
||||
|
||||
computeDirectionData();
|
||||
|
||||
if (m_all_nodes.size() > 0)
|
||||
{
|
||||
m_lap_length = m_all_nodes[m_all_nodes.size()-1]->getDistanceFromStart()
|
||||
@ -184,6 +185,7 @@ void QuadGraph::load(const std::string &filename)
|
||||
|
||||
setDefaultSuccessors();
|
||||
computeDistanceFromStart(getStartNode(), 0.0f);
|
||||
computeDirectionData();
|
||||
|
||||
// Define the track length as the maximum at the end of a quad
|
||||
// (i.e. distance_from_start + length till successor 0).
|
||||
@ -211,6 +213,8 @@ unsigned int QuadGraph::getStartNode() const
|
||||
} // getStartNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Sets the checkline requirements for all nodes in the graph.
|
||||
*/
|
||||
void QuadGraph::computeChecklineRequirements()
|
||||
{
|
||||
computeChecklineRequirements(m_all_nodes[0],
|
||||
@ -641,6 +645,109 @@ void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta)
|
||||
}
|
||||
} // updateDistancesForAllSuccessors
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Computes the direction (striaght, left, right) of all graph nodes and the
|
||||
* lastest graph node that is still turning in the given direction. For
|
||||
* example, if a successor to this graph node is turning left, it will compute
|
||||
* the last graph node that is still turning left. This data is used by the
|
||||
* AI to estimate the turn radius.
|
||||
* At this stage there is one restriction: if a node with more than one
|
||||
* successor is ahead, only successor 0 is used. That might lead to somewhat
|
||||
* incorrect results (i.e. the last successor is determined assuming that
|
||||
* the kart is always using successor 0, while in reality it might follow
|
||||
* a different successor, resulting in a different turn radius. It is not
|
||||
* expected that this makes much difference for the AI (since it will update
|
||||
* its data constantly, i.e. if it takes a different turn, it will be using
|
||||
* the new data).
|
||||
*/
|
||||
void QuadGraph::computeDirectionData()
|
||||
{
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++)
|
||||
{
|
||||
for(unsigned int succ_index=0;
|
||||
succ_index<m_all_nodes[i]->getNumberOfSuccessors();
|
||||
succ_index++)
|
||||
{
|
||||
determineDirection(i, succ_index);
|
||||
} // for next < getNumberOfSuccessor
|
||||
|
||||
} // for i < m_all_nodes.size()
|
||||
} // computeDirectionData
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Adjust the given angle to be in [-PI, PI].
|
||||
*/
|
||||
float QuadGraph::normalizeAngle(float f)
|
||||
{
|
||||
if(f>M_PI) f -= 2*M_PI;
|
||||
else if(f<-M_PI) f += 2*M_PI;
|
||||
return f;
|
||||
} // normalizeAngle
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Determines the direction of the quad graph when driving to the specified
|
||||
* successor. It also determines the last graph node that is still following
|
||||
* the given direction. The computed data is saved in the corresponding
|
||||
* graph node.
|
||||
* It compares the lines connecting the center point of node n with n+1 and
|
||||
* the lines connecting n+1 and n+2 (where n is the current node, and +1 etc.
|
||||
* specifying the successor). Then it keeps on testing the line from n+2 to
|
||||
* n+3, n+3 to n+4, ... as long as the turn direction is the same. The last
|
||||
* value that still has the same direction is then set as the last node
|
||||
* with the same direction in the specified graph node.
|
||||
* \param current Index of the graph node with which to start ('n' in the
|
||||
* description above).
|
||||
* \param succ_index The successor to be followed from the current node.
|
||||
* If there should be any other branches later, successor
|
||||
* 0 will always be tetsed.
|
||||
*/
|
||||
void QuadGraph::determineDirection(unsigned int current,
|
||||
unsigned int succ_index)
|
||||
{
|
||||
// The maximum angle which is still considered to be straight
|
||||
const float max_straight_angle=0.1f;
|
||||
if(current==28)
|
||||
printf("");
|
||||
// Compute the angle from n (=current) to n+1 (=next)
|
||||
float angle_current = getAngleToNext(current, succ_index);
|
||||
unsigned int next = getNode(current).getSuccessor(succ_index);
|
||||
float angle_next = getAngleToNext(next, 0);
|
||||
float rel_angle = normalizeAngle(angle_next-angle_current);
|
||||
// Small angles are considered to be straight
|
||||
if(fabsf(rel_angle)<max_straight_angle)
|
||||
rel_angle = 0;
|
||||
|
||||
int prev = next; // is now n+1
|
||||
next = getNode(next).getSuccessor(0); // next is now n+2
|
||||
|
||||
while(1)
|
||||
{
|
||||
// Now compute the angle from n+1 (new current) to n+2 (new next)
|
||||
angle_current = angle_next;
|
||||
angle_next = getAngleToNext(next, 0);
|
||||
float new_rel_angle = normalizeAngle(angle_next - angle_current);
|
||||
if(fabsf(new_rel_angle)<max_straight_angle)
|
||||
new_rel_angle = 0;
|
||||
// We have reached a different direction if we go from a non-straight
|
||||
// section to a straight one, from a straight section to a non-
|
||||
// straight section, or from a left to a right turn (or vice versa)
|
||||
if( (rel_angle != 0 && new_rel_angle == 0 ) ||
|
||||
(rel_angle == 0 && new_rel_angle != 0 ) ||
|
||||
(rel_angle * new_rel_angle < 0 ) )
|
||||
break;
|
||||
rel_angle = new_rel_angle;
|
||||
|
||||
prev = next;
|
||||
next = getNode(next).getSuccessor(0);
|
||||
} // while(1)
|
||||
|
||||
GraphNode::DirectionType dir =
|
||||
rel_angle==0 ? GraphNode::DIR_STRAIGHT
|
||||
: (rel_angle>0) ? GraphNode::DIR_RIGHT
|
||||
: GraphNode::DIR_LEFT;
|
||||
m_all_nodes[current]->setDirectionData(succ_index, dir, next);
|
||||
} // determineDirection
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This function takes absolute coordinates (coordinates in OpenGL
|
||||
* space) and transforms them into coordinates based on the track. The y-axis
|
||||
|
@ -80,7 +80,10 @@ private:
|
||||
|
||||
void setDefaultSuccessors();
|
||||
void computeChecklineRequirements(GraphNode* node, int latest_checkline);
|
||||
|
||||
void computeDirectionData();
|
||||
void determineDirection(unsigned int current, unsigned int succ_index);
|
||||
float normalizeAngle(float f);
|
||||
|
||||
void addSuccessor(unsigned int from, unsigned int to);
|
||||
void load (const std::string &filename);
|
||||
void computeDistanceFromStart(unsigned int start_node, float distance);
|
||||
|
@ -1417,7 +1417,7 @@ void Track::loadTrackModel(World* parent, bool reverse_track,
|
||||
{
|
||||
std::vector<XMLNode*> subtitles;
|
||||
node->getNodes("subtitle", subtitles);
|
||||
for (int i = 0; i < subtitles.size(); i++)
|
||||
for (unsigned int i = 0; i < subtitles.size(); i++)
|
||||
{
|
||||
int from = -1, to = -1;
|
||||
std::string subtitle_text;
|
||||
|
Loading…
Reference in New Issue
Block a user