Use math formula to determine overtake position

This commit is contained in:
Benau
2016-05-21 00:37:11 +08:00
parent a94fc6c1c3
commit 0f9438d824
6 changed files with 162 additions and 61 deletions

View File

@@ -50,6 +50,7 @@ void ArenaAI::reset()
m_cur_kart_pos_data = {0};
m_is_stuck = false;
m_is_uturn = false;
m_avoid_eating_banana = false;
m_target_point = Vec3(0, 0, 0);
m_time_since_last_shot = 0.0f;
m_time_since_driving = 0.0f;
@@ -159,6 +160,7 @@ void ArenaAI::checkIfStuck(const float dt)
*/
void ArenaAI::handleArenaAcceleration(const float dt)
{
if (m_controls->m_brake)
{
m_controls->m_accel = 0.0f;
@@ -239,9 +241,10 @@ void ArenaAI::handleArenaSteering(const float dt)
if (current_node == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
if (m_target_node == current_node)
if (m_target_node == current_node || directDrive())
{
// Very close to the item, steer directly
m_path_corners.clear();
checkPosition(m_target_point, &m_cur_kart_pos_data);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_target_point.toIrrVector());
@@ -306,6 +309,8 @@ void ArenaAI::handleArenaSteering(const float dt)
//-----------------------------------------------------------------------------
void ArenaAI::handleArenaBanana()
{
m_avoid_eating_banana = false;
if (m_is_uturn) return;
const std::vector< std::pair<const Item*, int> >& item_list =
@@ -327,10 +332,7 @@ void ArenaAI::handleArenaBanana()
banana_lc = (banana_pos.lhs ? banana_lc + Vec3 (2, 0, 0) :
banana_lc - Vec3 (2, 0, 0));
m_target_point = m_kart->getTrans()(banana_lc);
m_target_node = BattleGraph::get()
->pointToNode(getCurrentNode(), m_target_point,
false/*ignore_vertical*/);
m_avoid_eating_banana = true;
// Handle one banana only
break;
}
@@ -504,6 +506,7 @@ void ArenaAI::handleArenaBraking()
if (forceBraking() && m_kart->getSpeed() > MIN_SPEED)
{
// Brake now
m_controls->m_brake = true;
return;
}
@@ -512,6 +515,8 @@ void ArenaAI::handleArenaBraking()
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
if (m_path_corners.empty()) return;
float current_curve_radius = determineTurnRadius(m_kart->getXYZ(),
m_path_corners[0], (m_path_corners.size() >= 2 ? m_path_corners[1] :
m_path_corners[0]));

View File

@@ -64,6 +64,9 @@ protected:
/** The target point. */
Vec3 m_target_point;
/** For directDrive() to work */
bool m_avoid_eating_banana;
void collectItemInArena(Vec3*, int*) const;
private:
/** Used by handleArenaUTurn, it tells whether to do left or right
@@ -121,7 +124,8 @@ private:
virtual void resetAfterStop() {};
virtual void findClosestKart(bool use_difficulty) = 0;
virtual void findTarget() = 0;
virtual bool forceBraking() { return false; }
virtual bool forceBraking() { return false; }
virtual bool directDrive() { return m_avoid_eating_banana; }
public:
ArenaAI(AbstractKart *kart);
virtual ~ArenaAI() {};

View File

@@ -99,6 +99,8 @@ void SoccerAI::reset()
m_overtake_ball = false;
m_force_brake = false;
m_steer_with_ball = false;
} // reset
//-----------------------------------------------------------------------------
@@ -111,6 +113,7 @@ void SoccerAI::update(float dt)
m_blue_sphere->setPosition(blue.toIrrVector());
#endif
m_force_brake = false;
m_steer_with_ball = false;
if (World::getWorld()->getPhase() == World::GOAL_PHASE)
{
@@ -212,6 +215,7 @@ Vec3 SoccerAI::determineBallAimingPosition()
const Vec3& ball_aim_pos = m_world->getBallAimPosition(m_opp_team);
const Vec3& orig_pos = m_world->getBallPosition();
const float ball_diameter = m_world->getBallDiameter();
posData ball_pos = {0};
posData aim_pos = {0};
Vec3 ball_lc;
@@ -222,66 +226,53 @@ Vec3 SoccerAI::determineBallAimingPosition()
// Too far from the ball,
// use path finding from arena ai to get close
// ie no extra braking is needed
if (aim_pos.distance > 6.0f) return ball_aim_pos;
if (aim_pos.distance > 10.0f) return ball_aim_pos;
if (m_overtake_ball)
{
// Check if the kart passed the ball already,
// If so aim the front side of ball
if (ball_pos.behind)
Vec3 overtake_wc;
const bool can_overtake = determineOvertakePosition(ball_lc, ball_pos,
&overtake_wc);
if (!can_overtake)
{
const Vec3& front_pos =
m_world->getBallAimPosition(m_opp_team, true/*reverse*/);
Vec3 d = front_pos - m_kart->getFrontXYZ();
if (d.length_2d() < (m_world->getBallDiameter() / 2))
{
// Almost arrive, reset
m_overtake_ball = false;
}
return front_pos;
m_overtake_ball = false;
return ball_aim_pos;
}
else
{
// Otherwise aim left/right depends on the side of ball
if (ball_pos.lhs)
{
return m_world->getBallTrans()
(Vec3(m_world->getBallDiameter(), 0, 0));
}
else
{
return m_world->getBallTrans()
(Vec3(-m_world->getBallDiameter(), 0, 0));
}
}
return overtake_wc;
}
else
{
// Check whether the aim point is non-reachable
// ie the ball is in front of the kart, which the aim position
// is behind the ball, in an almost straight line
// If so m_overtake_ball is true
if (aim_lc.z() > 0 && aim_lc.z() > ball_lc.z() &&
ball_pos.angle < 0.6f && aim_pos.angle < 0.2f)
// is behind the ball , if so m_overtake_ball is true
if (aim_lc.z() > 0 && aim_lc.z() > ball_lc.z())
{
m_overtake_ball = true;
const bool can_overtake = determineOvertakePosition(ball_lc,
ball_pos, NULL);
if (can_overtake)
{
m_overtake_ball = true;
}
else
{
// Stop a while to wait for overtaking, prevent own goal too
// Only do that if the ball is moving
if (!m_world->ballNotMoving())
m_force_brake = true;
return ball_aim_pos;
}
}
// Otherwise use the aim position calculated by soccer world
// Prevent lost control when steering with ball
const bool need_braking = ball_pos.angle > 0.15f &&
m_kart->getSpeed() > 9.0f &&
ball_pos.distance < m_world->getBallDiameter();
m_force_brake = ball_pos.angle > 0.15f &&
m_kart->getSpeed() > 9.0f && ball_pos.distance < ball_diameter;
if (need_braking)
{
m_controls->m_brake = true;
m_force_brake = true;
}
if (aim_pos.behind && aim_pos.distance <
(m_world->getBallDiameter() / 2))
if (aim_pos.behind && aim_pos.distance < (ball_diameter / 2))
{
// Reached aim point, aim forward
m_steer_with_ball = true;
return m_world->getBallAimPosition(m_opp_team, true/*reverse*/);
}
return ball_aim_pos;
@@ -292,6 +283,102 @@ Vec3 SoccerAI::determineBallAimingPosition()
} // determineBallAimingPosition
//-----------------------------------------------------------------------------
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
const posData& ball_pos,
Vec3* overtake_wc)
{
// This done by drawing a circle using the center of ball local coordinates
// and the distance / 2 from kart to ball center as radius (which allows more
// offset for overtaking), then find tangent line from kart (0, 0, 0) to the
// circle. The intercept point will be used as overtake position
// No overtake if ball is behind
if (ball_lc.z() < 0.0f) return false;
// Circle equation: (x-a)2 + (y-b)2 = r2
const float r2 = (ball_pos.distance / 2) * (ball_pos.distance / 2);
// No overtake if sqrtf(r2) / 2 < ball radius,
// which will likely push to ball forward
if ((sqrtf(r2) / 2) < (m_world->getBallDiameter() / 2))
{
return false;
}
const float a = ball_lc.x();
const float b = ball_lc.z();
// Check first if the kart is lies inside the circle, if so no tangent
// can be drawn ( so can't overtake), minus 0.1 as epslion
const float test_radius_2 = ((a * a) + (b * b)) - 0.1f;
if (test_radius_2 < r2)
{
return false;
}
// Otherwise calculate the tangent
// As all are local coordinates, so center is 0,0 which is y = mx for the
// tangent equation, and the m (slope) can be calculated by puting y = mx
// into the general form of circle equation x2 + y2 + Dx + Ey + F = 0
// This means: x2 + m2x2 + Dx + Emx + F = 0
// (1+m2)x2 + (D+Em)x +F = 0
// As only one root for it, so discriminant b2 - 4ac = 0
// So: (D+Em)2 - 4(1+m2)(F) = 0
// D2 + 2DEm +E2m2 - 4F - 4m2F = 0
// (E2 - 4F)m2 + (2DE)m + (D2 - 4F) = 0
// Now solve the above quadratic equation using
// x = -b (+/-) sqrt(b2 - 4ac) / 2a
const float d = -2 * a;
const float e = -2 * b;
const float f = (d * d / 4) + (e * e / 4) - r2;
const float discriminant = (2 * 2 * d * d * e * e) -
(4 * ((e * e) - (4 * f)) * ((d * d) - (4 * f)));
assert(discriminant > 0.0f);
const float slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
const float slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
assert(!std::isnan(slope_1));
assert(!std::isnan(slope_2));
// Calculate two intercept points, as we already put y=mx into circle
// equation and know that only one root for each slope, so x can be
// calculated easily with -b / 2a
// From (1+m2)x2 + (D+Em)x +F = 0:
const float x1 = -(d + (e * slope_1)) / (2 * (1 + (slope_1 * slope_1)));
const float x2 = -(d + (e * slope_2)) / (2 * (1 + (slope_2 * slope_2)));
// Use the closest point to aim
float x = std::min(fabsf(x1), fabsf(x2));
float y = 0.0f;
if (-x == x1)
{
// x was negative
x = -x;
y = slope_1 * x;
}
else if (x == x1)
{
y = slope_1 * x;
}
else if (-x == x2)
{
x = -x;
y = slope_2 * x;
}
else
{
y = slope_2 * x;
}
if (overtake_wc)
*overtake_wc = m_kart->getTrans()(Vec3(x, 0, y));
return true;
} // determineOvertakePosition
//-----------------------------------------------------------------------------
int SoccerAI::getCurrentNode() const
{

View File

@@ -52,16 +52,24 @@ private:
*/
bool m_overtake_ball;
bool m_force_brake;
bool m_steer_with_ball;
Vec3 determineBallAimingPosition();
bool determineOvertakePosition(const Vec3& ball_lc,
const posData& ball_pos, Vec3* overtake_wc);
virtual void findClosestKart(bool use_difficulty);
virtual void findTarget();
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
virtual int getCurrentNode() const;
virtual bool isWaiting() const;
virtual bool canSkid(float steer_fraction) { return false; }
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
virtual bool canSkid(float steer_fraction) { return false; }
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
virtual bool directDrive() OVERRIDE
{
return m_avoid_eating_banana || m_overtake_ball || m_steer_with_ball;
}
public:
SoccerAI(AbstractKart *kart);
~SoccerAI();

View File

@@ -427,8 +427,7 @@ void SoccerWorld::updateBallPosition(float dt)
{
if (isRaceOver()) return;
if (!(m_ball_body->getLinearVelocity().x() == 0.0f ||
m_ball_body->getLinearVelocity().z() == 0.0f))
if (!ballNotMoving())
{
// Only update heading if the ball is moving
m_ball_heading = atan2f(m_ball_body->getLinearVelocity().getX(),

View File

@@ -114,15 +114,10 @@ private:
m_trans = btTransform(btQuaternion(0, 0, 0, 1), Vec3(0, 0, 0));
} // reset
const btTransform& getTrans() const
{
return m_trans;
} // getTrans
float getDiameter() const
{
return m_radius * 2;
} // getTrans
} // getDiameter
void init(float ball_radius)
{
@@ -365,6 +360,12 @@ public:
const Vec3& getBallPosition() const
{ return (Vec3&)m_ball_body->getCenterOfMassTransform().getOrigin(); }
// ------------------------------------------------------------------------
bool ballNotMoving() const
{
return (m_ball_body->getLinearVelocity().x() == 0.0f ||
m_ball_body->getLinearVelocity().z() == 0.0f);
}
// ------------------------------------------------------------------------
float getBallHeading() const
{ return m_ball_heading; }
// ------------------------------------------------------------------------
@@ -377,9 +378,6 @@ public:
Vec3 getBallAimPosition(SoccerTeam team, bool reverse = false) const
{ return m_bgd.getAimPosition(team, reverse); }
// ------------------------------------------------------------------------
const btTransform& getBallTrans() const
{ return m_bgd.getTrans(); }
// ------------------------------------------------------------------------
bool isCorrectGoal(unsigned int kart_id, bool first_goal) const;
// ------------------------------------------------------------------------
int getBallChaser(SoccerTeam team) const