Add more defense

This commit is contained in:
Benau 2016-05-24 15:48:38 +08:00
parent ae18983655
commit a18c53f4e7
2 changed files with 131 additions and 48 deletions

View File

@ -230,16 +230,16 @@ Vec3 SoccerAI::determineBallAimingPosition()
if (m_overtake_ball)
{
Vec3 overtake_wc;
const bool can_overtake = determineOvertakePosition(ball_lc, ball_pos,
&overtake_wc);
Vec3 overtake_lc;
const bool can_overtake = determineOvertakePosition(ball_lc, aim_lc,
ball_pos, &overtake_lc);
if (!can_overtake)
{
m_overtake_ball = false;
return ball_aim_pos;
}
else
return overtake_wc;
return m_kart->getTrans()(Vec3(overtake_lc));
}
else
{
@ -248,11 +248,10 @@ Vec3 SoccerAI::determineBallAimingPosition()
// is behind the ball , if so m_overtake_ball is true
if (aim_lc.z() > 0 && aim_lc.z() > ball_lc.z())
{
const bool can_overtake = determineOvertakePosition(ball_lc,
ball_pos, NULL);
if (can_overtake)
if (isOvertakable(ball_lc, ball_pos))
{
m_overtake_ball = true;
return ball_aim_pos;
}
else
{
@ -284,28 +283,13 @@ Vec3 SoccerAI::determineBallAimingPosition()
} // determineBallAimingPosition
//-----------------------------------------------------------------------------
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
const posData& ball_pos,
Vec3* overtake_wc)
bool SoccerAI::isOvertakable(const Vec3& ball_lc, const posData& ball_pos)
{
// 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();
@ -316,6 +300,23 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
{
return false;
}
return true;
} // isOvertakable
//-----------------------------------------------------------------------------
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
const Vec3& aim_lc,
const posData& ball_pos,
Vec3* overtake_lc)
{
// 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
// Check if overtakable at current location
if (!isOvertakable(ball_lc, ball_pos)) return false;
// Otherwise calculate the tangent
// As all are local coordinates, so center is 0,0 which is y = mx for the
@ -329,6 +330,13 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
// (E2 - 4F)m2 + (2DE)m + (D2 - 4F) = 0
// Now solve the above quadratic equation using
// x = -b (+/-) sqrt(b2 - 4ac) / 2a
// Circle equation: (x-a)2 + (y-b)2 = r2
const float r = ball_pos.distance / 2;
const float r2 = r * r;
const float a = ball_lc.x();
const float b = ball_lc.z();
const float d = -2 * a;
const float e = -2 * b;
const float f = (d * d / 4) + (e * e / 4) - r2;
@ -336,13 +344,43 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
(4 * ((e * e) - (4 * f)) * ((d * d) - (4 * f)));
assert(discriminant > 0.0f);
const float slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
float t_slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
const float slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
float t_slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
(2 * ((e * e) - (4 * f)));
assert(!std::isnan(slope_1));
assert(!std::isnan(slope_2));
assert(!std::isnan(t_slope_1));
assert(!std::isnan(t_slope_2));
// Make the slopes in correct order, allow easier rotate later
float slope_1 = 0.0f;
float slope_2 = 0.0f;
if ((t_slope_1 > 0 && t_slope_2 > 0) || (t_slope_1 < 0 && t_slope_2 < 0))
{
if (t_slope_1 > t_slope_2)
{
slope_1 = t_slope_1;
slope_2 = t_slope_2;
}
else
{
slope_1 = t_slope_2;
slope_2 = t_slope_1;
}
}
else
{
if (t_slope_1 > t_slope_2)
{
slope_1 = t_slope_2;
slope_2 = t_slope_1;
}
else
{
slope_1 = t_slope_1;
slope_2 = t_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
@ -350,35 +388,78 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
// 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)));
const float y1 = slope_1 * x1;
const float y2 = slope_2 * x2;
// Use the closest point to aim
float x = std::min(fabsf(x1), fabsf(x2));
float y = 0.0f;
if (-x == x1)
const Vec3 point1(x1, 0, y1);
const Vec3 point2(x2, 0, y2);
const float d1 = (point1 - aim_lc).length_2d();
const float d2 = (point2 - aim_lc).length_2d();
// Use the tangent closest to the ball aiming position to aim
const bool use_tangent_one = d1 < d2;
// Adjust x and y if r < ball diameter,
// which will likely push to ball forward
// Notice: we cannot increase the radius before, as the kart location
// will likely lie inside the enlarged circle
if (r < m_world->getBallDiameter())
{
// 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;
// Constuctor a equation using y = (rotateSlope(old_m)) x which is
// a less steep or steeper line, and find out the new adjusted position
// using the distance to the original point * 2 at new line
// Determine if the circle is drawn around the side of kart
// ie point1 or point2 z() < 0, if so reverse the below logic
const float m = ((point1.z() < 0 || point2.z() < 0) ?
(use_tangent_one ? rotateSlope(slope_1, false/*rotate_up*/) :
rotateSlope(slope_2, true/*rotate_up*/)) :
(use_tangent_one ? rotateSlope(slope_1, true/*rotate_up*/) :
rotateSlope(slope_2, false/*rotate_up*/)));
// Calculate new distance from kart to new adjusted position
const float dist = (use_tangent_one ? point1 : point2).length_2d() * 2;
const float dist2 = dist * dist;
// x2 + y2 = dist2
// so y = m * sqrtf (dist2 - y2)
// y = sqrtf(m2 * dist2 / (1 + m2))
const float y = sqrtf((m * m * dist2) / (1 + (m * m)));
const float x = y / m;
*overtake_lc = Vec3(x, 0, y);
}
else
{
y = slope_2 * x;
// Use the calculated position depends on distance to aim position
if (use_tangent_one)
*overtake_lc = point1;
else
*overtake_lc = point2;
}
if (overtake_wc)
*overtake_wc = m_kart->getTrans()(Vec3(x, 0, y));
return true;
} // determineOvertakePosition
//-----------------------------------------------------------------------------
float SoccerAI::rotateSlope(float old_slope, bool rotate_up)
{
const float theta = atan(old_slope) + (old_slope < 0 ? M_PI : 0);
float new_theta = theta + (rotate_up ? M_PI / 6 : -M_PI /6);
if (new_theta > ((M_PI / 2) - 0.02f) && new_theta < ((M_PI / 2) + 0.02f))
{
// Avoid almost tan 90
new_theta = (M_PI / 2) - 0.02f;
}
// Check if over-rotated
if (new_theta > M_PI)
new_theta = M_PI - 0.1f;
else if (new_theta < 0)
new_theta = 0.1f;
return tan(new_theta);
} // rotateSlope
//-----------------------------------------------------------------------------
int SoccerAI::getCurrentNode() const
{

View File

@ -55,8 +55,10 @@ private:
bool m_steer_with_ball;
Vec3 determineBallAimingPosition();
bool determineOvertakePosition(const Vec3& ball_lc,
const posData& ball_pos, Vec3* overtake_wc);
bool isOvertakable(const Vec3& ball_lc, const posData& ball_pos);
bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc,
const posData& ball_pos, Vec3* overtake_lc);
float rotateSlope(float old_slope, bool rotate_up);
virtual void findClosestKart(bool use_difficulty);
virtual void findTarget();