Try to fix lost control for soccer ai

This commit is contained in:
Benau 2016-05-10 08:53:33 +08:00
parent 58f7424b4f
commit 9624d27f59
11 changed files with 180 additions and 132 deletions

View File

@ -289,17 +289,18 @@ void AIBaseController::checkPosition(const Vec3 &point,
{
// Convert to local coordinates from the point of view of current kart
btQuaternion q(btVector3(0, 1, 0), -m_kart->getHeading());
Vec3 p = point - m_kart->getXYZ();
Vec3 p = point - m_kart->getXYZ();
Vec3 local_coordinates = quatRotate(q, p);
// Save local coordinates for later use if needed
if (lc) *lc = local_coordinates;
// on_side: tell whether it's left or right hand side
if (pos_data == NULL) return;
// lhs: tell whether it's left or right hand side
if (local_coordinates.getX() < 0)
pos_data->on_side = true;
pos_data->lhs = true;
else
pos_data->on_side = false;
pos_data->lhs = false;
// behind: tell whether it's behind or not
if (local_coordinates.getZ() < 0)

View File

@ -65,7 +65,7 @@ protected:
static int m_test_ai;
/** Position info structure of targets. */
struct posData {bool behind; bool on_side; float angle; float distance;};
struct posData {bool behind; bool lhs; float angle; float distance;};
void setControllerName(const std::string &name);
float steerToPoint(const Vec3 &point);

View File

@ -243,7 +243,7 @@ void ArenaAI::handleArenaSteering(const float dt)
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_adjusting_side = m_cur_kart_pos_data.lhs;
m_is_uturn = true;
}
else
@ -278,7 +278,7 @@ void ArenaAI::handleArenaSteering(const float dt)
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_adjusting_side = m_cur_kart_pos_data.lhs;
m_is_uturn = true;
}
else
@ -318,7 +318,7 @@ void ArenaAI::handleArenaBanana()
{
// Check whether it's straight ahead towards a banana
// If so, adjust target point
banana_lc = (banana_pos.on_side ? banana_lc + Vec3 (2, 0, 0) :
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()
@ -490,16 +490,22 @@ void ArenaAI::stringPull(const Vec3& start_pos, const Vec3& end_pos)
*/
void ArenaAI::handleArenaBraking()
{
m_controls->m_brake = false;
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
// A kart will not brake when the speed is already slower than this
// value. This prevents a kart from going too slow (or even backwards)
// in tight curves.
const float MIN_SPEED = 5.0f;
if (forceBraking() && m_kart->getSpeed() > MIN_SPEED)
{
// Brake now
return;
}
m_controls->m_brake = false;
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) 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

@ -120,6 +120,7 @@ private:
virtual bool isWaiting() const = 0;
virtual void findClosestKart(bool use_difficulty) = 0;
virtual void findTarget() = 0;
virtual bool forceBraking() { return false; }
public:
ArenaAI(AbstractKart *kart);
virtual ~ArenaAI() {};

View File

@ -161,12 +161,12 @@ void BattleAI::findTarget()
}
} // findTarget
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int BattleAI::getCurrentNode() const
{
return m_world->getKartNode(m_kart->getWorldKartId());
} // getCurrentNode
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool BattleAI::isWaiting() const
{
return m_world->isStartPhase();

View File

@ -76,12 +76,15 @@ void SoccerAI::reset()
AIBaseController::reset();
m_saving_ball = false;
m_force_brake = false;
} // reset
//-----------------------------------------------------------------------------
void SoccerAI::update(float dt)
{
m_saving_ball = false;
m_force_brake = false;
if (World::getWorld()->getPhase() == World::GOAL_PHASE)
{
m_controls->m_brake = false;
@ -133,8 +136,8 @@ void SoccerAI::findClosestKart(bool use_difficulty)
void SoccerAI::findTarget()
{
// Check whether any defense is needed
if ((m_world->getBallPosition() - NavMesh::get()->getNavPoly(m_world
->getGoalNode(m_cur_team)).getCenter()).length_2d() < 50.0f &&
if ((m_world->getBallPosition() - m_world->getGoalLocation(m_cur_team,
CheckGoal::POINT_CENTER)).length_2d() < 50.0f &&
m_world->getDefender(m_cur_team) == (signed)m_kart->getWorldKartId())
{
m_target_node = m_world->getBallNode();
@ -160,86 +163,110 @@ Vec3 SoccerAI::correctBallPosition(const Vec3& orig_pos)
{
// Notice: Build with AI_DEBUG and change camera target to an AI kart,
// to debug or see how AI steer with the ball
posData ball_pos = {0};
posData goal_pos = {0};
Vec3 ball_lc(0, 0, 0);
Vec3 ball_lc;
checkPosition(orig_pos, &ball_pos, &ball_lc);
// opposite team goal
checkPosition(NavMesh::get()->getNavPoly(m_world
->getGoalNode(m_cur_team == SOCCER_TEAM_BLUE ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE)).getCenter(), &goal_pos);
// Too far / behind from the ball,
// use path finding from arena ai to get close
if (!(ball_pos.distance < 3.0f) || ball_pos.behind) return orig_pos;
if (goal_pos.behind)
// Save the opposite team
SoccerTeam opp_team = (m_cur_team == SOCCER_TEAM_BLUE ?
SOCCER_TEAM_RED : SOCCER_TEAM_BLUE);
// Prevent lost control when steering with ball
const bool need_braking = ball_pos.angle > 0.1f &&
m_kart->getSpeed() > 9.0f && ball_pos.distance <2.5f;
// Goal / own goal detection first, as different aiming method will be used
const bool likely_to_goal =
ball_pos.angle < 0.2f && isLikelyToGoal(opp_team);
if (likely_to_goal)
{
if (goal_pos.angle > 0.3f && ball_pos.distance < 3.0f &&
!ball_pos.behind)
if (need_braking)
{
// Only steer with ball if same sides for ball and goal
if (ball_pos.on_side && goal_pos.on_side)
{
ball_lc = ball_lc + Vec3 (1, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else if (!ball_pos.on_side && !goal_pos.on_side)
{
ball_lc = ball_lc - Vec3 (1, 0, 0) + Vec3 (0, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else
m_controls->m_brake = true;
}
else
{
// This case is facing straight ahead opposite goal
// (which is straight behind itself), apply more
// offset for skidding, to save the ball from behind
// scored.
// Notice: this assume map maker make soccer field
// with two goals facing each other straight
ball_lc = (goal_pos.on_side ? ball_lc - Vec3 (2, 0, 0) +
Vec3 (0, 0, 2) : ball_lc + Vec3 (2, 0, 2));
if (ball_pos.distance < 3.0f &&
(m_cur_difficulty == RaceManager::DIFFICULTY_HARD ||
m_cur_difficulty == RaceManager::DIFFICULTY_BEST))
m_saving_ball = true;
return m_kart->getTrans()(ball_lc);
m_controls->m_brake = true;
m_force_brake = true;
// Prevent keep pushing by aiming a nearer point
return m_kart->getTrans()(ball_lc - Vec3(0, 0, 1));
}
// Keep pushing the ball straight to the goal
return orig_pos;
}
if (ball_pos.distance < 3.0f &&
!ball_pos.behind && !goal_pos.behind)
const bool likely_to_own_goal =
ball_pos.angle < 0.2f && isLikelyToGoal(m_cur_team);
if (likely_to_own_goal)
{
if (goal_pos.angle < 0.5f)
return orig_pos;
else
{
// Same with above
if (ball_pos.on_side && goal_pos.on_side)
{
ball_lc = ball_lc + Vec3 (1, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else if (!ball_pos.on_side && !goal_pos.on_side)
{
ball_lc = ball_lc - Vec3 (1, 0, 0) + Vec3 (0, 0, 1);
return m_kart->getTrans()(ball_lc);
}
else
m_controls->m_brake = true;
}
// It's getting likely to own goal, apply more
// offset for skidding, to save the ball from behind
// scored.
if (m_cur_difficulty == RaceManager::DIFFICULTY_HARD ||
m_cur_difficulty == RaceManager::DIFFICULTY_BEST)
m_saving_ball = true;
return m_kart->getTrans()(ball_pos.lhs ?
ball_lc - Vec3(2, 0, 0) + Vec3(0, 0, 2) :
ball_lc + Vec3(2, 0, 2));
}
return orig_pos;
// Now try to make the ball face towards the goal
// Aim at upper/lower left/right corner of ball depends on location
posData goal_pos = {0};
checkPosition(m_world->getGoalLocation(opp_team, CheckGoal::POINT_CENTER),
&goal_pos);
Vec3 corrected_pos;
if (ball_pos.lhs && goal_pos.lhs)
{
corrected_pos = ball_lc + Vec3(1, 0, 1);
}
else if (!ball_pos.lhs && !goal_pos.lhs)
{
corrected_pos = ball_lc - Vec3(1, 0, 0) + Vec3(0, 0, 1);
}
else if (!ball_pos.lhs && goal_pos.lhs)
{
corrected_pos = ball_lc + Vec3(1, 0, 0) - Vec3(0, 0, 1);
}
else
{
corrected_pos = ball_lc - Vec3(1, 0, 1);
}
if (need_braking)
{
m_controls->m_brake = true;
m_force_brake = true;
}
return m_kart->getTrans()(corrected_pos);
} // correctBallPosition
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SoccerAI::isLikelyToGoal(SoccerTeam team) const
{
// Use local coordinate for easy compare
Vec3 first_pos;
Vec3 last_pos;
checkPosition(m_world->getGoalLocation(team, CheckGoal::POINT_FIRST),
NULL, &first_pos);
checkPosition(m_world->getGoalLocation(team, CheckGoal::POINT_LAST),
NULL, &last_pos);
// If the kart lies between the first and last pos, and faces
// in front of them, than it's likely to goal
if ((first_pos.z() > 0.0f && last_pos.z() > 0.0f) &&
((first_pos.x() < 0.0f && last_pos.x() > 0.0f) ||
(last_pos.x() < 0.0f && first_pos.x() > 0.0f)))
return true;
return false;
} // isLikelyToGoal
//-----------------------------------------------------------------------------
int SoccerAI::getCurrentNode() const
{
return m_world->getKartNode(m_kart->getWorldKartId());
} // getCurrentNode
// ------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SoccerAI::isWaiting() const
{
return m_world->isStartPhase();

View File

@ -36,14 +36,17 @@ private:
SoccerTeam m_cur_team;
bool m_saving_ball;
bool m_force_brake;
Vec3 correctBallPosition(const Vec3&);
bool isLikelyToGoal(SoccerTeam team) const;
virtual void findClosestKart(bool use_difficulty);
virtual void findTarget();
virtual int getCurrentNode() const;
virtual bool isWaiting() const;
virtual bool canSkid(float steer_fraction) { return m_saving_ball; }
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
public:
SoccerAI(AbstractKart *kart);
~SoccerAI();

View File

@ -30,7 +30,6 @@
#include "karts/controller/soccer_ai.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/check_goal.hpp"
#include "tracks/check_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
@ -78,8 +77,11 @@ void SoccerWorld::init()
m_display_rank = false;
m_goal_timer = 0.0f;
m_ball_hitter = -1;
m_red_check_goal = NULL;
m_blue_check_goal = NULL;
m_goal_target = race_manager->getMaxGoal();
m_goal_sound = SFXManager::get()->createSoundSource("goal_scored");
initGoal();
} // init
@ -131,8 +133,7 @@ void SoccerWorld::reset()
}
initKartList();
resetAllNodes();
initGoalNodes();
resetAllPosition();
resetBall();
} // reset
@ -474,9 +475,7 @@ void SoccerWorld::updateBallPosition(float dt)
} // updateBallPosition
//-----------------------------------------------------------------------------
/** Localize two goals on the navigation mesh.
*/
void SoccerWorld::initGoalNodes()
void SoccerWorld::initGoal()
{
if (!m_track->hasNavMesh()) return;
@ -490,20 +489,18 @@ void SoccerWorld::initGoalNodes()
{
if (goal->getTeam())
{
m_blue_goal_node = BattleGraph::get()->pointToNode(m_blue_goal_node,
goal->convertTo3DCenter(), true/*ignore_vertical*/);
m_blue_check_goal = goal;
}
else
{
m_red_goal_node = BattleGraph::get()->pointToNode(m_red_goal_node,
goal->convertTo3DCenter(), true/*ignore_vertical*/);
m_red_check_goal = goal;
}
}
}
} // initGoalNodes
} // initGoal
//-----------------------------------------------------------------------------
void SoccerWorld::resetAllNodes()
void SoccerWorld::resetAllPosition()
{
m_kart_on_node.clear();
m_kart_on_node.resize(m_karts.size());
@ -511,9 +508,7 @@ void SoccerWorld::resetAllNodes()
m_kart_on_node[n] = BattleGraph::UNKNOWN_POLY;
m_ball_on_node = BattleGraph::UNKNOWN_POLY;
m_ball_position = Vec3(0, 0, 0);
m_red_goal_node = BattleGraph::UNKNOWN_POLY;
m_blue_goal_node = BattleGraph::UNKNOWN_POLY;
} // resetAllNodes
} // resetAllPosition
//-----------------------------------------------------------------------------
SoccerTeam SoccerWorld::getKartTeam(unsigned int kart_id) const
{
@ -561,9 +556,8 @@ void SoccerWorld::updateDefenders()
getKartTeam(m_karts[i]->getWorldKartId()) != SOCCER_TEAM_RED)
continue;
Vec3 d = NavMesh::get()->getNavPoly(this
->getGoalNode(SOCCER_TEAM_RED)).getCenter()
- m_karts[i]->getXYZ();
Vec3 d = this->getGoalLocation(SOCCER_TEAM_RED,
CheckGoal::POINT_CENTER) - m_karts[i]->getXYZ();
if (d.length_2d() <= distance)
{
@ -583,9 +577,8 @@ void SoccerWorld::updateDefenders()
getKartTeam(m_karts[i]->getWorldKartId()) != SOCCER_TEAM_BLUE)
continue;
Vec3 d = NavMesh::get()->getNavPoly(this
->getGoalNode(SOCCER_TEAM_BLUE)).getCenter()
- m_karts[i]->getXYZ();
Vec3 d = this->getGoalLocation(SOCCER_TEAM_BLUE,
CheckGoal::POINT_CENTER) - m_karts[i]->getXYZ();
if (d.length_2d() <= distance)
{
@ -651,25 +644,33 @@ void SoccerWorld::setAITeam()
team == SOCCER_TEAM_BLUE ? blue_player++ : red_player++;
}
int avaliable_ai = total_karts - red_player - blue_player;
while (avaliable_ai > 0)
int available_ai = total_karts - red_player - blue_player;
while (available_ai > 0)
{
if ((m_red_ai + red_player) > (m_blue_ai + blue_player))
{
m_blue_ai++;
avaliable_ai--;
available_ai--;
}
else if ((m_blue_ai + blue_player) > (m_red_ai + red_player))
{
m_red_ai++;
avaliable_ai--;
available_ai--;
}
else if ((m_blue_ai + blue_player) == (m_red_ai + red_player))
{
blue_player > red_player ? m_red_ai++ : m_blue_ai++;
avaliable_ai--;
available_ai--;
}
}
Log::debug("SoccerWorld","blue AI: %d red AI: %d", m_blue_ai, m_red_ai);
} // setAITeam
//-----------------------------------------------------------------------------
const Vec3& SoccerWorld::getGoalLocation(SoccerTeam team,
CheckGoal::PointLocation point) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_check_goal->getPoint(point) :
m_red_check_goal->getPoint(point));
} // getGoalLocation

View File

@ -22,6 +22,7 @@
#include "modes/world_with_rank.hpp"
#include "states_screens/race_gui_base.hpp"
#include "karts/abstract_kart.hpp"
#include "tracks/check_goal.hpp"
#include <IMesh.h>
#include <string>
@ -85,8 +86,9 @@ private:
std::vector<int> m_kart_on_node;
int m_ball_on_node;
Vec3 m_ball_position;
int m_red_goal_node;
int m_blue_goal_node;
CheckGoal* m_red_check_goal;
CheckGoal* m_blue_check_goal;
int m_red_defender;
int m_blue_defender;
@ -96,14 +98,14 @@ private:
/** Set the team for the karts */
void initKartList();
/** Function to init the locations of two goals on the polygon map */
void initGoalNodes();
/** Function to save check goal for the getting of coordinate */
void initGoal();
/** Function to update the locations of all karts on the polygon map */
void updateKartNodes();
/** Function to update the location the ball on the polygon map */
void updateBallPosition(float dt);
/** Clean up */
void resetAllNodes();
void resetAllPosition();
/** Reset the ball to original starting position. */
void resetBall();
/** Function to update the AI which is the closest to its goal to defend. */
@ -169,11 +171,6 @@ public:
const Vec3& getBallPosition() const
{ return m_ball_position; }
// ------------------------------------------------------------------------
const int getGoalNode(SoccerTeam team) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_goal_node : m_red_goal_node);
}
// ------------------------------------------------------------------------
bool isCorrectGoal(unsigned int kart_id, bool first_goal) const;
// ------------------------------------------------------------------------
const int getDefender(SoccerTeam team) const
@ -182,6 +179,10 @@ public:
}
// ------------------------------------------------------------------------
void setAITeam();
// ------------------------------------------------------------------------
const Vec3& getGoalLocation(SoccerTeam team,
CheckGoal::PointLocation point) const;
// ------------------------------------------------------------------------
}; // SoccerWorld

View File

@ -37,12 +37,12 @@ CheckGoal::CheckGoal(const XMLNode &node, unsigned int index)
m_first_goal = false;
node.get("first_goal", &m_first_goal);
Vec3 p1, p2;
node.get("p1", &p1);
node.get("p2", &p2);
node.get("p1", &m_p1);
node.get("p2", &m_p3);
m_line.setLine( core::vector2df(p1.getX(), p1.getZ()),
core::vector2df(p2.getX(), p2.getZ()) );
m_line.setLine( core::vector2df(m_p1.getX(), m_p1.getZ()),
core::vector2df(m_p3.getX(), m_p3.getZ()) );
m_p2 = (m_p1 + m_p3) / 2;
} // CheckGoal
// ----------------------------------------------------------------------------
@ -113,11 +113,3 @@ void CheckGoal::reset(const Track &track)
}
} // reset
// ----------------------------------------------------------------------------
Vec3 CheckGoal::convertTo3DCenter() const
{
float x = m_line.getMiddle().X;
float y = m_line.getMiddle().Y;
return Vec3(x, 0, y);
}

View File

@ -24,7 +24,6 @@
#include <line2d.h>
using namespace irr;
class CheckManager;
class XMLNode;
class Track;
class Vec3;
@ -37,6 +36,14 @@ class Vec3;
*/
class CheckGoal : public CheckStructure
{
public:
/** Used by AIs to test whether the ball is likely to goal. */
enum PointLocation
{
POINT_FIRST,
POINT_CENTER,
POINT_LAST
};
private:
/** Previois ball position. */
Vec3 m_previous_ball_position;
@ -47,6 +54,11 @@ private:
/** The line that is tested for being crossed. */
core::line2df m_line;
/** Used by AIs to test whether the ball is likely to goal. */
Vec3 m_p1;
Vec3 m_p2;
Vec3 m_p3;
public:
CheckGoal(const XMLNode &node, unsigned int index);
virtual ~CheckGoal() {}
@ -59,7 +71,11 @@ public:
// ------------------------------------------------------------------------
bool getTeam() const { return m_first_goal; }
// ------------------------------------------------------------------------
Vec3 convertTo3DCenter() const;
}; // CheckLine
const Vec3& getPoint(PointLocation point) const
{
return (point == POINT_LAST ? m_p3 :
(point == POINT_CENTER ? m_p2 : m_p1));
}
}; // CheckGoal
#endif