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
This commit is contained in:
parent
1c75270912
commit
a31c314cb4
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 <iostream>
|
||||
|
||||
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; i<max_count; i++)
|
||||
{
|
||||
const NavPoly& p = BattleGraph::get()->getPolyOfNode(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<int>& 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; i<adjacents.size(); i++)
|
||||
{
|
||||
const NavPoly& p_temp =
|
||||
BattleGraph::get()->getPolyOfNode(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; i<BattleGraph::get()->getNumNodes(); 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);
|
||||
}
|
@ -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) {};
|
||||
|
@ -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);
|
||||
|
@ -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<float>> (n_polys, std::vector<float>(n_polys, 999.9f));
|
||||
for(unsigned int i=0; i<n_polys; i++)
|
||||
{
|
||||
NavPoly currentPoly = navmesh->getNavPoly(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<int>> (n, std::vector<int>(n,BattleGraph::UNKNOWN_POLY));
|
||||
|
||||
for(unsigned int k=0; k<n; k++)
|
||||
{
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if( (m_distance_matrix[i][k] + m_distance_matrix[k][j]) < m_distance_matrix[i][j])
|
||||
{
|
||||
m_distance_matrix[i][j] = m_distance_matrix[i][k] + m_distance_matrix[k][j];
|
||||
m_next_poly[i][j] = k;
|
||||
}
|
||||
else if((m_distance_matrix[i][k] + m_distance_matrix[k][j]) == m_distance_matrix[i][j])
|
||||
{
|
||||
m_next_poly[i][j]= j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BattleGraph::~BattleGraph(void)
|
||||
{
|
||||
NavMesh::destroy();
|
||||
@ -93,7 +133,7 @@ void BattleGraph::createMesh(bool enable_transparency,
|
||||
|
||||
// Now add all polygons
|
||||
int i=0;
|
||||
for(unsigned int count=0; count<m_graph.size(); count++)
|
||||
for(unsigned int count=0; count<getNumNodes(); count++)
|
||||
{
|
||||
///compute colors
|
||||
if(!track_color)
|
||||
@ -163,7 +203,7 @@ void BattleGraph::createMesh(bool enable_transparency,
|
||||
|
||||
void BattleGraph::createDebugMesh()
|
||||
{
|
||||
if(m_graph.size()<=0) return; // no debug output if not graph
|
||||
if(getNumNodes()<=0) return; // no debug output if not graph
|
||||
|
||||
createMesh(/*enable_transparency*/true);
|
||||
m_node = irr_driver->addMesh(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.
|
||||
|
@ -43,7 +43,8 @@ private:
|
||||
static BattleGraph *m_battle_graph;
|
||||
|
||||
std::vector< std::vector< std::pair<int,float> > > 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();
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "tracks/navmesh.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
NavPoly::NavPoly(const std::vector<int> &polygonVertIndices,
|
||||
const std::vector<int> &adjacentPolygonIndices)
|
||||
@ -30,10 +30,10 @@ NavPoly::NavPoly(const std::vector<int> &polygonVertIndices,
|
||||
m_vertices = polygonVertIndices;
|
||||
|
||||
m_adjacents = adjacentPolygonIndices;
|
||||
|
||||
|
||||
std::vector<Vec3> xyz_points = getVertices();
|
||||
|
||||
Vec3 temp;
|
||||
Vec3 temp(0.0f,0.0f,0.0f);
|
||||
for(unsigned int i=0; i<xyz_points.size(); i++)
|
||||
temp = temp + xyz_points[i];
|
||||
|
||||
|
@ -101,7 +101,7 @@ NavMesh::NavMesh(const std::string &filename)
|
||||
std::vector<int> polygonVertIndices;
|
||||
std::vector<int> 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++;
|
||||
|
Loading…
Reference in New Issue
Block a user