From d2f26fe70d164317d940367157ff33af5fe95498 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 16 May 2016 15:24:05 +0800 Subject: [PATCH] First playable advanced soccer ai --- src/karts/controller/ai_base_controller.cpp | 8 +- src/karts/controller/ai_base_controller.hpp | 4 +- src/karts/controller/arena_ai.cpp | 10 ++- src/karts/controller/arena_ai.hpp | 1 + src/karts/controller/soccer_ai.cpp | 90 ++++++++++++++++----- src/karts/controller/soccer_ai.hpp | 1 + src/modes/soccer_world.cpp | 7 +- src/modes/soccer_world.hpp | 50 +++++++----- src/physics/physical_object.hpp | 2 + 9 files changed, 125 insertions(+), 48 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index 1600df748..a46d197e5 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -283,13 +283,13 @@ void AIBaseController::crashed(const Material *m) } // crashed(Material) //----------------------------------------------------------------------------- -void AIBaseController::checkPosition(const Vec3 &point, - posData *pos_data, - Vec3 *lc) const +void AIBaseController::checkPosition(const Vec3 &point, posData *pos_data, + Vec3 *lc, bool use_front_xyz) const { // 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 - + (use_front_xyz ? m_kart->getFrontXYZ() : m_kart->getXYZ()); Vec3 local_coordinates = quatRotate(q, p); // Save local coordinates for later use if needed diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index ffa8e3fef..d52c70672 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -77,7 +77,9 @@ protected: /** This can be called to detect if the kart is stuck (i.e. repeatedly * hitting part of the track). */ bool isStuck() const { return m_stuck; } - void checkPosition(const Vec3&, posData*, Vec3* lc = NULL) const; + void checkPosition(const Vec3&, posData*, + Vec3* lc = NULL, + bool use_front_xyz = false) const; public: AIBaseController(AbstractKart *kart); diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index d58937080..237a5e82c 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -76,7 +76,10 @@ void ArenaAI::update(float dt) // Don't do anything if there is currently a kart animations shown. if (m_kart->getKartAnimation()) + { + resetAfterStop(); return; + } if (isWaiting()) { @@ -101,6 +104,7 @@ void ArenaAI::update(float dt) if (m_is_uturn) { + resetAfterStop(); handleArenaUTurn(dt); } else @@ -202,6 +206,7 @@ bool ArenaAI::handleArenaUnstuck(const float dt) { if (!m_is_stuck || m_is_uturn) return false; + resetAfterStop(); setSteering(0.0f, dt); if (fabsf(m_kart->getSpeed()) > @@ -263,7 +268,8 @@ void ArenaAI::handleArenaSteering(const float dt) checkPosition(m_target_point, &m_cur_kart_pos_data); #ifdef AI_DEBUG - if (m_path_corners.size() > 2) + m_debug_sphere->setPosition(m_path_corners[0].toIrrVector()); + /*if (m_path_corners.size() > 2) { m_debug_sphere->setVisible(true); m_debug_sphere_next->setVisible(true); @@ -274,7 +280,7 @@ void ArenaAI::handleArenaSteering(const float dt) { m_debug_sphere->setVisible(false); m_debug_sphere_next->setVisible(false); - } + }*/ #endif if (m_cur_kart_pos_data.behind) { diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 9ebe46a0d..0bd7c6a1f 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -118,6 +118,7 @@ private: void stringPull(const Vec3&, const Vec3&); virtual int getCurrentNode() const = 0; virtual bool isWaiting() const = 0; + virtual void resetAfterStop() {}; virtual void findClosestKart(bool use_difficulty) = 0; virtual void findTarget() = 0; virtual bool forceBraking() { return false; } diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index db0ea07b9..35c0dcb31 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -56,9 +56,9 @@ SoccerAI::SoccerAI(AbstractKart *kart) video::SColor red(128, 128, 0, 0); video::SColor blue(128, 0, 0, 128); m_red_sphere = irr_driver->addSphere(1.0f, red); - m_red_sphere->setVisible(true); + m_red_sphere->setVisible(false); m_blue_sphere = irr_driver->addSphere(1.0f, blue); - m_blue_sphere->setVisible(true); + m_blue_sphere->setVisible(false); #endif m_world = dynamic_cast(World::getWorld()); @@ -114,6 +114,7 @@ void SoccerAI::update(float dt) if (World::getWorld()->getPhase() == World::GOAL_PHASE) { + resetAfterStop(); m_controls->m_brake = false; m_controls->m_accel = 0.0f; AIBaseController::update(dt); @@ -170,7 +171,10 @@ void SoccerAI::findTarget() return; } - // Otherwise do the same as in battle mode, attack other karts + // Always reset this flag, + // in case the ball chaser lost the ball somehow + m_overtake_ball = false; + if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER) { @@ -212,30 +216,80 @@ Vec3 SoccerAI::determineBallAimingPosition() posData aim_pos = {0}; Vec3 ball_lc; Vec3 aim_lc; - checkPosition(orig_pos, &ball_pos); - checkPosition(orig_pos, &aim_pos, &aim_lc); + checkPosition(orig_pos, &ball_pos, &ball_lc, true/*use_front_xyz*/); + checkPosition(ball_aim_pos, &aim_pos, &aim_lc, true/*use_front_xyz*/); // Too far from the ball, // use path finding from arena ai to get close - if (ball_pos.distance > 6.0f) return ball_aim_pos; + // ie no extra braking is needed + if (aim_pos.distance > 6.0f) return ball_aim_pos; - const Vec3 dist_to_aim_point = m_kart->getFrontXYZ() - ball_aim_pos; - - // Prevent lost control when steering with ball - const bool need_braking = ball_pos.angle > 0.1f && - m_kart->getSpeed() > 9.0f && ball_pos.distance < 3.0f; - - if (need_braking) + if (m_overtake_ball) { - m_controls->m_brake = true; - m_force_brake = true; + // Check if the kart passed the ball already, + // If so aim the front side of ball + if (ball_pos.behind) + { + 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; + } + 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)); + } + } } - if (dist_to_aim_point.length_2d() < 0.4f) + else { - //Log::info("","%f",dist_to_aim_point.length_2d()); - return m_world->getBallTrans()(Vec3(0, 0, 1)); + // 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) + { + m_overtake_ball = true; + } + + // 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(); + + if (need_braking) + { + m_controls->m_brake = true; + m_force_brake = true; + } + if (aim_pos.behind && aim_pos.distance < + (m_world->getBallDiameter() / 2)) + { + // Reached aim point, aim forward + return m_world->getBallAimPosition(m_opp_team, true/*reverse*/); + } + return ball_aim_pos; } + + // Make compiler happy return ball_aim_pos; + } // determineBallAimingPosition //----------------------------------------------------------------------------- diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index d0c39670a..78c513381 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -57,6 +57,7 @@ private: virtual void findClosestKart(bool use_difficulty); virtual void findTarget(); + virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; } virtual int getCurrentNode() const; virtual bool isWaiting() const; virtual bool canSkid(float steer_fraction) { return false; } diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index 99345c84f..b8521e975 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -97,7 +97,7 @@ void SoccerWorld::init() if (!m_ball) Log::fatal("SoccerWorld","Ball is missing in soccer field, abort."); - m_bgd.init(); + m_bgd.init(m_ball->getPhysicalObject()->getRadius()); } // init @@ -423,9 +423,8 @@ void SoccerWorld::updateBallPosition(float dt) { if (isRaceOver()) return; - if (!(m_ball->getPhysicalObject()->getBody() - ->getLinearVelocity().x() == 0.0f || m_ball->getPhysicalObject() - ->getBody()->getLinearVelocity().z() == 0.0f)) + if (!(m_ball_body->getLinearVelocity().x() == 0.0f || + m_ball_body->getLinearVelocity().z() == 0.0f)) { // Only update heading if the ball is moving m_ball_heading = atan2f(m_ball_body->getLinearVelocity().getX(), diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 21ae4c056..958e8d8e9 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -78,6 +78,9 @@ private: { // These data are used by AI to determine ball aiming angle private: + // Radius of the ball + float m_radius; + // Slope of the line from ball to the center point of goals float m_red_goal_slope; float m_blue_goal_slope; @@ -116,8 +119,16 @@ private: return m_trans; } // getTrans - void init() + float getDiameter() const { + return m_radius * 2; + } // getTrans + + void init(float ball_radius) + { + m_radius = ball_radius; + assert(m_radius > 0.0f); + // Save two goals unsigned int n = CheckManager::get()->getCheckStructureCount(); for (unsigned int i = 0; i < n; i++) @@ -191,7 +202,7 @@ private: return false; } // isApproachingGoal - Vec3 getAimPosition(SoccerTeam team) const + Vec3 getAimPosition(SoccerTeam team, bool reverse) const { // If it's likely to goal already, aim the ball straight behind // should do the job @@ -202,16 +213,17 @@ private: // This is done by using Pythagorean Theorem and solving the // equation from ball to goal center (y = (m_***_goal_slope) x) - // We aim 1 unit behind the ball (easier to solve), - // so 1 = sqrt (x2 + y2) and than x = sqrt (1 - y2) + // We aim behind the ball from the center of the ball to its + // diameter, so 2*m_radius = sqrt (x2 + y2), + // which is next x = sqrt (2*m_radius - y2) // And than we have x = y / m(m_***_goal_slope) // After put that in the slope equation, we have - // y = sqrt(m2 / (1+m2)) + // y = sqrt(2*m_radius*m2 / (1+m2)) float x = 0.0f; float y = 0.0f; if (team == SOCCER_TEAM_BLUE) { - y = sqrt((m_blue_goal_slope * m_blue_goal_slope) / + y = sqrt((m_blue_goal_slope * m_blue_goal_slope * m_radius*2) / (1 + (m_blue_goal_slope * m_blue_goal_slope))); if (m_blue_goal_2.x() == 0.0f || (m_blue_goal_2.x() > 0.0f && m_blue_goal_2.z() > 0.0f) || @@ -224,7 +236,7 @@ private: } else { - y = sqrt((m_red_goal_slope * m_red_goal_slope) / + y = sqrt((m_red_goal_slope * m_red_goal_slope * m_radius*2) / (1 + (m_red_goal_slope * m_red_goal_slope))); if (m_red_goal_2.x() == 0.0f || (m_red_goal_2.x() > 0.0f && m_red_goal_2.z() > 0.0f) || @@ -237,7 +249,8 @@ private: assert (!std::isnan(x)); assert (!std::isnan(y)); // Return the world coordinates - return m_trans(Vec3(x, 0, y)); + return (reverse ? m_trans(Vec3(-x, 0, -y)) : + m_trans(Vec3(x, 0, y))); } // getAimPosition }; // BallGoalData @@ -344,29 +357,28 @@ public: } // ------------------------------------------------------------------------ int getKartNode(unsigned int kart_id) const - { return m_kart_on_node[kart_id]; } + { return m_kart_on_node[kart_id]; } // ------------------------------------------------------------------------ int getBallNode() const - { return m_ball_on_node; } + { return m_ball_on_node; } // ------------------------------------------------------------------------ const Vec3& getBallPosition() const { return (Vec3&)m_ball_body->getCenterOfMassTransform().getOrigin(); } // ------------------------------------------------------------------------ float getBallHeading() const - { return m_ball_heading; } + { return m_ball_heading; } + // ------------------------------------------------------------------------ + float getBallDiameter() const + { return m_bgd.getDiameter(); } // ------------------------------------------------------------------------ bool ballApproachingGoal(SoccerTeam team) const - { - return m_bgd.isApproachingGoal(team); - } + { return m_bgd.isApproachingGoal(team); } // ------------------------------------------------------------------------ - Vec3 getBallAimPosition(SoccerTeam team) const - { - return m_bgd.getAimPosition(team); - } + Vec3 getBallAimPosition(SoccerTeam team, bool reverse = false) const + { return m_bgd.getAimPosition(team, reverse); } // ------------------------------------------------------------------------ const btTransform& getBallTrans() const - { return m_bgd.getTrans(); } + { return m_bgd.getTrans(); } // ------------------------------------------------------------------------ bool isCorrectGoal(unsigned int kart_id, bool first_goal) const; // ------------------------------------------------------------------------ diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index 02d2580ba..4535121b5 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -211,6 +211,8 @@ public: /** Add body to dynamic world */ void addBody(); // ------------------------------------------------------------------------ + float getRadius() const { return m_radius; } + // ------------------------------------------------------------------------ const std::string& getOnKartCollisionFunction() const { return m_on_kart_collision; } // ------------------------------------------------------------------------ const std::string& getOnItemCollisionFunction() const { return m_on_item_collision; }