package org.newdawn.slick.util.pathfinding.navmesh; import java.util.ArrayList; /** * A nav-mesh is a set of shapes that describe the navigation of a map. These * shapes are linked together allow path finding but without the high * resolution that tile maps require. This leads to fast path finding and * potentially much more accurate map definition. * * @author kevin * */ public class NavMesh { /** The list of spaces that build up this navigation mesh */ private ArrayList spaces = new ArrayList(); /** * Create a new empty mesh */ public NavMesh() { } /** * Create a new mesh with a set of spaces * * @param spaces The spaces included in the mesh */ public NavMesh(ArrayList spaces) { this.spaces.addAll(spaces); } /** * Get the number of spaces that are in the mesh * * @return The spaces in the mesh */ public int getSpaceCount() { return spaces.size(); } /** * Get the space at a given index * * @param index The index of the space to retrieve * @return The space at the given index */ public Space getSpace(int index) { return (Space) spaces.get(index); } /** * Add a single space to the mesh * * @param space The space to be added */ public void addSpace(Space space) { spaces.add(space); } /** * Find the space at a given location * * @param x The x coordinate at which to find the space * @param y The y coordinate at which to find the space * @return The space at the given location */ public Space findSpace(float x, float y) { for (int i=0;i<spaces.size();i++) { Space space = getSpace(i); if (space.contains(x,y)) { return space; } } return null; } /** * Find a path from the source to the target coordinates * * @param sx The x coordinate of the source location * @param sy The y coordinate of the source location * @param tx The x coordinate of the target location * @param ty The y coordinate of the target location * @param optimize True if paths should be optimized * @return The path between the two spaces */ public NavPath findPath(float sx, float sy, float tx, float ty, boolean optimize) { Space source = findSpace(sx,sy); Space target = findSpace(tx,ty); if ((source == null) || (target == null)) { return null; } for (int i=0;i<spaces.size();i++) { ((Space) spaces.get(i)).clearCost(); } target.fill(source,tx, ty, 0); if (target.getCost() == Float.MAX_VALUE) { return null; } if (source.getCost() == Float.MAX_VALUE) { return null; } NavPath path = new NavPath(); path.push(new Link(sx, sy, null)); if (source.pickLowestCost(target, path)) { path.push(new Link(tx, ty, null)); if (optimize) { optimize(path); } return path; } return null; } /** * Check if a particular path is clear * * @param x1 The x coordinate of the starting point * @param y1 The y coordinate of the starting point * @param x2 The x coordinate of the ending point * @param y2 The y coordinate of the ending point * @param step The size of the step between points * @return True if there are no blockages along the path */ private boolean isClear(float x1, float y1, float x2, float y2, float step) { float dx = (x2 - x1); float dy = (y2 - y1); float len = (float) Math.sqrt((dx*dx)+(dy*dy)); dx *= step; dx /= len; dy *= step; dy /= len; int steps = (int) (len / step); for (int i=0;i<steps;i++) { float x = x1 + (dx*i); float y = y1 + (dy*i); if (findSpace(x,y) == null) { return false; } } return true; } /** * Optimize a path by removing segments that arn't required * to reach the end point * * @param path The path to optimize. Redundant segments will be removed */ private void optimize(NavPath path) { int pt = 0; while (pt < path.length()-2) { float sx = path.getX(pt); float sy = path.getY(pt); float nx = path.getX(pt+2); float ny = path.getY(pt+2); if (isClear(sx,sy,nx,ny,0.1f)) { path.remove(pt+1); } else { pt++; } } } }