From a31c314cb446240064f67bfcad1684c329258f8b Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 29 Oct 2013 05:47:11 +0000 Subject: [PATCH] Major update to Battle AI. AI can localize itself on the navigation mesh. Battle graph is now stored as adjacency matrix instead of adjacency list. Implemented pathfinding (Floyd-Warshall), AI can now find a path from one sector to another. As a proof of concept: AI can almost follow a player kart. Some code refactoring is in order before further development. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14340 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/ai_base_controller.hpp | 6 +- .../controller/ai_base_lap_controller.hpp | 6 ++ src/karts/controller/battle_ai.cpp | 89 ++++++++++++++++++- src/karts/controller/battle_ai.hpp | 16 +++- src/modes/world.cpp | 7 ++ src/tracks/battle_graph.cpp | 54 +++++++++-- src/tracks/battle_graph.hpp | 15 +++- src/tracks/nav_poly.cpp | 6 +- src/tracks/navmesh.cpp | 2 +- 9 files changed, 178 insertions(+), 23 deletions(-) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 0febdadbb..929e211b8 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -53,11 +53,7 @@ protected: - /** The current node the kart is on. This can be different from the value - * in LinearWorld, since it takes the chosen path of the AI into account - * (e.g. the closest point in LinearWorld might be on a branch not - * chosen by the AI). */ - int m_track_node; + virtual void setSteering (float angle, float dt); void setControllerName(const std::string &name); diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp index 637a7e7d4..1791b5242 100644 --- a/src/karts/controller/ai_base_lap_controller.hpp +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -45,6 +45,12 @@ private: protected: + /** The current node the kart is on. This can be different from the value + * in LinearWorld, since it takes the chosen path of the AI into account + * (e.g. the closest point in LinearWorld might be on a branch not + * chosen by the AI). */ + int m_track_node; + /** Keep a pointer to world. */ LinearWorld *m_world; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 4ff70ac00..22d4ce0a1 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -30,12 +30,17 @@ #include "karts/skidding.hpp" #include "karts/skidding_properties.hpp" #include "modes/three_strikes_battle.hpp" +#include "tracks/nav_poly.hpp" +#include "tracks/navmesh.hpp" +#include BattleAI::BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player) : AIBaseController(kart, player) { + + reset(); if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) { @@ -50,6 +55,9 @@ BattleAI::BattleAI(AbstractKart *kart, m_world = NULL; m_track = NULL; } + + updateCurrentNode(); + // Don't call our own setControllerName, since this will add a // billboard showing 'AIBaseController' to the kar. Controller::setControllerName("BattleAI"); @@ -58,7 +66,82 @@ BattleAI::BattleAI(AbstractKart *kart, void BattleAI::update(float dt) { - m_controls->m_accel = 1; - m_controls->m_steer = 0; - return; + m_controls->m_accel = 0.65f; + // m_controls->m_steer = 0; + updateCurrentNode(); + + handleSteering(dt); +} + + +void BattleAI::reset() +{ + m_current_node = BattleGraph::UNKNOWN_POLY; +} + +void BattleAI::updateCurrentNode() +{ + //std::cout<<"Current Node \t"<< m_current_node << std::endl; + + // if unknown location, search everywhere + if(m_current_node == BattleGraph::UNKNOWN_POLY) + { + int max_count = BattleGraph::get()->getNumNodes(); + for(unsigned int i =0; igetPolyOfNode(i); + if(p.pointInPoly(m_kart->getXYZ())) + m_current_node = i; + } + return; + } + + if(m_current_node != BattleGraph::UNKNOWN_POLY) + { + //check if the kart is still on the same node + const NavPoly& p = BattleGraph::get()->getPolyOfNode(m_current_node); + if(p.pointInPoly(m_kart->getXYZ())) return; + + //if not then check all adjacent polys + const std::vector& adjacents = + NavMesh::get()->getAdjacentPolys(m_current_node); + + // Set m_current_node to unknown so that if no adjacent poly checks true + // we look everywhere the next time updateCurrentNode is called. This is + // useful in cases when you are "teleported" to some other poly, ex. rescue + m_current_node = BattleGraph::UNKNOWN_POLY; + + + for(unsigned int i=0; igetPolyOfNode(adjacents[i]); + if(p_temp.pointInPoly(m_kart->getXYZ())) m_current_node = adjacents[i]; + } + return; + } +} + + +void BattleAI::handleSteering(const float dt) +{ + Vec3 target_point; + const AbstractKart* kart = m_world->getPlayerKart(0); + int player_node = -1; + for(unsigned int i =0; igetNumNodes(); i++) + { + const NavPoly& p = BattleGraph::get()->getPolyOfNode(i); + if(p.pointInPoly(kart->getXYZ())) + player_node = i; + } + + if(player_node == BattleGraph::UNKNOWN_POLY || m_current_node == BattleGraph::UNKNOWN_POLY) return; + int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node); + if(next_node == -1) return; + target_point = NavMesh::get()->getCenterOfPoly(next_node); + + if(player_node != m_current_node) + setSteering(steerToPoint(target_point),dt); + else + setSteering(steerToPoint(kart->getXYZ()),dt); } \ No newline at end of file diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 0cab4eee1..be68d0f7d 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -44,19 +44,31 @@ namespace irr class BattleAI : public AIBaseController { +private: + + int m_current_node; + + void updateCurrentNode(); + void handleAcceleration(const float dt) {}; + void handleSteering(const float dt); + void handleBraking() {}; + protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; + + public: BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); //~BattleAI(); + unsigned int getCurrentNode() const { return m_current_node; } virtual void update (float delta); - virtual void reset () {}; - //static void enableDebug() {m_ai_debug = true; } + virtual void reset (); + virtual void crashed(const AbstractKart *k) {}; virtual void crashed(const Material *m) {}; virtual void handleZipper(bool play_sound) {}; diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 3174bb1e1..092b48d99 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -35,6 +35,7 @@ #include "io/file_manager.hpp" #include "input/device_manager.hpp" #include "items/projectile_manager.hpp" +#include "karts/controller/battle_ai.hpp" #include "karts/controller/player_controller.hpp" #include "karts/controller/end_controller.hpp" #include "karts/controller/skidding_ai.hpp" @@ -327,6 +328,9 @@ Controller* World::loadAIController(AbstractKart *kart) { Controller *controller; int turn=0; + + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) + turn=1; // If different AIs 8should be used, adjust turn (or switch randomly // or dependent on difficulty) switch(turn) @@ -334,6 +338,9 @@ Controller* World::loadAIController(AbstractKart *kart) case 0: controller = new SkiddingAI(kart); break; + case 1: + controller = new BattleAI(kart); + break; default: Log::warn("World", "Unknown AI, using default."); controller = new SkiddingAI(kart); diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index efdf380f5..a55babc80 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -28,7 +28,7 @@ #include "tracks/navmesh.hpp" #include "utils/vec3.hpp" - +const int BattleGraph::UNKNOWN_POLY = -1; BattleGraph * BattleGraph::m_battle_graph = NULL; BattleGraph::BattleGraph(const std::string &navmesh_file_name) @@ -36,12 +36,15 @@ BattleGraph::BattleGraph(const std::string &navmesh_file_name) NavMesh::create(navmesh_file_name); m_navmesh_file = navmesh_file_name; buildGraph(NavMesh::get()); + computeFloydWarshall(); + } void BattleGraph::buildGraph(NavMesh* navmesh) { unsigned int n_polys = navmesh->getNumberOfPolys(); - m_graph.resize(n_polys); + //m_graph.resize(n_polys); + m_distance_matrix = std::vector< std::vector> (n_polys, std::vector(n_polys, 999.9f)); for(unsigned int i=0; igetNavPoly(i); @@ -50,12 +53,49 @@ void BattleGraph::buildGraph(NavMesh* navmesh) { Vec3 adjacentPolyCenter = navmesh->getCenterOfPoly(adjacents[j]); float distance = Vec3(adjacentPolyCenter - currentPoly.getCenter()).length_2d(); - m_graph[i].push_back(std::make_pair(adjacents[i], distance)); + //m_graph[i].push_back(std::make_pair(adjacents[j], distance)); + m_distance_matrix[i][adjacents[j]] = distance; + + } + m_distance_matrix[i][i] = 0.0f; + } + +} + + +// computes all pair shortest path +// iteratively updates the m_next_poly +void BattleGraph::computeFloydWarshall() +{ + int n = getNumNodes(); + + // initialize m_next_poly with unknown_poly so that if no path is found b/w i and j + // then m_next_poly[i][j] = -1 (UNKNOWN_POLY) + // AI must check this + m_next_poly = std::vector< std::vector> (n, std::vector(n,BattleGraph::UNKNOWN_POLY)); + + for(unsigned int k=0; kaddMesh(m_mesh); @@ -175,7 +215,9 @@ void BattleGraph::createDebugMesh() void BattleGraph::cleanupDebugMesh() { - irr_driver->removeNode(m_node); + if(m_node != NULL) + irr_driver->removeNode(m_node); + m_node = NULL; // No need to call irr_driber->removeMeshFromCache, since the mesh // was manually made and so never added to the mesh cache. diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp index b057df7dd..65d3a6fc6 100644 --- a/src/tracks/battle_graph.hpp +++ b/src/tracks/battle_graph.hpp @@ -43,7 +43,8 @@ private: static BattleGraph *m_battle_graph; std::vector< std::vector< std::pair > > m_graph; - + std::vector< std::vector< float > > m_distance_matrix; + std::vector< std::vector< int > > m_next_poly; /** For debug mode only: the node of the debug mesh. */ scene::ISceneNode *m_node; /** For debug only: the mesh of the debug mesh. */ @@ -56,6 +57,7 @@ private: void buildGraph(NavMesh*); + void computeFloydWarshall(); void createMesh(bool enable_transparency=false, const video::SColor *track_color=NULL); @@ -63,7 +65,9 @@ private: ~BattleGraph(void); public: - + + static const int UNKNOWN_POLY; + static BattleGraph *get() { return m_battle_graph; } static void create(const std::string &navmesh_file_name) @@ -82,7 +86,12 @@ public: } } - unsigned int getNumNodes() const { return m_graph.size(); } + unsigned int getNumNodes() const { return m_distance_matrix.size(); } + const NavPoly& getPolyOfNode(int i) const + { return NavMesh::get()->getNavPoly(i); } + + const int & getNextShortestPathPoly(int i, int j) const + { return m_next_poly[i][j]; } void createDebugMesh(); void cleanupDebugMesh(); diff --git a/src/tracks/nav_poly.cpp b/src/tracks/nav_poly.cpp index e022b3093..3670da369 100644 --- a/src/tracks/nav_poly.cpp +++ b/src/tracks/nav_poly.cpp @@ -22,7 +22,7 @@ #include "tracks/navmesh.hpp" #include - +#include NavPoly::NavPoly(const std::vector &polygonVertIndices, const std::vector &adjacentPolygonIndices) @@ -30,10 +30,10 @@ NavPoly::NavPoly(const std::vector &polygonVertIndices, m_vertices = polygonVertIndices; m_adjacents = adjacentPolygonIndices; - + std::vector xyz_points = getVertices(); - Vec3 temp; + Vec3 temp(0.0f,0.0f,0.0f); for(unsigned int i=0; i polygonVertIndices; std::vector adjacentPolygonIndices; xml_node_node->get("indices", &polygonVertIndices); - xml_node_node->get("adjacent", &adjacentPolygonIndices); + xml_node_node->get("adjacents", &adjacentPolygonIndices); NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices); m_polys.push_back(*np); m_n_polys++;