Coverage Summary for Class: TileMapGraph (com.mygdx.game.AI)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| TileMapGraph | 100% (1/1) | 31.2% (5/16) | 41.6% (37/89) |
1 package com.mygdx.game.AI; 2 3 import com.badlogic.gdx.ai.pfa.Connection; 4 import com.badlogic.gdx.ai.pfa.DefaultGraphPath; 5 import com.badlogic.gdx.ai.pfa.GraphPath; 6 import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder; 7 import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph; 8 import com.badlogic.gdx.maps.MapLayer; 9 import com.badlogic.gdx.maps.MapLayers; 10 import com.badlogic.gdx.maps.tiled.TiledMap; 11 import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; 12 import com.badlogic.gdx.math.Vector2; 13 import com.badlogic.gdx.utils.Array; 14 import com.badlogic.gdx.utils.ObjectMap; 15 import com.mygdx.utils.QueueFIFO; 16 17 import static com.mygdx.utils.TileMapCells.OBSTACLE; 18 import static com.mygdx.utils.TileMapCells.PASSABLE; 19 20 /** 21 * The Graphical representation of the tilemap with each cell being bidirectionally to the adjacent nodes. 22 */ 23 public class TileMapGraph implements IndexedGraph<Node> { 24 private final NodeHeuristic heuristic; 25 private final Array<Node> nodes; 26 private final Array<Path> paths; 27 private final Vector2 mapDim; 28 29 private final ObjectMap<Node, Array<Connection<Node>>> nodePaths; 30 31 private TileMapGraph() { 32 33 heuristic = new NodeHeuristic(); 34 nodes = new Array<>(); 35 paths = new Array<>(); 36 nodePaths = new ObjectMap<>(); 37 mapDim = new Vector2(); 38 } 39 40 /** 41 * Creates a Graph from the given tilemap 42 * 43 * @param map the source tilemap 44 */ 45 public TileMapGraph(TiledMap map) { 46 this(); 47 48 MapLayers layers = map.getLayers(); 49 TiledMapTileLayer layer = null; 50 51 // find the collision layer 52 for (MapLayer l : layers) { 53 if (l.getName().equals("Collision")) { 54 layer = (TiledMapTileLayer) l; 55 } 56 } 57 // if there is no collision layer 58 if (layer == null) { 59 return; 60 } 61 // the map dimensions 62 mapDim.set(layer.getWidth(), layer.getHeight()); 63 64 nodes.ensureCapacity((int) mapDim.x * (int) mapDim.y); 65 66 // create all the nodes 67 for (int i = 0; i < mapDim.x * mapDim.y; i++) { 68 nodes.add(new Node(0, 0)); 69 } 70 71 // for each column 72 for (int x = 0; x < layer.getWidth(); x++) { 73 // for each row 74 for (int y = 0; y < layer.getHeight(); y++) { 75 TiledMapTileLayer.Cell center = layer.getCell(x, y); 76 77 if (getType(center) == OBSTACLE) { 78 continue; 79 } 80 // the central node 81 addNode(x, y); 82 83 // all surrounding nodes 84 for (int i = -1; i < 2; i++) { 85 for (int j = -1; j < 2; j++) { 86 87 // prevents the node pathing to its self 88 if (i == 0 && j == 0) { 89 continue; 90 } 91 92 TiledMapTileLayer.Cell cell = layer.getCell(x + i, y + j); 93 // is cell outside the map 94 if (cell == null) { 95 continue; 96 } 97 98 // is the cell passable 99 if (cell.getTile().getId() == PASSABLE) { 100 101 addNode(x + i, y + j); 102 addPath(x, y, x + i, y + j); 103 } 104 } 105 } 106 } 107 } 108 } 109 110 /** 111 * Node a position (x, y) 112 * 113 * @param x co-ord 114 * @param y co-ord 115 * @return Node at (x, y) or null 116 */ 117 public Node getNode(float x, float y) { 118 Node n = nodes.get(getIndex(x, y)); 119 if (n.cost == -1) { 120 return null; 121 } 122 return n; 123 } 124 125 private int getIndex(float x, float y) { 126 return (int) (mapDim.x * y + x); 127 } 128 129 /** 130 * @return type of cell as far as the tile map is condensed 131 */ 132 private int getType(TiledMapTileLayer.Cell c) { 133 return c.getTile().getId(); 134 } 135 136 private int getIndex(int x, int y) { 137 return (int) mapDim.x * y + x; 138 } 139 140 /** 141 * doesn't add if already there 142 * 143 * @param x x pos 144 * @param y y pos 145 */ 146 private void addNode(float x, float y) { 147 Node n = nodes.get(getIndex((int) x, (int) y)); 148 if (n.cost > 0) { 149 return; 150 } 151 n.set(x, y); 152 n.cost = 1; 153 } 154 155 /** 156 * Adds path to map doesn't check for duplicates 157 * 158 * @param a src 159 * @param b dst 160 */ 161 private void addPath(Node a, Node b) { 162 Path path = new Path(a, b); 163 164 if (!nodePaths.containsKey(a)) { 165 nodePaths.put(a, new Array<>()); 166 } 167 nodePaths.get(a).add(path); 168 paths.add(path); 169 } 170 171 /** 172 * Adds path to map doesn't check for duplicates 173 * 174 * @param x1 src.x 175 * @param y1 src.y 176 * @param x2 dst.x 177 * @param y2 dst.y 178 */ 179 private void addPath(float x1, float y1, float x2, float y2) { 180 Node a = getNode(x1, y1); 181 Node b = getNode(x2, y2); 182 183 addPath(a, b); 184 } 185 186 /** 187 * Finds the path 188 * 189 * @param start the starting node 190 * @param goal the ending node 191 * @return a queue of the nodes to visit null if no path id found 192 */ 193 public GraphPath<Node> findPath(Node start, Node goal) { 194 if (start == null || goal == null) { 195 return null; 196 } 197 GraphPath<Node> path = new DefaultGraphPath<>(); 198 new IndexedAStarPathFinder<>(this).searchNodePath(start, goal, heuristic, path); 199 return path; 200 } 201 202 /** 203 * Finds a sequence of locations which can be travelled to without collision 204 * 205 * @param a starting node 206 * @param b destination node 207 * @return queue of location to travel to in order 208 */ 209 public QueueFIFO<Vector2> findOptimisedPath(Node a, Node b) { 210 GraphPath<Node> path = findPath(a, b); 211 QueueFIFO<Vector2> res = new QueueFIFO<>(); 212 Vector2 delta = new Vector2(); 213 float sequenceLength = 0; // the amount of times a 214 Vector2 cur = new Vector2(); 215 216 Vector2 prev = path.get(0).getPosition(); 217 for (int i = 1; i < path.getCount(); i++) { 218 Node n = path.get(i); 219 cur.set(n.getPosition()); 220 // d contains the current vector between the current pos an prev 221 Vector2 d = cur.cpy(); 222 d.sub(prev); 223 224 225 if (delta.x == d.x && delta.y == d.y) { 226 sequenceLength++; 227 } else { 228 res.add(delta.scl(sequenceLength)); 229 delta = d; 230 sequenceLength = 1; 231 } 232 prev.set(cur); 233 } 234 res.remove(0); 235 res.add(delta.scl(sequenceLength)); 236 return res; 237 } 238 239 /** 240 * Finds a sequence on locations which can be travelled to without collision 241 * 242 * @param a starting node location 243 * @param b destination node location 244 * @return queue of location to travel to in order 245 */ 246 public QueueFIFO<Vector2> findOptimisedPath(Vector2 a, Vector2 b) { 247 Node n1 = getNode(a.x, a.y); 248 Node n2 = getNode(b.x, b.y); 249 return findOptimisedPath(n1, n2); 250 } 251 252 /** 253 * Finds a sequence on locations which can be travelled to without collision 254 * 255 * @param x1 starting node x co-ords tile space 256 * @param y1 starting node y co-ords tile space 257 * @param x2 destination node x co-ords tile space 258 * @param y2 destination node y co-ords tile space 259 * @return queue of location to travel to in order 260 */ 261 public QueueFIFO<Vector2> findOptimisedPath(float x1, float y1, float x2, float y2) { 262 Node a = getNode(x1, y1); 263 Node b = getNode(x2, y2); 264 return findOptimisedPath(a, b); 265 } 266 267 268 // The Interface 269 @Override 270 public int getIndex(Node node) { 271 return getIndex(node.getPosition().x, node.getPosition().y); 272 } 273 274 @Override 275 public int getNodeCount() { 276 return (int) (mapDim.x * mapDim.y); 277 } 278 279 @Override 280 public Array<Connection<Node>> getConnections(Node fromNode) { 281 if (nodePaths.containsKey(fromNode)) { 282 return nodePaths.get(fromNode); 283 } 284 285 return new Array<>(); 286 } 287 }