@@ -230,16 +230,16 @@ Vec3 SoccerAI::determineBallAimingPosition()
230230
231231 if (m_overtake_ball)
232232 {
233- Vec3 overtake_wc ;
234- const bool can_overtake = determineOvertakePosition (ball_lc, ball_pos ,
235- &overtake_wc );
233+ Vec3 overtake_lc ;
234+ const bool can_overtake = determineOvertakePosition (ball_lc, aim_lc ,
235+ ball_pos, &overtake_lc );
236236 if (!can_overtake)
237237 {
238238 m_overtake_ball = false ;
239239 return ball_aim_pos;
240240 }
241241 else
242- return overtake_wc ;
242+ return m_kart-> getTrans ()( Vec3 (overtake_lc)) ;
243243 }
244244 else
245245 {
@@ -248,11 +248,10 @@ Vec3 SoccerAI::determineBallAimingPosition()
248248 // is behind the ball , if so m_overtake_ball is true
249249 if (aim_lc.z () > 0 && aim_lc.z () > ball_lc.z ())
250250 {
251- const bool can_overtake = determineOvertakePosition (ball_lc,
252- ball_pos, NULL );
253- if (can_overtake)
251+ if (isOvertakable (ball_lc, ball_pos))
254252 {
255253 m_overtake_ball = true ;
254+ return ball_aim_pos;
256255 }
257256 else
258257 {
@@ -284,28 +283,13 @@ Vec3 SoccerAI::determineBallAimingPosition()
284283} // determineBallAimingPosition
285284
286285// -----------------------------------------------------------------------------
287- bool SoccerAI::determineOvertakePosition (const Vec3& ball_lc,
288- const posData& ball_pos,
289- Vec3* overtake_wc)
286+ bool SoccerAI::isOvertakable (const Vec3& ball_lc, const posData& ball_pos)
290287{
291- // This done by drawing a circle using the center of ball local coordinates
292- // and the distance / 2 from kart to ball center as radius (which allows more
293- // offset for overtaking), then find tangent line from kart (0, 0, 0) to the
294- // circle. The intercept point will be used as overtake position
295-
296288 // No overtake if ball is behind
297289 if (ball_lc.z () < 0 .0f ) return false ;
298290
299291 // Circle equation: (x-a)2 + (y-b)2 = r2
300292 const float r2 = (ball_pos.distance / 2 ) * (ball_pos.distance / 2 );
301-
302- // No overtake if sqrtf(r2) / 2 < ball radius,
303- // which will likely push to ball forward
304- if ((sqrtf (r2) / 2 ) < (m_world->getBallDiameter () / 2 ))
305- {
306- return false ;
307- }
308-
309293 const float a = ball_lc.x ();
310294 const float b = ball_lc.z ();
311295
@@ -316,6 +300,23 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
316300 {
317301 return false ;
318302 }
303+ return true ;
304+
305+ } // isOvertakable
306+
307+ // -----------------------------------------------------------------------------
308+ bool SoccerAI::determineOvertakePosition (const Vec3& ball_lc,
309+ const Vec3& aim_lc,
310+ const posData& ball_pos,
311+ Vec3* overtake_lc)
312+ {
313+ // This done by drawing a circle using the center of ball local coordinates
314+ // and the distance / 2 from kart to ball center as radius (which allows more
315+ // offset for overtaking), then find tangent line from kart (0, 0, 0) to the
316+ // circle. The intercept point will be used as overtake position
317+
318+ // Check if overtakable at current location
319+ if (!isOvertakable (ball_lc, ball_pos)) return false ;
319320
320321 // Otherwise calculate the tangent
321322 // As all are local coordinates, so center is 0,0 which is y = mx for the
@@ -329,56 +330,136 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
329330 // (E2 - 4F)m2 + (2DE)m + (D2 - 4F) = 0
330331 // Now solve the above quadratic equation using
331332 // x = -b (+/-) sqrt(b2 - 4ac) / 2a
333+
334+ // Circle equation: (x-a)2 + (y-b)2 = r2
335+ const float r = ball_pos.distance / 2 ;
336+ const float r2 = r * r;
337+ const float a = ball_lc.x ();
338+ const float b = ball_lc.z ();
339+
332340 const float d = -2 * a;
333341 const float e = -2 * b;
334342 const float f = (d * d / 4 ) + (e * e / 4 ) - r2;
335343 const float discriminant = (2 * 2 * d * d * e * e) -
336344 (4 * ((e * e) - (4 * f)) * ((d * d) - (4 * f)));
337345
338346 assert (discriminant > 0 .0f );
339- const float slope_1 = (-(2 * d * e) + sqrtf (discriminant)) /
347+ float t_slope_1 = (-(2 * d * e) + sqrtf (discriminant)) /
340348 (2 * ((e * e) - (4 * f)));
341- const float slope_2 = (-(2 * d * e) - sqrtf (discriminant)) /
349+ float t_slope_2 = (-(2 * d * e) - sqrtf (discriminant)) /
342350 (2 * ((e * e) - (4 * f)));
343351
344- assert (!std::isnan (slope_1));
345- assert (!std::isnan (slope_2));
352+ assert (!std::isnan (t_slope_1));
353+ assert (!std::isnan (t_slope_2));
354+
355+ // Make the slopes in correct order, allow easier rotate later
356+ float slope_1 = 0 .0f ;
357+ float slope_2 = 0 .0f ;
358+ if ((t_slope_1 > 0 && t_slope_2 > 0 ) || (t_slope_1 < 0 && t_slope_2 < 0 ))
359+ {
360+ if (t_slope_1 > t_slope_2)
361+ {
362+ slope_1 = t_slope_1;
363+ slope_2 = t_slope_2;
364+ }
365+ else
366+ {
367+ slope_1 = t_slope_2;
368+ slope_2 = t_slope_1;
369+ }
370+ }
371+ else
372+ {
373+ if (t_slope_1 > t_slope_2)
374+ {
375+ slope_1 = t_slope_2;
376+ slope_2 = t_slope_1;
377+ }
378+ else
379+ {
380+ slope_1 = t_slope_1;
381+ slope_2 = t_slope_2;
382+ }
383+ }
346384
347385 // Calculate two intercept points, as we already put y=mx into circle
348386 // equation and know that only one root for each slope, so x can be
349387 // calculated easily with -b / 2a
350388 // From (1+m2)x2 + (D+Em)x +F = 0:
351389 const float x1 = -(d + (e * slope_1)) / (2 * (1 + (slope_1 * slope_1)));
352390 const float x2 = -(d + (e * slope_2)) / (2 * (1 + (slope_2 * slope_2)));
391+ const float y1 = slope_1 * x1;
392+ const float y2 = slope_2 * x2;
353393
354- // Use the closest point to aim
355- float x = std::min ( fabsf (x1), fabsf (x2) );
356- float y = 0 . 0f ;
357- if (-x == x1)
358- {
359- // x was negative
360- x = -x;
361- y = slope_1 * x ;
362- }
363- else if (x == x1)
364- {
365- y = slope_1 * x;
366- }
367- else if (-x == x2 )
394+ const Vec3 point1 (x1, 0 , y1);
395+ const Vec3 point2 (x2, 0 , y2 );
396+
397+ const float d1 = (point1 - aim_lc). length_2d ();
398+ const float d2 = (point2 - aim_lc). length_2d ();
399+
400+ // Use the tangent closest to the ball aiming position to aim
401+ const bool use_tangent_one = d1 < d2 ;
402+
403+ // Adjust x and y if r < ball diameter,
404+ // which will likely push to ball forward
405+ // Notice: we cannot increase the radius before, as the kart location
406+ // will likely lie inside the enlarged circle
407+ if (r < m_world-> getBallDiameter () )
368408 {
369- x = -x;
370- y = slope_2 * x;
409+ // Constuctor a equation using y = (rotateSlope(old_m)) x which is
410+ // a less steep or steeper line, and find out the new adjusted position
411+ // using the distance to the original point * 2 at new line
412+
413+ // Determine if the circle is drawn around the side of kart
414+ // ie point1 or point2 z() < 0, if so reverse the below logic
415+ const float m = ((point1.z () < 0 || point2.z () < 0 ) ?
416+ (use_tangent_one ? rotateSlope (slope_1, false /* rotate_up*/ ) :
417+ rotateSlope (slope_2, true /* rotate_up*/ )) :
418+ (use_tangent_one ? rotateSlope (slope_1, true /* rotate_up*/ ) :
419+ rotateSlope (slope_2, false /* rotate_up*/ )));
420+
421+ // Calculate new distance from kart to new adjusted position
422+ const float dist = (use_tangent_one ? point1 : point2).length_2d () * 2 ;
423+ const float dist2 = dist * dist;
424+
425+ // x2 + y2 = dist2
426+ // so y = m * sqrtf (dist2 - y2)
427+ // y = sqrtf(m2 * dist2 / (1 + m2))
428+ const float y = sqrtf ((m * m * dist2) / (1 + (m * m)));
429+ const float x = y / m;
430+ *overtake_lc = Vec3 (x, 0 , y);
371431 }
372432 else
373433 {
374- y = slope_2 * x;
434+ // Use the calculated position depends on distance to aim position
435+ if (use_tangent_one)
436+ *overtake_lc = point1;
437+ else
438+ *overtake_lc = point2;
375439 }
376440
377- if (overtake_wc)
378- *overtake_wc = m_kart->getTrans ()(Vec3 (x, 0 , y));
379441 return true ;
380442} // determineOvertakePosition
381443
444+ // -----------------------------------------------------------------------------
445+ float SoccerAI::rotateSlope (float old_slope, bool rotate_up)
446+ {
447+ const float theta = atan (old_slope) + (old_slope < 0 ? M_PI : 0 );
448+ float new_theta = theta + (rotate_up ? M_PI / 6 : -M_PI /6 );
449+ if (new_theta > ((M_PI / 2 ) - 0 .02f ) && new_theta < ((M_PI / 2 ) + 0 .02f ))
450+ {
451+ // Avoid almost tan 90
452+ new_theta = (M_PI / 2 ) - 0 .02f ;
453+ }
454+ // Check if over-rotated
455+ if (new_theta > M_PI)
456+ new_theta = M_PI - 0 .1f ;
457+ else if (new_theta < 0 )
458+ new_theta = 0 .1f ;
459+
460+ return tan (new_theta);
461+ } // rotateSlope
462+
382463// -----------------------------------------------------------------------------
383464int SoccerAI::getCurrentNode () const
384465{
0 commit comments