First version of the new AI that can skid. It's still behaving

quite stupid, but can sometimes compete with the old AI (unless
the new AI is behaving stupid ... as I've said). This is still
disabled by default (and if you want to test it, make sure
to adjust stk_config.xml as well).


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11438 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-07-25 23:58:30 +00:00
parent 9b15b22ef5
commit fb5ae0a566
7 changed files with 301 additions and 61 deletions

View File

@ -402,13 +402,7 @@ float AIBaseController::normalizeAngle(float angle)
void AIBaseController::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
m_controls->m_skid = fabsf(steer_fraction)>=m_skidding_threshold;
if(m_kart->hasViewBlockedByPlunger()) m_controls->m_skid = false;
// FIXME: Disable skidding for now if the new skidding
// code is activated, since the AI can not handle this
// properly.
if(m_kart->getKartProperties()->getSkiddingProperties()->getSkidVisualTime()>0)
m_controls->m_skid = false;
m_controls->m_skid = doSkid(steer_fraction);
float old_steer = m_controls->m_steer;
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
@ -433,3 +427,28 @@ void AIBaseController::setSteering(float angle, float dt)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
// ----------------------------------------------------------------------------
/** Determines if the kart should skid. The base implementation enables
* skidding if a sharp turn is needed (which is for the old skidding
* implementation).
* \param steer_fraction The steering fraction as computed by the
* AIBaseController.
* \return True if the kart should skid.
*/
bool AIBaseController::doSkid(float steer_fraction)
{
// Disable skidding when a plunger is in the face
if(m_kart->hasViewBlockedByPlunger()) return false;
// FIXME: Disable skidding for now if the new skidding
// code is activated, since the AI can not handle this
// properly.
if(m_kart->getKartProperties()->getSkiddingProperties()
->getSkidVisualTime()>0)
return false;
// Otherwise return if we need a sharp turn (which is
// for the old skidding implementation).
return fabsf(steer_fraction)>=m_skidding_threshold;
} // doSkid

View File

@ -83,12 +83,13 @@ protected:
virtual unsigned int getNextSector(unsigned int index);
virtual void newLap (int lap);
virtual void setControllerName(const std::string &name);
virtual void setSteering (float angle, float dt);
float steerToAngle (const unsigned int sector, const float angle);
float steerToPoint (const Vec3 &point);
float normalizeAngle(float angle);
void setSteering (float angle, float dt);
void setSkiddingFraction(float f);
void computePath();
virtual bool doSkid(float steer_fraction);
// ------------------------------------------------------------------------
/** This can be called to detect if the kart is stuck (i.e. repeatedly
* hitting part of the track). */

View File

@ -22,7 +22,14 @@
//The AI debugging works best with just 1 AI kart, so set the number of karts
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#ifdef DEBUG
// Enable AI graphical debugging
# undef AI_DEBUG
// Shows left and right lines when using new findNonCrashing function
# undef AI_DEBUG_NEW_FIND_NON_CRASHING
// Show the predicted turn circles
# undef AI_DEBUG_CIRCLES
// Show the heading of the kart
# undef AI_DEBUG_KART_HEADING
#endif
#include "karts/controller/skidding_ai.hpp"
@ -47,6 +54,8 @@
#include "karts/kart_properties.hpp"
#include "karts/max_speed.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/skidding.hpp"
#include "karts/skidding_properties.hpp"
#include "items/attachment.hpp"
#include "items/powerup.hpp"
#include "modes/linear_world.hpp"
@ -117,14 +126,22 @@ SkiddingAI::SkiddingAI(AbstractKart *kart)
#define NUM_CURVES (CURVE_QG+1)
m_curve = new ShowCurve*[NUM_CURVES];
for(unsigned int i=0; i<NUM_CURVES; i++)
m_curve[i] = NULL;
#ifdef AI_DEBUG_CIRCLES
m_curve[CURVE_PREDICT1] = new ShowCurve(0.05f, 0.5f,
irr::video::SColor(128, 0, 0, 128));
#endif
#ifdef AI_DEBUG_KART_HEADING
m_curve[CURVE_KART] = new ShowCurve(0.5f, 0.5f,
irr::video::SColor(128, 0, 0, 128));
#endif
#ifdef AI_DEBUG_NEW_FIND_NON_CRASHING
m_curve[CURVE_LEFT] = new ShowCurve(0.5f, 0.5f,
irr::video::SColor(128, 128, 0, 0));
m_curve[CURVE_RIGHT] = new ShowCurve(0.5f, 0.5f,
irr::video::SColor(128, 0, 128, 0));
#endif
m_curve[CURVE_QG] = new ShowCurve(0.5f, 0.5f,
irr::video::SColor(128, 0, 128, 0));
#endif
@ -343,7 +360,7 @@ void SkiddingAI::handleBraking()
printf("[AI] braking: %s not aligned with track.\n",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
//m_controls->m_brake = true;
return;
}
if(m_current_track_direction==GraphNode::DIR_LEFT ||
@ -690,7 +707,7 @@ void SkiddingAI::handleAcceleration( const float dt)
return;
}
if( m_controls->m_brake == true )
if( m_controls->m_brake )
{
m_controls->m_accel = 0.0f;
return;
@ -986,7 +1003,8 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result)
const unsigned int RIGHT_END_POINT = 1;
core::line2df left (xz, q[LEFT_END_POINT ].toIrrVector2d());
core::line2df right(xz, q[RIGHT_END_POINT].toIrrVector2d());
#ifdef AI_DEBUG
#if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
const Vec3 eps(0,0.5f,0);
m_curve[CURVE_LEFT]->clear();
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps);
@ -996,10 +1014,12 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result)
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps);
m_curve[CURVE_RIGHT]->addPoint(q[RIGHT_END_POINT]+eps);
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps);
#endif
#ifdef AI_DEBUG_KART_HEADING
m_curve[CURVE_KART]->clear();
m_curve[CURVE_KART]->addPoint(m_kart->getXYZ()+eps);
Vec3 forw(0, 0, 50);
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw));
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
#endif
while(1)
{
@ -1015,7 +1035,7 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result)
if(right.getPointOrientation(p)<0)
break;
left.end = p;
#ifdef AI_DEBUG
#if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
Vec3 ppp(p.X, m_kart->getXYZ().getY(), p.Y);
m_curve[CURVE_LEFT]->addPoint(ppp+eps);
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps);
@ -1031,7 +1051,8 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result)
// Break if new point is to the left of left line
if(left.getPointOrientation(p)>0)
break;
#ifdef AI_DEBUG
#if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
Vec3 ppp(p.X, m_kart->getXYZ().getY(), p.Y);
m_curve[CURVE_RIGHT]->addPoint(ppp+eps);
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps);
@ -1098,6 +1119,13 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result)
*/
void SkiddingAI::findNonCrashingPoint(Vec3 *result)
{
#ifdef AI_DEBUG_KART_HEADING
const Vec3 eps(0,0.5f,0);
m_curve[CURVE_KART]->clear();
m_curve[CURVE_KART]->addPoint(m_kart->getXYZ()+eps);
Vec3 forw(0, 0, 50);
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
#endif
unsigned int sector = m_next_node_index[m_track_node];
int target_sector;
@ -1193,11 +1221,12 @@ void SkiddingAI::determineTrackDirection()
#ifdef AI_DEBUG
// m_curve[CURVE_QG]->clear();
for(unsigned int i=m_track_node; i<=last; i++)
// for(unsigned int i=m_track_node; i<=last; i++)
{
// m_curve[CURVE_QG]->addPoint(qg->getNode(i).getCenter());
}
#endif
m_controls->m_skid = false;
if(m_current_track_direction==GraphNode::DIR_LEFT ||
m_current_track_direction==GraphNode::DIR_RIGHT )
@ -1220,32 +1249,161 @@ void SkiddingAI::determineTrackDirection()
determineTurnRadius(xyz, tangent, last_xyz,
&center, &m_current_curve_radius);
#ifdef AI_DEBUG
// m_curve[CURVE_PREDICT1]->makeCircle(center, m_current_curve_radius);
// m_curve[CURVE_PREDICT1]->addPoint(last_xyz);
// m_curve[CURVE_PREDICT1]->addPoint(center);
// m_curve[CURVE_PREDICT1]->addPoint(xyz);
#ifdef ADJUST_TURN_RADIUS_TO_AVOID_CRASH_INTO_TRACK
for(unsigned int i=next; i<=last; i++)
{
// Pick either the lower left or right point:
int index = m_current_track_direction==GraphNode::DIR_LEFT
? 0 : 1;
float r = (center - qg->getQuadOfNode(i)[index]).length();
if(m_current_curve_radius < r)
{
determineTurnRadius(xyz, tangent, qg->getQuadOfNode(i)[index],
&center, &m_current_curve_radius);
break;
}
}
#endif
#if defined(AI_DEBUG) && defined(AI_DEBUG_CIRCLES)
m_curve[CURVE_PREDICT1]->makeCircle(center, m_current_curve_radius);
m_curve[CURVE_PREDICT1]->addPoint(last_xyz);
m_curve[CURVE_PREDICT1]->addPoint(center);
m_curve[CURVE_PREDICT1]->addPoint(xyz);
#endif
// Estimate how long it takes to finish the curve
Vec3 diff_kart = xyz - center;
Vec3 diff_last = last_xyz - center;
float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ());
float angle_last = atan2(diff_last.getX(), diff_last.getZ());
float angle = m_current_track_direction == GraphNode::DIR_RIGHT
? angle_last - angle_kart
: angle_kart - angle_last;
angle = normalizeAngle(angle);
float length = m_current_curve_radius*angle;
float duration = length / m_kart->getSpeed();
//printf("Radius %f angle %f length %f dur %f\n",
// m_current_curve_radius, angle*180.0/3.1415,
// length, duration);
}
// Only try skidding when a certain minimum speed is reached.
if(m_kart->getSpeed() > 5.0f)
{
// Estimate how long it takes to finish the curve
Vec3 diff_kart = xyz - center;
Vec3 diff_last = last_xyz - center;
float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ());
float angle_last = atan2(diff_last.getX(), diff_last.getZ());
float angle = m_current_track_direction == GraphNode::DIR_RIGHT
? angle_last - angle_kart
: angle_kart - angle_last;
angle = normalizeAngle(angle);
float length = m_current_curve_radius*fabsf(angle);
float duration = length / m_kart->getSpeed();
duration *= 1.5f;
const Skidding *skidding = m_kart->getSkidding();
if(m_controls->m_skid && duration < 1.0f)
{
m_controls->m_skid = false;
if(m_ai_debug)
printf("[AI] skid : '%s' too short, stop skid.\n",
m_kart->getIdent().c_str());
}
else if(skidding->getNumberOfBonusTimes()>0 &&
skidding->getTimeTillBonus(0) < duration)
{
#ifdef DEBUG
if(m_ai_debug)
printf("[AI] skid: %s start skid, duration %f.\n",
m_kart->getIdent().c_str(), duration);
#endif
m_controls->m_skid = true;
//m_controls->m_steering =
// m_current_track_direction == GraphNode::DIR_RIGHT : 1 : -1;
} // if curve long enough for skidding
} // if speed > minimum skid speed
} // if(m_current_track_direction == DIR_LEFT || DIR_RIGHT )
return;
} // determineTrackDirection
// ----------------------------------------------------------------------------
/** Determines if the kart should skid. The base implementation enables
* skidding
* \param steer_fraction The steering fraction as computed by the
* AIBaseController.
* \return True if the kart should skid.
*/
bool SkiddingAI::doSkid(float steer_fraction)
{
return m_controls->m_skid;
} // doSkid
//-----------------------------------------------------------------------------
/** Converts the steering angle to a lr steering in the range of -1 to 1.
* If the steering angle is too great, it will also trigger skidding. This
* function uses a 'time till full steer' value specifying the time it takes
* for the wheel to reach full left/right steering similar to player karts
* when using a digital input device. The parameter is defined in the kart
* properties and helps somewhat to make AI karts more 'pushable' (since
* otherwise the karts counter-steer to fast).
* It also takes the effect of a plunger into account by restricting the
* actual steer angle to 50% of the maximum.
* \param angle Steering angle.
* \param dt Time step.
*/
void SkiddingAI::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
m_controls->m_skid = doSkid(steer_fraction);
// Adjust steer fraction in case to be in [-1,1]
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
// Restrict steering when a plunger is in the face
if(m_kart->hasViewBlockedByPlunger())
{
if (steer_fraction > 0.5f) steer_fraction = 0.5f;
else if(steer_fraction < -0.5f) steer_fraction = -0.5f;
}
const Skidding *skidding = m_kart->getSkidding();
// If we are supposed to skid, but the current steering is still
// in the wrong direction, don't start to skid just now, since then
// we can't turn into the direction we want to anymore (see
// Skidding class)
Skidding::SkidState ss = skidding->getSkidState();
if(ss==Skidding::SKID_ACCUMULATE_LEFT && steer_fraction>0 ||
ss==Skidding::SKID_ACCUMULATE_RIGHT && steer_fraction<0 )
{
m_controls->m_skid = false;
#ifdef DEBUG
if(m_ai_debug)
printf("[AI] skid : '%s' wrong steering, stop skid.\n",
m_kart->getIdent().c_str());
#endif
}
if(m_controls->m_skid && (
skidding->getSkidState()==Skidding::SKID_ACCUMULATE_LEFT ||
skidding->getSkidState()==Skidding::SKID_ACCUMULATE_RIGHT))
{
steer_fraction =
skidding->getSteeringWhenSkidding(steer_fraction);
if(steer_fraction<-1.0f)
steer_fraction = -1.0f;
else if(steer_fraction>1.0f)
steer_fraction = 1.0f;
}
float old_steer = m_controls->m_steer;
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_kart->getKartProperties()->getTimeFullSteerAI();
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
// ----------------------------------------------------------------------------
/** 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:

View File

@ -158,6 +158,9 @@ private:
const Vec3 &end,
Vec3 *center,
float *radius);
virtual bool doSkid(float steer_fraction);
virtual void setSteering(float angle, float dt);
protected:
virtual unsigned int getNextSector(unsigned int index);

View File

@ -120,6 +120,41 @@ void Skidding::updateSteering(float steer)
} // updateSteering
// ----------------------------------------------------------------------------
/** Returns the steering value necessary to steer the specified amount in
* 'steering'. If the kart is not skidding, the return value is just
* steering. Otherwise the value will be (depending on current skidding
* direction) adjusted to a value 'steering1', so that when the kart
* steers 'steering1', it will de facto steer by the original steering
* amount. If it's not possible
*/
float Skidding::getSteeringWhenSkidding(float steering) const
{
switch(m_skid_state)
{
case SKID_OLD: assert(false); break;
case SKID_SHOW_GFX_LEFT:
case SKID_SHOW_GFX_RIGHT:
case SKID_NONE: return steering;
break;
case SKID_ACCUMULATE_RIGHT:
{
float f = (steering - m_skid_reduce_turn_min)
/ m_skid_reduce_turn_delta;
return f *2.0f-1.0f;
break;
}
case SKID_ACCUMULATE_LEFT:
{
float f = (steering + m_skid_reduce_turn_min)
/ m_skid_reduce_turn_delta;
return 2.0f * f +1.0f;
break;
}
} // switch m_skid_state
return 0; // keep compiler quiet
} // getSteeringWhenSkidding
// ----------------------------------------------------------------------------
/** Updates skidding status.
* \param dt Time step size.
* \param is_on_ground True if the kart is on ground.
@ -186,9 +221,10 @@ void Skidding::update(float dt, bool is_on_ground,
// Just testing for the sign of steering can result in unexpected
// beahviour, e.g. if a player is still turning left, but already
// presses right (it will take a few frames for this steering to
// actuallu take place, see player_controller) - the kart would skid
// actually take place, see player_controller) - the kart would skid
// to the left. So we test for a 'clear enough' steering direction.
if(!skidding || fabsf(steering)<0.9f) break;
//FIXME if(!skidding || fabsf(steering)<0.9f) break;
if(!skidding) break;
m_skid_state = steering > 0 ? SKID_ACCUMULATE_RIGHT
: SKID_ACCUMULATE_LEFT;
// Add a little jump to the kart. Determine the vertical speed

View File

@ -57,6 +57,7 @@ private:
* trigger the skidding bonus. */
float m_skid_time;
public:
/** SKID_OLD: old skidding, will be removed. */
/** SKID_NONE: Kart is currently not skidding.
* SKID_ACCUMULATE_LEFT: Kart is skidding to the left and accumulating
@ -65,9 +66,13 @@ private:
* SKID_SHOW_GFX_LEFT: Shows the gfx, while the bonus is active,
* and the kart was turning left.
* SKID_SHOW_GFX_RIGHT: Similar for turning right. */
enum {SKID_OLD, SKID_NONE, SKID_ACCUMULATE_LEFT, SKID_ACCUMULATE_RIGHT,
SKID_SHOW_GFX_LEFT, SKID_SHOW_GFX_RIGHT}
m_skid_state;
enum SkidState {SKID_OLD, SKID_NONE, SKID_ACCUMULATE_LEFT,
SKID_ACCUMULATE_RIGHT, SKID_SHOW_GFX_LEFT,
SKID_SHOW_GFX_RIGHT} ;
private:
/** The current skidding state. */
SkidState m_skid_state;
/** A read-only pointer to the kart's properties. */
Kart *m_kart;
@ -80,27 +85,34 @@ private:
unsigned int getSkidBonus(float *bonus_time, float *bonus_speed) const;
void updateSteering(float steer);
public:
Skidding(Kart *kart, const SkiddingProperties *sp);
~Skidding();
void reset();
void update(float dt, bool is_on_ground, float steer,
bool skidding);
// ----------------------------------------------------------------------
/** Determines how much the graphics model of the kart should be rotated
* additionally (for skidding), depending on how long the kart has been
* skidding etc. */
float getVisualSkidRotation() const { return m_visual_rotation; };
// ----------------------------------------------------------------------
/** Returns the current skid factor in [1, skid_max_for_this_kart]. */
float getSkidFactor() const { return m_skid_factor; }
// ----------------------------------------------------------------------
/** Returns true if the kart is skidding. */
bool isSkidding() const { return m_skid_factor>1.0f; }
// ----------------------------------------------------------------------
/** Returns the steering fraction to be used by the physics. This is
* a fraction of the maximum steering angle ( so in [-1, 1]). */
float getSteeringFraction() { return m_real_steering; }
Skidding(Kart *kart, const SkiddingProperties *sp);
~Skidding();
void reset();
void update(float dt, bool is_on_ground, float steer,
bool skidding);
// ------------------------------------------------------------------------
/** Determines how much the graphics model of the kart should be rotated
* additionally (for skidding), depending on how long the kart has been
* skidding etc. */
float getVisualSkidRotation() const { return m_visual_rotation; };
// ------------------------------------------------------------------------
/** Returns the current skid factor in [1, skid_max_for_this_kart]. */
float getSkidFactor() const { return m_skid_factor; }
// ------------------------------------------------------------------------
/** Returns true if the kart is skidding. */
bool isSkidding() const { return m_skid_factor>1.0f; }
// ------------------------------------------------------------------------
/** Returns the steering fraction to be used by the physics. This is
* a fraction of the maximum steering angle ( so in [-1, 1]). */
float getSteeringFraction() { return m_real_steering; }
// ------------------------------------------------------------------------
protected:
// The AI needs more details about the skidding state
friend class SkiddingAI;
/** Returns the skidding state. */
SkidState getSkidState() const { return m_skid_state; }
// ------------------------------------------------------------------------
float getSteeringWhenSkidding(float steering) const;
}; // Skidding

View File

@ -132,6 +132,17 @@ public:
float getSkidReduceTurnMin () const { return m_skid_reduce_turn_min; }
// ------------------------------------------------------------------------
float getSkidReduceTurnMax () const { return m_skid_reduce_turn_max; }
// ------------------------------------------------------------------------
/** Returns how many boni are defined for this kart. */
int getNumberOfBonusTimes() const { return m_skid_bonus_time.size(); }
// ------------------------------------------------------------------------
/** Returns how long a kart must skid in order to reach the specified
* bonus level.
* param n Bonus level (0<=n<m_skid_bonus_time.size())
*/
float getTimeTillBonus(unsigned int n) const
{ return m_skid_time_till_bonus[n]; }
// ------------------------------------------------------------------------
}; // SkiddingProperties