From: <hik...@us...> - 2012-07-25 23:58:36
|
Revision: 11438 http://supertuxkart.svn.sourceforge.net/supertuxkart/?rev=11438&view=rev Author: hikerstk Date: 2012-07-25 23:58:30 +0000 (Wed, 25 Jul 2012) Log Message: ----------- 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). Modified Paths: -------------- main/trunk/src/karts/controller/ai_base_controller.cpp main/trunk/src/karts/controller/ai_base_controller.hpp main/trunk/src/karts/controller/skidding_ai.cpp main/trunk/src/karts/controller/skidding_ai.hpp main/trunk/src/karts/skidding.cpp main/trunk/src/karts/skidding.hpp main/trunk/src/karts/skidding_properties.hpp Modified: main/trunk/src/karts/controller/ai_base_controller.cpp =================================================================== --- main/trunk/src/karts/controller/ai_base_controller.cpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/controller/ai_base_controller.cpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -402,13 +402,7 @@ 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 @@ ? 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 Modified: main/trunk/src/karts/controller/ai_base_controller.hpp =================================================================== --- main/trunk/src/karts/controller/ai_base_controller.hpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/controller/ai_base_controller.hpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -83,12 +83,13 @@ 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). */ Modified: main/trunk/src/karts/controller/skidding_ai.cpp =================================================================== --- main/trunk/src/karts/controller/skidding_ai.cpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/controller/skidding_ai.cpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -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 @@ #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 @@ 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 @@ return; } - if( m_controls->m_brake == true ) + if( m_controls->m_brake ) { m_controls->m_accel = 0.0f; return; @@ -986,7 +1003,8 @@ 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 @@ 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 @@ 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 @@ // 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::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 @@ #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,33 +1249,162 @@ determineTurnRadius(xyz, tangent, last_xyz, ¢er, &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], + ¢er, &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: * 1) Determine the line going through the center point start+end, which is Modified: main/trunk/src/karts/controller/skidding_ai.hpp =================================================================== --- main/trunk/src/karts/controller/skidding_ai.hpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/controller/skidding_ai.hpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -158,6 +158,9 @@ 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); Modified: main/trunk/src/karts/skidding.cpp =================================================================== --- main/trunk/src/karts/skidding.cpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/skidding.cpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -120,6 +120,41 @@ } // 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 @@ // 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 Modified: main/trunk/src/karts/skidding.hpp =================================================================== --- main/trunk/src/karts/skidding.hpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/skidding.hpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -57,6 +57,7 @@ * 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,10 +66,14 @@ * 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 @@ 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 Modified: main/trunk/src/karts/skidding_properties.hpp =================================================================== --- main/trunk/src/karts/skidding_properties.hpp 2012-07-25 22:09:12 UTC (rev 11437) +++ main/trunk/src/karts/skidding_properties.hpp 2012-07-25 23:58:30 UTC (rev 11438) @@ -132,6 +132,17 @@ 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 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |