Improved handling of situations in which a good and bad item

are close, which results in the AI getting the bad item when
trying to collect the good one. Some refactoring of code by
adding a hitLine function to Item.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11541 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-09-04 23:17:09 +00:00
parent 06821dc880
commit 6c7bfd3050
5 changed files with 117 additions and 50 deletions

View File

@ -211,9 +211,15 @@
impossible). A value of 1/maxFPS / 2 will guarantee that
the wheel can go from -1 to +1 steering in one frame,
basically disabling this mechanism.
bad-item-closeness is the maximum distance between a good and a
bad item which can force the AI to abandon a good item in order
to avoid hitting a bad item. If the distance is larger, it is
assumed that there will be enough time to change steering
direction.
-->
<ai max-item-angle="0.7" max-item-angle-high-speed="0.3"
time-full-steer="0.1"
bad-item-closeness="6"
/>
<!-- Slipstream: length: How far behind a kart slipstream works

View File

@ -34,6 +34,8 @@ using namespace irr;
#include "utils/no_copy.hpp"
#include "utils/vec3.hpp"
#include <line2d.h>
class AbstractKart;
class LODNode;
class Item;
@ -182,6 +184,7 @@ public:
(xyz-m_xyz).length2()<m_distance_2;
} // hitKart
private:
// ------------------------------------------------------------------------
/** Returns true if the Kart is close enough to hit this item, the item is
* not deactivated anymore, and it wasn't placed by this kart (this is
@ -191,9 +194,8 @@ public:
* \param xyz Location of kart (avoiding to use kart->getXYZ() so that
* kart.hpp does not need to be included here).
*/
protected:
friend class SkiddingAI;
bool hitKart (const core::vector2df &xyz, const AbstractKart *kart=NULL) const
bool hitKart (const core::vector2df &xyz,
const AbstractKart *kart=NULL) const
{
if(m_event_handler==kart && m_deactive_time >0) return false;
float d2 = (m_xyz.getX()-xyz.X)*(m_xyz.getX()-xyz.X)
@ -201,6 +203,24 @@ protected:
return d2 < m_distance_2;
} // hitKart
protected:
// ------------------------------------------------------------------------
// Some convenient functions for the AI only
friend class SkiddingAI;
/** Returns true if the specified line segment would come close enough
* to this item so that this item would be collected.
* \param line The line segment which is tested if it is close enough
* to this item so that this item would be collected.
*/
bool hitLine(const core::line2df &line,
const AbstractKart *kart=NULL) const
{
if(m_event_handler==kart && m_deactive_time >0) return false;
core::vector2df p2d = m_xyz.toIrrVector2d();
core::vector2df closest = line.getClosestPoint(p2d);
return hitKart(closest, kart);
} // hitLine
public:
// ------------------------------------------------------------------------
/** Sets the index of this item in the item manager list. */

View File

@ -29,6 +29,7 @@ AIProperties::AIProperties()
m_max_item_angle = UNDEFINED;
m_max_item_angle_high_speed = UNDEFINED;
m_time_full_steer = UNDEFINED;
m_bad_item_closeness_2 = UNDEFINED;
} // AIProperties
// ----------------------------------------------------------------------------
@ -40,6 +41,11 @@ void AIProperties::load(const XMLNode *ai_node)
ai_node->get("max-item-angle", &m_max_item_angle );
ai_node->get("max-item-angle-high-speed", &m_max_item_angle_high_speed);
ai_node->get("time-full-steer", &m_time_full_steer );
ai_node->get("bad-item-closeness", &m_bad_item_closeness_2 );
// We actually need the square of the distance later
m_bad_item_closeness_2 *= m_bad_item_closeness_2;
} // load
// ----------------------------------------------------------------------------
@ -56,6 +62,7 @@ void AIProperties::checkAllSet(const std::string &filename) const
CHECK_NEG(m_max_item_angle, "max-item-angle" );
CHECK_NEG(m_max_item_angle_high_speed, "max-item-angle-high-speed");
CHECK_NEG(m_time_full_steer, "time-full-steer" );
CHECK_NEG(m_bad_item_closeness_2, "bad-item-closeness" );
} // checkAllSet

View File

@ -53,6 +53,13 @@ protected:
* are more than this away, will not even be considered. */
float m_max_item_angle_high_speed;
/** If a good item and a bad item are closer than this distance, a good
* item will be avoided (in order to avoid the bad item). If the items
* are further apart, it is assumed that there is enough time to
* change steering direction.
*/
float m_bad_item_closeness_2;
/** Time for AI karts to reach full steer angle (used to reduce shaking
* of karts). */
float m_time_full_steer;

View File

@ -23,15 +23,15 @@
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#ifdef DEBUG
// Enable AeI graphical debugging
# undef AI_DEBUG
# define 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
# define AI_DEBUG_KART_HEADING
// Shows line from kart to its aim point
# undef AI_DEBUG_KART_AIM
# define AI_DEBUG_KART_AIM
#endif
#include "karts/controller/skidding_ai.hpp"
@ -561,6 +561,75 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
float kart_aim_angle = atan2(aim_point->getX()-m_kart->getXYZ().getX(),
aim_point->getZ()-m_kart->getXYZ().getZ());
// Make sure we have a valid last_node
if(last_node==QuadGraph::UNKNOWN_SECTOR)
last_node = m_next_node_index[m_track_node];
int node = m_track_node;
float distance = 0;
std::vector<const Item *> items_to_collect;
std::vector<const Item *> items_to_avoid;
const float max_item_lookahead_distance = 30.f;
while(distance < max_item_lookahead_distance)
{
int q_index= QuadGraph::get()->getNode(node).getQuadIndex();
const std::vector<Item *> &items_ahead =
ItemManager::get()->getItemsInQuads(q_index);
for(unsigned int i=0; i<items_ahead.size(); i++)
{
evaluateItems(items_ahead[i], kart_aim_angle,
&items_to_avoid, &items_to_collect);
} // for i<items_ahead;
distance += QuadGraph::get()->getDistanceToNext(node,
m_successor_index[node]);
node = m_next_node_index[node];
// Stop when we have reached the last quad
if(node==last_node) break;
} // while (distance < max_item_lookahead_distance)
core::line2df line_to_target(aim_point->getX(),
aim_point->getZ(),
m_kart->getXYZ().getX(),
m_kart->getXYZ().getZ());
// If the kart is aiming for an item, but (suddenly) detects some
// clos- by items to avoid, the kart cancels collecting the item if
// this could cause the avoidance item to be collected
if(m_item_to_collect && items_to_avoid.size()>0)
{
for(unsigned int i=0; i< items_to_avoid.size(); i++)
{
Vec3 d = items_to_avoid[i]->getXYZ()-m_item_to_collect->getXYZ();
if( d.length2_2d()>m_ai_properties->m_bad_item_closeness_2)
continue;
// It could make sense to also test if the bad item would
// actually be hit. But in (at least) one case steering
// after collecting m_item_to_collect causes it to then
// collect the bad item (it's too close to avoid it at that
// time). So for now this test is actually disabled.
#undef ADDITIONAL_TEST
#ifdef ADDITIONAL_TEST
core::line2df to_item(m_item_to_collect->getXYZ().toIrrVector2d(),
m_kart->getXYZ().toIrrVector2d());
if(items_to_avoid[i]->hitLine(to_item))
{
// Since from now on bad items will be tested before good
// items, the item will not be picked again as a collection
// target (till the bad items behind the kart).
} // hitLine(to_item)
#endif
m_item_to_collect = NULL;
break;
} // for i<items_to_avoid.size()
} // if m_item_to_collect && items_to_avoid.size()>0
// The AI does a much better job of collecting items if after selecting
// an item it tries to collect this item even if it doesn't fulfill the
// original conditions to be selected in the first place anymore.
@ -590,40 +659,6 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
} // m_item_to_collect
// Make sure we have a valid last_node
if(last_node==QuadGraph::UNKNOWN_SECTOR)
last_node = m_next_node_index[m_track_node];
int node = m_track_node;
float distance = 0;
std::vector<const Item *> items_to_collect;
std::vector<const Item *> items_to_avoid;
const float max_item_lookahead_distance = 30.f;
while(distance < max_item_lookahead_distance)
{
int q_index= QuadGraph::get()->getNode(node).getQuadIndex();
const std::vector<Item *> &items_ahead =
ItemManager::get()->getItemsInQuads(q_index);
for(unsigned int i=0; i<items_ahead.size(); i++)
{
evaluateItems(items_ahead[i], kart_aim_angle,
&items_to_avoid, &items_to_collect);
} // for i<items_ahead;
distance += QuadGraph::get()->getDistanceToNext(node,
m_successor_index[node]);
node = m_next_node_index[node];
// Stop when we have reached the last quad
if(node==last_node) break;
} // while (distance < max_item_lookahead_distance)
core::line2df line_to_target(aim_point->getX(),
aim_point->getZ(),
m_kart->getXYZ().getX(),
m_kart->getXYZ().getZ());
if(items_to_avoid.size()>0)
{
// If we need to steer to avoid an item, this takes priority,
@ -641,11 +676,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
if(items_to_collect.size()>0)
{
const Item *item_to_collect = items_to_collect[0];
core::vector2df collect(item_to_collect->getXYZ().getX(),
item_to_collect->getXYZ().getZ());
core::vector2df cp = line_to_target.getClosestPoint(collect);
Vec3 xyz(cp.X, item_to_collect->getXYZ().getY(), cp.Y);
if(item_to_collect->hitKart(xyz, m_kart))
if(item_to_collect->hitLine(line_to_target, m_kart))
{
#ifdef AI_DEBUG
m_item_sphere->setVisible(true);
@ -796,14 +827,10 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
}
if(item_index>-1)
{
core::vector2df point =
items_to_avoid[item_index]->getXYZ().toIrrVector2d();
// Even though we are on the side, we must make sure
// that we don't hit that item
core::vector2df close = line_to_target.getClosestPoint(point, true);
Vec3 close3d(close.X, m_kart->getXYZ().getY(), close.Y);
// If we don't hit the item on the side, no more tests are necessary
if(!items_to_avoid[item_index]->hitKart(close3d, m_kart))
if(!items_to_avoid[item_index]->hitLine(line_to_target, m_kart))
return false;
// See if we can avoid this item by driving further to the side