Added a new algo that performs better
This commit is contained in:
parent
380492de56
commit
0f98073f0e
14 changed files with 1014 additions and 458 deletions
|
|
@ -1,141 +0,0 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import ei.engine.math.Vector2i;
|
||||
import ei.engine.util.MultiPrintStream;
|
||||
import ei.game.gamestate.InGameState;
|
||||
|
||||
public class AStar{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private AStarPathfinder pathfinder;
|
||||
|
||||
private int width;
|
||||
private int hight;
|
||||
private static AStarNode[][] map;
|
||||
|
||||
protected boolean randomizeMap = true;
|
||||
protected Random RAND = new Random(0);
|
||||
|
||||
public AStar() {
|
||||
width = InGameState.getMap().getSize().getX();
|
||||
hight = InGameState.getMap().getSize().getY();
|
||||
initializePathfinder();
|
||||
if(map != null){
|
||||
reset();
|
||||
}
|
||||
else{
|
||||
initializeMap();
|
||||
}
|
||||
}
|
||||
|
||||
public List<AStarNode> startSearch(Vector2i start, Vector2i goal){
|
||||
//checks that the start and goul are inside the map
|
||||
if(start.getX() >= map.length)
|
||||
start.setX(map.length-1);
|
||||
if(start.getX() < 0)
|
||||
start.setX(0);
|
||||
|
||||
if(start.getY() >= map[0].length)
|
||||
start.setY(map[0].length-1);
|
||||
if(start.getY() < 0)
|
||||
start.setY(0);
|
||||
|
||||
if(goal.getX() >= map.length)
|
||||
goal.setX(map.length-1);
|
||||
if(goal.getX() < 0)
|
||||
goal.setX(0);
|
||||
|
||||
if(goal.getY() >= map[0].length)
|
||||
goal.setY(map[0].length-1);
|
||||
if(goal.getY() < 0)
|
||||
goal.setY(0);
|
||||
|
||||
map[goal.getX()][goal.getY()].setBlocked(false);
|
||||
return pathfinder.findPath(map[start.getX()][start.getY()], map[goal.getX()][goal.getY()]);
|
||||
}
|
||||
|
||||
protected void initializePathfinder() {
|
||||
System.out.println("Initializing pathfinder");
|
||||
|
||||
// Create the pathfinder.
|
||||
pathfinder = new AStarPathfinder();
|
||||
}
|
||||
|
||||
protected void reset(){
|
||||
for(int y = 0, nodeId = 0; hight > y; y++) {
|
||||
MultiPrintStream.out.println("");
|
||||
for(int x = 0; width > x; x++, nodeId++) {
|
||||
if(!InGameState.getMap().isPosEmpty(x, y)) {
|
||||
map[x][y].setBlocked(true);
|
||||
MultiPrintStream.out.print(1);
|
||||
}
|
||||
else{
|
||||
MultiPrintStream.out.print(""+0);
|
||||
map[x][y].setBlocked(false);
|
||||
}
|
||||
map[x][y].setVisited(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeMap() {
|
||||
System.out.println("Initializing map");
|
||||
// Create the map.
|
||||
map = new AStarNode2D[width][hight];
|
||||
for(int y = 0, nodeId = 0; hight > y; y++) {
|
||||
MultiPrintStream.out.println("");
|
||||
for(int x = 0; width > x; x++, nodeId++) {
|
||||
map[x][y] = new AStarNode2D(x, y, nodeId);
|
||||
|
||||
if(!InGameState.getMap().isPosEmpty(x, y)) {
|
||||
map[x][y].setBlocked(true);
|
||||
MultiPrintStream.out.print(""+1);
|
||||
}
|
||||
else MultiPrintStream.out.print(""+0);
|
||||
}
|
||||
}
|
||||
MultiPrintStream.out.println("");
|
||||
/*
|
||||
for(int x = 0, nodeId = 0; width > x; x++) {
|
||||
for(int y = 0; hight > y; y++, nodeId++) {
|
||||
map[x][y] = new AStarNode2D(x, y, nodeId);
|
||||
|
||||
if(!InGameState.getMap().isPosEmpty(x, y)) {
|
||||
map[x][y].setBlocked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Create the neighbours.
|
||||
for(int x = 0; width > x; x++) {
|
||||
for(int y = 0; hight > y; y++) {
|
||||
|
||||
List<AStarNeighbour> neighbours = new LinkedList<AStarNeighbour>();
|
||||
|
||||
if(y-1 >= 0)
|
||||
neighbours.add(new AStarNeighbour(map[x][y-1], AStarNeighbour.Location.Diagonal)); // North.
|
||||
if(x+1 < width && y-1 > 0)
|
||||
neighbours.add(new AStarNeighbour(map[x+1][y-1], AStarNeighbour.Location.Adjacent)); // North-East.
|
||||
if(x+1 < width)
|
||||
neighbours.add(new AStarNeighbour(map[x+1][y], AStarNeighbour.Location.Diagonal)); // East.
|
||||
if(x+1 < width && y+1 < hight)
|
||||
neighbours.add(new AStarNeighbour(map[x+1][y+1], AStarNeighbour.Location.Adjacent)); // South-East.
|
||||
if(y+1 < hight)
|
||||
neighbours.add(new AStarNeighbour(map[x][y+1], AStarNeighbour.Location.Diagonal)); // South.
|
||||
if(x-1 >= 0 && y+1 < hight)
|
||||
neighbours.add(new AStarNeighbour(map[x-1][y+1], AStarNeighbour.Location.Adjacent)); // South-West.
|
||||
if(x-1 >= 0)
|
||||
neighbours.add(new AStarNeighbour(map[x-1][y], AStarNeighbour.Location.Diagonal)); // West.
|
||||
if(x-1 >= 0 && y-1 > 0)
|
||||
neighbours.add(new AStarNeighbour(map[x-1][y-1], AStarNeighbour.Location.Adjacent)); // North-West.
|
||||
|
||||
map[x][y].setNeighbours(neighbours);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package ei.game.algo;
|
||||
|
||||
public class AStarNeighbour {
|
||||
|
||||
private Location location;
|
||||
private AStarNode node;
|
||||
|
||||
public AStarNeighbour(AStarNode node, Location location) {
|
||||
this.node = node;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public AStarNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public enum Location {
|
||||
Adjacent,
|
||||
Diagonal
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ei.game.algo.AStarNeighbour.Location;
|
||||
|
||||
/**
|
||||
* This Node class defines the Node that make up grids, paths etc.
|
||||
*
|
||||
* @author Árni Arent
|
||||
*
|
||||
*/
|
||||
public abstract class AStarNode implements Comparable {
|
||||
|
||||
protected int nodeId;
|
||||
protected List<AStarNeighbour> neighbours;
|
||||
protected AStarNode parent;
|
||||
protected boolean blocked;
|
||||
protected boolean visited;
|
||||
protected int visitOrder;
|
||||
protected boolean partOfPath;
|
||||
public float costFromStart;
|
||||
protected float estimatedCostToGoal;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nodeId
|
||||
*/
|
||||
public AStarNode(int nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public AStarNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
public void setParent(AStarNode parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the neighbours of this node.
|
||||
* Usually in grid maps the neighbours are positioned in north,
|
||||
* north-east, east, south-east, south, south-west, west and north-west.
|
||||
*
|
||||
* @return the neighbours of this node.
|
||||
*/
|
||||
public List<AStarNeighbour> getNeighbours() {
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the neighbours of this Node.
|
||||
* Usually in grid maps the neighbours are positioned in north,
|
||||
* north-east, east, south-east, south, south-west, west and north-west.
|
||||
*
|
||||
* @param neighbours the neighbours of this Node.
|
||||
*/
|
||||
public void setNeighbours(List<AStarNeighbour> neighbours) {
|
||||
this.neighbours = neighbours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the id of the Node. The id is a unique integer number
|
||||
* defining the number of the node.
|
||||
* Grid maps of size 64x64 have 4096 nodes, so each node has a
|
||||
* id ranging from 0 to 4095.
|
||||
* This can usually be thought of as a auto incremental unique
|
||||
* identifier number, each Node created gets a incremented unique
|
||||
* id.
|
||||
*
|
||||
* @return the id of this Node.
|
||||
*/
|
||||
public int getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tells if this Node is blocked or not.
|
||||
*
|
||||
* @return true if the node is blocked, false if not.
|
||||
*/
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined if this Node should be blocked or not.
|
||||
*
|
||||
* @param blocked true if it should be blocked, false if not.
|
||||
*/
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
public boolean isVisited() {
|
||||
return visited;
|
||||
}
|
||||
|
||||
public void setVisited(boolean visited) {
|
||||
this.visited = visited;
|
||||
}
|
||||
|
||||
public void setVisitOrder(int order) {
|
||||
this.visitOrder = order;
|
||||
}
|
||||
|
||||
public int getVisitOrder() {
|
||||
return visitOrder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isPartOfPath() {
|
||||
return partOfPath;
|
||||
}
|
||||
|
||||
public void setPartOfPath(boolean partOfPath) {
|
||||
this.partOfPath = partOfPath;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
visited = false;
|
||||
visitOrder = 0;
|
||||
parent = null;
|
||||
partOfPath = false;
|
||||
estimatedCostToGoal = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Node " + nodeId + " ("+(blocked?"X":" ")+")]";
|
||||
}
|
||||
|
||||
public float getEstimatedCostFromStartToGoal() {
|
||||
return costFromStart+estimatedCostToGoal;
|
||||
}
|
||||
|
||||
public abstract float getEstimatedCostTo(AStarNode node, AStarNode startNode);
|
||||
|
||||
public abstract float getCost(AStarNode node, Location location);
|
||||
|
||||
public int compareTo(Object n) {
|
||||
float cost = getEstimatedCostFromStartToGoal() - ((AStarNode)n).getEstimatedCostFromStartToGoal();
|
||||
|
||||
if(cost > 0)
|
||||
return 1;
|
||||
else if(cost < 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public abstract int getX();
|
||||
|
||||
public abstract int getY();
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import ei.game.algo.AStarNeighbour.Location;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Árni Arent
|
||||
*
|
||||
*/
|
||||
public class AStarNode2D extends AStarNode {
|
||||
protected final static float adjacentCost = 1;
|
||||
protected final static float diagonalCost = (float)Math.sqrt(2)*adjacentCost;
|
||||
protected final static float tieBreaker = adjacentCost/(1024f/1024f);
|
||||
|
||||
public enum Heuristic {
|
||||
Manhattan,
|
||||
Euclidean,
|
||||
Diagonal,
|
||||
DiagonalWithTieBreaking,
|
||||
DiagonalWithTieBreakingCrossProduct,
|
||||
}
|
||||
|
||||
public static Heuristic heuristic = Heuristic.Euclidean;
|
||||
|
||||
protected int x, y;
|
||||
|
||||
public AStarNode2D(int x, int y, int nodeId) {
|
||||
super(nodeId);
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getEstimatedCostTo(AStarNode node, AStarNode startNode) {
|
||||
AStarNode2D goal = (AStarNode2D)node;
|
||||
AStarNode2D start = (AStarNode2D)startNode;
|
||||
|
||||
float h = 0;
|
||||
|
||||
/*
|
||||
* Manhattan distance heuristics. (no diagonal treatment)
|
||||
*/
|
||||
if(heuristic == Heuristic.Manhattan) {
|
||||
h = adjacentCost * (Math.abs(x-goal.x) + Math.abs(y-goal.y));
|
||||
}
|
||||
|
||||
/*
|
||||
* Euclidean distances (move at any angle, again no diagonal treatment).
|
||||
*/
|
||||
else if(heuristic == Heuristic.Euclidean) {
|
||||
h = adjacentCost * (float)Math.sqrt(Math.pow(x-goal.x, 2) + Math.pow(y-goal.y,2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Diagonal distance heuristic.
|
||||
*/
|
||||
else if(heuristic == Heuristic.Diagonal ||
|
||||
heuristic == Heuristic.DiagonalWithTieBreaking ||
|
||||
heuristic == Heuristic.DiagonalWithTieBreakingCrossProduct) {
|
||||
float diagonal = Math.min(Math.abs(x-goal.x), Math.abs(y-goal.y));
|
||||
float straight = (Math.abs(x-goal.x) + Math.abs(y-goal.y));
|
||||
h = (diagonalCost * diagonal) + (adjacentCost * (straight - (2*diagonal)));
|
||||
|
||||
|
||||
/*
|
||||
* Normal tie-breaking.
|
||||
*/
|
||||
if(heuristic == Heuristic.DiagonalWithTieBreaking) {
|
||||
h *= (1.0 + tieBreaker);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add tie-breaking cross-product to the heuristics.
|
||||
* (Produces nicer looking diagonal paths, but weird with obstacles)
|
||||
*/
|
||||
else if(heuristic == Heuristic.DiagonalWithTieBreakingCrossProduct) {
|
||||
float dx1 = x - goal.x;
|
||||
float dy1 = y - goal.y;
|
||||
float dx2 = start.x - goal.x;
|
||||
float dy2 = start.y - goal.y;
|
||||
float cross = Math.abs(dx1*dy2 - dx2*dy1);
|
||||
h += cross*0.001;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the heuristic.
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCost(AStarNode node, Location location) {
|
||||
|
||||
if(heuristic == Heuristic.Manhattan || heuristic == Heuristic.Euclidean) {
|
||||
return adjacentCost;
|
||||
} else {
|
||||
if(location == Location.Adjacent) {
|
||||
return adjacentCost;
|
||||
} else {
|
||||
return diagonalCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
294
src/ei/game/algo/AStarPathFinder.java
Normal file
294
src/ei/game/algo/AStarPathFinder.java
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* A* path finder across a tile map
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public strictfp class AStarPathFinder implements PathFinder {
|
||||
/** The map being searched */
|
||||
private TileMap map;
|
||||
/** The set describing the properties of the tiles on the map */
|
||||
private TileSet set;
|
||||
/** The distance from the end point each point on the map is */
|
||||
private int[] distance;
|
||||
/** The maximum search depth before giving up */
|
||||
private int maxsearch;
|
||||
/** The starting x coordinate */
|
||||
private int sx;
|
||||
/** The starting y coordiante */
|
||||
private int sy;
|
||||
/** The callback notified as nodes are processed */
|
||||
private PathFinderCallback callback;
|
||||
/** The currently open nodes */
|
||||
private ArrayList<Step> open = new ArrayList<Step>();
|
||||
|
||||
/**
|
||||
* Create a new path finder based on the A* algorithm
|
||||
*
|
||||
* @param map The map being searched
|
||||
* @param set The set describing the tiles on the map
|
||||
*/
|
||||
public AStarPathFinder(TileMap map,TileSet set) {
|
||||
this(map,set,null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new path finder based on the A* algorithm
|
||||
*
|
||||
* @param map The map being searched
|
||||
* @param set The set describing the tiles on the map
|
||||
* @param callback The callback notified as nodes are traversed
|
||||
*/
|
||||
public AStarPathFinder(TileMap map,TileSet set,PathFinderCallback callback) {
|
||||
this.map = map;
|
||||
this.set = set;
|
||||
this.callback = callback;
|
||||
distance = new int[map.getMapWidth()*map.getMapHeight()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinder#reset()
|
||||
*/
|
||||
public void reset() {
|
||||
open.clear();
|
||||
Arrays.fill(distance,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinder#getSearchData()
|
||||
*/
|
||||
public int[] getSearchData() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinder#findPath(int, int, int, int, int)
|
||||
*/
|
||||
public synchronized Path findPath(int sx,int sy,int dx,int dy,int maxsearch) {
|
||||
this.sx = sx;
|
||||
this.sy = sy;
|
||||
|
||||
this.maxsearch = maxsearch;
|
||||
|
||||
Step step = new Step(null,dx,dy);
|
||||
open.add(step);
|
||||
|
||||
return processNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the nodes of the search graph
|
||||
*
|
||||
* @return The path found or null if no path could be found
|
||||
*/
|
||||
private Path processNodes() {
|
||||
Step step = findBest();
|
||||
|
||||
while (!step.is(sx,sy)) {
|
||||
for (int x=-1;x<2;x++) {
|
||||
for (int y=-1;y<2;y++) {
|
||||
if ((x != 0) || (y != 0)) {
|
||||
int xp = step.x + x;
|
||||
int yp = step.y + y;
|
||||
|
||||
if (!checkDiaganolBlock(step.x,step.y,x,y)) {
|
||||
if (validNode(xp,yp)) {
|
||||
open.add(new Step(step,xp,yp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
step = findBest();
|
||||
if (step == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
distance[step.x+(step.y*map.getMapWidth())] = 1;
|
||||
callback.fireNode(step.x,step.y);
|
||||
}
|
||||
}
|
||||
|
||||
Path path = new Path();
|
||||
|
||||
while (step.parent != null) {
|
||||
path.addPoint(step.x,step.y);
|
||||
step = step.parent;
|
||||
}
|
||||
path.addPoint(step.x,step.y);
|
||||
|
||||
System.out.println();
|
||||
for(int y=0; y<map.getMapHeight() ;y++){
|
||||
System.out.println();
|
||||
for(int x=0; x<map.getMapWidth() ;x++){
|
||||
System.out.print(map.getTileAt(x, y, 0));
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a particular move is actually valid
|
||||
*
|
||||
* @param x The original x coordinate
|
||||
* @param y The original y coordinate
|
||||
* @param xo The x direction of movement
|
||||
* @param yo The y direction of movement
|
||||
* @return True if the move is valid
|
||||
*/
|
||||
private boolean checkDiaganolBlock(int x,int y,int xo,int yo) {
|
||||
if ((xo == 0) || (yo == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isBlocked(x+xo,y)) {
|
||||
return true;
|
||||
}
|
||||
if (isBlocked(x,y+yo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a particular node on the graph is valid to check
|
||||
*
|
||||
* @param x The x position of the node to check
|
||||
* @param y The y position of the node to check
|
||||
* @return True if the node is valid to evaluate
|
||||
*/
|
||||
private boolean validNode(int x,int y) {
|
||||
if (isBlocked(x,y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return distance[x+(y*map.getMapWidth())] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the best open state currently available, i.e. the
|
||||
* best direction to move in
|
||||
*
|
||||
* @return The best step to take or null if there are no more
|
||||
* steps left (no path)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Step findBest() {
|
||||
Collections.sort(open);
|
||||
|
||||
if (open.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
Step best = (Step) open.get(0);
|
||||
best.count++;
|
||||
if ((best.count > 9) || (best.depth > maxsearch)) {
|
||||
open.remove(best);
|
||||
return findBest();
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the heuristic for a particular node
|
||||
*
|
||||
* @param x The x position of the node to evaluate
|
||||
* @param y The y position of the node to evaluate
|
||||
* @return The heuristic for the specified node
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int evalH(int x,int y) {
|
||||
return Math.abs(sx-x) + Math.abs(sy-y);
|
||||
}
|
||||
|
||||
/**
|
||||
* A step on the search path
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
private class Step implements Comparable {
|
||||
/** The x position of this step */
|
||||
public int x;
|
||||
/** The y position of this step */
|
||||
public int y;
|
||||
/** The heuristic for this step's node */
|
||||
public int h;
|
||||
/** The number of times this step has been used as a source */
|
||||
public int count;
|
||||
/** The depth of this search */
|
||||
public int depth;
|
||||
/** The parent step this step was spawned from */
|
||||
public Step parent;
|
||||
|
||||
/**
|
||||
* Create a new step
|
||||
*
|
||||
* @param parent The step we came from
|
||||
* @param x The x position we've moved to
|
||||
* @param y The y position we've moved to
|
||||
*/
|
||||
public Step(Step parent,int x,int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.parent = parent;
|
||||
if (parent != null) {
|
||||
depth = parent.depth+1;
|
||||
} else {
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
h = Math.abs(sx-x) + Math.abs(sy-y);
|
||||
|
||||
if (depth <= maxsearch) {
|
||||
distance[x+(y*map.getMapWidth())] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like an equals method, only for the data inside
|
||||
*
|
||||
* @param x The x position to check against
|
||||
* @param y The y position to check against
|
||||
* @return True if this steps position is that specified
|
||||
*/
|
||||
public boolean is(int x,int y) {
|
||||
return (x == this.x) && (y == this.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||
*/
|
||||
public int compareTo(Object o) {
|
||||
return h - ((Step) o).h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a particular location on the map blocks movement
|
||||
*
|
||||
* @param x The x location to check
|
||||
* @param y The y location to check
|
||||
* @return True if the locaiton is blocked
|
||||
*/
|
||||
public boolean isBlocked(int x,int y) {
|
||||
if(x == sx && y == sy){
|
||||
return false;
|
||||
}
|
||||
if (map.isBlocked(x,y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return set.blocksMovement(map.getTileAt(x,y,0));
|
||||
}
|
||||
}
|
||||
211
src/ei/game/algo/DJKPathFinder.java
Normal file
211
src/ei/game/algo/DJKPathFinder.java
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* DJK path finder across a tile map
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class DJKPathFinder implements PathFinder {
|
||||
/** The map being searchd */
|
||||
private TileMap map;
|
||||
/** The tileset describing the tile properties on the map */
|
||||
private TileSet set;
|
||||
/** The distances from the current destination on the map */
|
||||
private int[] distance;
|
||||
/** The maximum search depth reached before giving up */
|
||||
private int maxsearch;
|
||||
/** The starting x coordinate */
|
||||
private int sx;
|
||||
/** The starting y coordinate */
|
||||
private int sy;
|
||||
/** The distance from the destination of the best path found so far */
|
||||
private int bestPath = 10000;
|
||||
|
||||
/**
|
||||
* Create a new DJK based path finder
|
||||
*
|
||||
* @param map The map being searched
|
||||
* @param set The set describing the tiles on the map
|
||||
*/
|
||||
public DJKPathFinder(TileMap map,TileSet set) {
|
||||
this.map = map;
|
||||
this.set = set;
|
||||
distance = new int[map.getMapWidth()*map.getMapHeight()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinder#reset()
|
||||
*/
|
||||
public void reset() {
|
||||
Arrays.fill(distance,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinder#getSearchData()
|
||||
*/
|
||||
public int[] getSearchData() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinder#findPath(int, int, int, int, int)
|
||||
*/
|
||||
public synchronized Path findPath(int sx,int sy,int dx,int dy,int maxsearch) {
|
||||
int depth = 1;
|
||||
this.sx = sx;
|
||||
this.sy = sy;
|
||||
bestPath = 10000;
|
||||
|
||||
this.maxsearch = maxsearch;
|
||||
|
||||
if (processNode(dx,dy,depth)) {
|
||||
Path path = new Path();
|
||||
|
||||
findNextPoint(path,sx,sy,distance[sx+(sy*map.getMapWidth())]-1);
|
||||
return path;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the next point on the discovered path by moving
|
||||
* along the distances. After finding the path by DJK we need
|
||||
* to trace back along the distances working out how we got to the
|
||||
* end.
|
||||
*
|
||||
* @param path The path being built up
|
||||
* @param x The current x position
|
||||
* @param y The current y position
|
||||
* @param d The distance we're searching for at the moment.
|
||||
*/
|
||||
private void findNextPoint(Path path,int x,int y,int d) {
|
||||
if (d < 0) {
|
||||
return;
|
||||
}
|
||||
path.addPoint(x,y);
|
||||
|
||||
for (int xo=-1;xo<2;xo++) {
|
||||
for (int yo=-1;yo<2;yo++) {
|
||||
if ((xo != 0) || (yo != 0)) {
|
||||
if ((xo == 0) || (yo == 0)) {
|
||||
int sx = x+xo;
|
||||
int sy = y+yo;
|
||||
|
||||
if (distance[sx+(sy*map.getMapWidth())] == d) {
|
||||
findNextPoint(path,sx,sy,distance[sx+(sy*map.getMapWidth())]-1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int xo=-1;xo<2;xo++) {
|
||||
for (int yo=-1;yo<2;yo++) {
|
||||
if ((xo != 0) && (yo != 0)) {
|
||||
int sx = x+xo;
|
||||
int sy = y+yo;
|
||||
|
||||
if (distance[sx+(sy*map.getMapWidth())] == d) {
|
||||
findNextPoint(path,sx,sy,distance[sx+(sy*map.getMapWidth())]-1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the specified graph node (in this case grid cell)
|
||||
*
|
||||
* @param x The x location we're processing
|
||||
* @param y The y location we're processing
|
||||
* @param depth The depth of the search we're at
|
||||
* @return True if we're reached the destination
|
||||
*/
|
||||
private boolean processNode(int x,int y,int depth) {
|
||||
if (depth >= maxsearch) {
|
||||
return false;
|
||||
}
|
||||
if (depth >= bestPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((x < 0) || (y < 0) || (x >= map.getMapWidth()) || (y >= map.getMapHeight())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isBlocked(x,y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int val = distance[x+(y*map.getMapWidth())];
|
||||
|
||||
if ((val != 0) && (val <= depth)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
distance[x+(y*map.getMapWidth())] = depth;
|
||||
|
||||
if ((x == sx) && (y == sy)) {
|
||||
bestPath = depth;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
|
||||
for (int xo=-1;xo<2;xo++) {
|
||||
for (int yo=-1;yo<2;yo++) {
|
||||
if ((xo != 0) || (yo != 0)) {
|
||||
if (validMove(x,y,xo,yo)) {
|
||||
found |= processNode(x+xo,y+yo,depth+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the move specified is valid
|
||||
*
|
||||
* @param x The x position we're starting from
|
||||
* @param y The y position we're starting from
|
||||
* @param xo The x direction we're moving in
|
||||
* @param yo The y direction we're moving in
|
||||
* @return True if the move specified is valid
|
||||
*/
|
||||
private boolean validMove(int x,int y,int xo,int yo) {
|
||||
if ((xo == 0) || (yo == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isBlocked(x+xo,y)) {
|
||||
return false;
|
||||
}
|
||||
if (isBlocked(x,y+yo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a particular location on the map blocks movement
|
||||
*
|
||||
* @param x The x location to check
|
||||
* @param y The y location to check
|
||||
* @return True if the locaiton is blocked
|
||||
*/
|
||||
public boolean isBlocked(int x,int y) {
|
||||
if (map.isBlocked(x,y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return set.blocksMovement(map.getTileAt(x,y,0));
|
||||
}
|
||||
}
|
||||
88
src/ei/game/algo/Path.java
Normal file
88
src/ei/game/algo/Path.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import ei.engine.math.Vector2i;
|
||||
|
||||
/**
|
||||
* A path from one point to another generated by the path finding code
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class Path {
|
||||
/** The maximum path length */
|
||||
private static final int MAX_PATH = 200;
|
||||
/** The path coordinates */
|
||||
private int[][] path = new int[MAX_PATH][2];
|
||||
/** The size of the path */
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Create a new empty path
|
||||
*/
|
||||
public Path() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the path
|
||||
*
|
||||
* @param x The x coordinate of the point being added
|
||||
* @param y The y coordinate of the point being added
|
||||
*/
|
||||
public void addPoint(int x,int y) {
|
||||
path[size][0] = x;
|
||||
path[size][1] = y;
|
||||
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X coordinate of the point at the specified index
|
||||
*
|
||||
* @param index The index of the point to retrieve
|
||||
* @return The x coordinate of the point at the specified index
|
||||
*/
|
||||
public int getX(int index) {
|
||||
return path[index][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y coordinate of the point at the specified index
|
||||
*
|
||||
* @param index The index of the point to retrieve
|
||||
* @return The y coordinate of the point at the specified index
|
||||
*/
|
||||
public int getY(int index) {
|
||||
return path[index][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size/length of the path
|
||||
*
|
||||
* @return The size of the path
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public ArrayList<Vector2i> getArray(){
|
||||
ArrayList<Vector2i> array = new ArrayList<Vector2i>();
|
||||
|
||||
for(int i=0; i<size ;i++){
|
||||
array.add(new Vector2i(path[i][0],path[i][1]));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public LinkedList<Vector2i> getList(){
|
||||
LinkedList<Vector2i> array = new LinkedList<Vector2i>();
|
||||
|
||||
for(int i=0; i<size ;i++){
|
||||
array.add(new Vector2i(path[i][0],path[i][1]));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
44
src/ei/game/algo/PathFinder.java
Normal file
44
src/ei/game/algo/PathFinder.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package ei.game.algo;
|
||||
|
||||
/**
|
||||
* A description of any implementation of path finding. The class will
|
||||
* be responsible for finding paths between two points. The map and tileset
|
||||
* being searched will be specified in the constructor to the particular
|
||||
* search object.
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public interface PathFinder {
|
||||
/**
|
||||
* Reset the internal state of the path finder. This is expected to be
|
||||
* called between path finds to clear out search data.
|
||||
*
|
||||
* Note, implementations should endevour to keep this an efficient method
|
||||
*/
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Retrieve an array of distances for different points on the map. This
|
||||
* data is likely to be cleared after a call to <code>reset()</code>.
|
||||
*
|
||||
* This data is probably only useful for test/debug tools.
|
||||
*
|
||||
* @return An array of distances from the end point of the last search
|
||||
* for different points on the map.
|
||||
*/
|
||||
public abstract int[] getSearchData();
|
||||
|
||||
/**
|
||||
* Find a path from a starting location to a destination point.
|
||||
*
|
||||
* @param sx The starting x coordinate
|
||||
* @param sy The starting y coordinate
|
||||
* @param dx The destination x coordinate
|
||||
* @param dy The destination y coordinate
|
||||
* @param maxsearch The maximum search depth that will be reached
|
||||
* during the search. This is helpful for restricting the amount of
|
||||
* time a search may take
|
||||
* @return The path found or null if no path could be determined
|
||||
*/
|
||||
public abstract Path findPath(int sx, int sy, int dx, int dy, int maxsearch);
|
||||
}
|
||||
17
src/ei/game/algo/PathFinderCallback.java
Normal file
17
src/ei/game/algo/PathFinderCallback.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package ei.game.algo;
|
||||
|
||||
/**
|
||||
* A simple interface for testing path finding. This allows you
|
||||
* to watch the nodes being searched by the finder
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public interface PathFinderCallback {
|
||||
/**
|
||||
* Notification that a particular node has been reached
|
||||
*
|
||||
* @param x The x coordinate of the node reached
|
||||
* @param y The y coordinate of the node reached
|
||||
*/
|
||||
public void fireNode(int x,int y);
|
||||
}
|
||||
243
src/ei/game/algo/TestPathFinder.java
Normal file
243
src/ei/game/algo/TestPathFinder.java
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
package ei.game.algo;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
/**
|
||||
* A test for the different path finding implementations defined
|
||||
*
|
||||
* Note: Like all the tests this isn't designed to be a robust piece
|
||||
* of software, rather a testing tool.
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class TestPathFinder extends JPanel implements TileSet, TileMap, PathFinderCallback {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** The depth of search allowed */
|
||||
private static final int SEARCH_DEPTH = 500;
|
||||
|
||||
/** The map being searched */
|
||||
private int[][] map = new int[100][100];
|
||||
/** The current start x coordinate */
|
||||
private int sx = 5;
|
||||
/** The current start y coordinate */
|
||||
private int sy = 5;
|
||||
/** The current end x coordinate */
|
||||
private int dx = 5;
|
||||
/** The current end y coordinate */
|
||||
private int dy = 5;
|
||||
/** The finder searching the map */
|
||||
private PathFinder finder;
|
||||
/** The last path found */
|
||||
private Path path;
|
||||
|
||||
/**
|
||||
* Create a test for path finding
|
||||
*/
|
||||
public TestPathFinder() {
|
||||
generateData(map);
|
||||
|
||||
finder = new AStarPathFinder(this,this,this);
|
||||
//finder = new DJKPathFinder(this,this);
|
||||
|
||||
JFrame frame = new JFrame();
|
||||
frame.setContentPane(new JScrollPane(this));
|
||||
|
||||
addMouseListener(new MouseHandler());
|
||||
setPreferredSize(new Dimension(1000,1000));
|
||||
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
frame.setSize(800,600);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random map for testing against, worst
|
||||
* alogrithm ever?
|
||||
*
|
||||
* @param data The data array to generate the map into
|
||||
*/
|
||||
private void generateData(int[][] data) {
|
||||
for (int x=0;x<100;x++) {
|
||||
for (int y=0;y<100;y++) {
|
||||
data[x][y] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int count = (int) (Math.random() * 110) + 40;
|
||||
for (int i=0;i<count;i++) {
|
||||
int xs = (int) (Math.random() * 100);
|
||||
int ys = (int) (Math.random() * 100);
|
||||
int w = (int) (Math.random() * 10) + 6;
|
||||
int h = (int) (Math.random() * 10) + 6;
|
||||
|
||||
for (int x=xs;x<xs+w;x++) {
|
||||
for (int y=ys;y<ys+h;y++) {
|
||||
if ((x > 0) && (y > 0) && (x < 99) && (y < 99)) {
|
||||
data[x][y] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
data[2][1] = 1;
|
||||
data[1][1] = 0;
|
||||
data[1][2] = 0;
|
||||
data[1][3] = 0;
|
||||
data[2][3] = 0;
|
||||
data[2][4] = 0;
|
||||
data[2][5] = 0;
|
||||
data[2][6] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.awt.Component#paint(java.awt.Graphics)
|
||||
*/
|
||||
public void paint(Graphics g) {
|
||||
g.setColor(Color.black);
|
||||
g.fillRect(0,0,1000,1000);
|
||||
|
||||
for (int x=0;x<100;x++) {
|
||||
for (int y=0;y<100;y++) {
|
||||
|
||||
if (isBlocked(x,y)) {
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(x*10,y*10,10,10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (finder.getSearchData() != null) {
|
||||
int[] data = finder.getSearchData();
|
||||
|
||||
for (int x=0;x<100;x++) {
|
||||
for (int y=0;y<100;y++) {
|
||||
if (!isBlocked(x,y)) {
|
||||
if (data[x+(y*100)] != 0) {
|
||||
g.setColor(Color.blue);
|
||||
g.fillRect(x*10,y*10,10,10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
for (int i=0;i<path.getSize();i++) {
|
||||
g.setColor(Color.yellow);
|
||||
g.fillRect(path.getX(i)*10,path.getY(i)*10,10,10);
|
||||
}
|
||||
}
|
||||
g.setColor(Color.green);
|
||||
g.fillRect(sx*10,sy*10,10,10);
|
||||
g.setColor(Color.red);
|
||||
g.fillRect(dx*10,dy*10,10,10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for mouse clicks. Attempts to find paths
|
||||
* via the path finder objects.
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
private class MouseHandler extends MouseAdapter {
|
||||
|
||||
/**
|
||||
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
|
||||
*/
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (e.getButton() == 1) {
|
||||
sx = e.getX() / 10;
|
||||
sy = e.getY() / 10;
|
||||
} else {
|
||||
dx = e.getX() / 10;
|
||||
dy = e.getY() / 10;
|
||||
|
||||
Thread t = new Thread() {
|
||||
public void run() {
|
||||
long start = System.currentTimeMillis();
|
||||
finder.reset();
|
||||
path = finder.findPath(sx,sy,dx,dy,SEARCH_DEPTH);
|
||||
long delta = System.currentTimeMillis() - start;
|
||||
if (path != null) {
|
||||
System.out.println("Path length: "+path.getSize());
|
||||
} else {
|
||||
System.out.println("No path found");
|
||||
}
|
||||
System.out.println("Path find took: "+delta+" milliseconds");
|
||||
repaint();
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.TileMap#getTileAt(int, int, int)
|
||||
*/
|
||||
public int getTileAt(int x, int y, int layer) {
|
||||
return map[x][y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.TileMap#isBlocked(int, int)
|
||||
*/
|
||||
public boolean isBlocked(int x, int y) {
|
||||
return map[x][y] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.TileMap#getMapWidth()
|
||||
*/
|
||||
public int getMapWidth() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.TileMap#getMapHeight()
|
||||
*/
|
||||
public int getMapHeight() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point to the path finding test
|
||||
*
|
||||
* @param argv The arguments into the test (none)
|
||||
*/
|
||||
public static void main(String[] argv) {
|
||||
new TestPathFinder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.PathFinderCallback#fireNode(int, int)
|
||||
*/
|
||||
public void fireNode(int x, int y) {
|
||||
//repaint(0);
|
||||
//try { Thread.sleep(100); } catch (Exception e) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.util.map.TileSet#blocksMovement(int)
|
||||
*/
|
||||
public boolean blocksMovement(int tile) {
|
||||
return tile != 0;
|
||||
}
|
||||
}
|
||||
44
src/ei/game/algo/TileMap.java
Normal file
44
src/ei/game/algo/TileMap.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package ei.game.algo;
|
||||
|
||||
/**
|
||||
* The description of a data source for rendering a map
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public interface TileMap {
|
||||
|
||||
/**
|
||||
* Get the tile at a specified location. The index returned should
|
||||
* allow the renderer to look up a renderable object in a renderer
|
||||
* dependent tileset.
|
||||
*
|
||||
* @param x The x position of the tile to retrieve
|
||||
* @param y The y position of the tile to retrieve
|
||||
* @param layer The layer of the tile to retrieve (zero being the base)
|
||||
* @return The tile at the specified location
|
||||
*/
|
||||
public int getTileAt(int x,int y,int layer);
|
||||
|
||||
/**
|
||||
* Get the width of the map in tiles
|
||||
*
|
||||
* @return The width of the map in tiles
|
||||
*/
|
||||
public int getMapWidth();
|
||||
|
||||
/**
|
||||
* Get the height of the map in tiles
|
||||
*
|
||||
* @return The height of the map in tiles
|
||||
*/
|
||||
public int getMapHeight();
|
||||
|
||||
/**
|
||||
* Is a particular tile location current blocked
|
||||
*
|
||||
* @param x The x coordinate of the tile location to check
|
||||
* @param y The y coordinate of the tile location to check
|
||||
* @return True if the specified locaiton is blocked
|
||||
*/
|
||||
public boolean isBlocked(int x,int y);
|
||||
}
|
||||
17
src/ei/game/algo/TileSet.java
Normal file
17
src/ei/game/algo/TileSet.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package ei.game.algo;
|
||||
|
||||
/**
|
||||
* A set of data describing the properties of different tiles
|
||||
* used on a map.
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public interface TileSet {
|
||||
/**
|
||||
* Check if the specified tile blocks movement
|
||||
*
|
||||
* @param tile The tile to check
|
||||
* @return True if the tile specified blocks movement
|
||||
*/
|
||||
public boolean blocksMovement(int tile);
|
||||
}
|
||||
|
|
@ -12,6 +12,9 @@ import ei.engine.scene.Entity;
|
|||
import ei.engine.scene.Node;
|
||||
import ei.engine.scene.Sprite;
|
||||
import ei.engine.util.MultiPrintStream;
|
||||
import ei.game.algo.PathFinderCallback;
|
||||
import ei.game.algo.TileMap;
|
||||
import ei.game.algo.TileSet;
|
||||
import ei.game.player.GaiaPlayer;
|
||||
import ei.game.player.Player;
|
||||
import ei.game.player.PlayerHandler;
|
||||
|
|
@ -20,7 +23,7 @@ import ei.game.scene.map.Prison;
|
|||
import ei.game.scene.map.Stone;
|
||||
import ei.game.scene.map.Water;
|
||||
|
||||
public class Map {
|
||||
public class Map implements TileMap, TileSet, PathFinderCallback{
|
||||
public static final int MAP_GRASS = 0;
|
||||
public static final int MAP_SAND = 1;
|
||||
public static final int MAP_REDMUD = 2;
|
||||
|
|
@ -396,4 +399,39 @@ public class Map {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//*************************************************************
|
||||
//*************************************************************
|
||||
//Methods for the path finding algo
|
||||
|
||||
public int getMapHeight() {
|
||||
return hight;
|
||||
}
|
||||
|
||||
public int getMapWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getTileAt(int x, int y, int layer) {
|
||||
if(map[x][y] == null){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean isBlocked(int x, int y) {
|
||||
if(x < 0 || x >= width || y < 0 || y >= hight){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean blocksMovement(int tile) {
|
||||
return tile != 0;
|
||||
}
|
||||
|
||||
public void fireNode(int x, int y) {
|
||||
//System.out.println(x+" "+y+" "+getTileAt(x,y,0)+" "+blocksMovement(getTileAt(x,y,0)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ import ei.engine.math.Vector2i;
|
|||
import ei.engine.math.Vector3f;
|
||||
import ei.engine.scene.Node;
|
||||
import ei.engine.sound.Sound;
|
||||
import ei.game.algo.AStar;
|
||||
import ei.game.algo.AStarNode;
|
||||
import ei.game.algo.AStarPathFinder;
|
||||
import ei.game.algo.Path;
|
||||
import ei.game.algo.PathFinder;
|
||||
import ei.game.gamestate.InGameState;
|
||||
import ei.game.player.Player;
|
||||
import ei.game.scene.GameEntity;
|
||||
|
|
@ -38,7 +39,9 @@ public abstract class Unit extends GameEntity{
|
|||
private int weponTimer;
|
||||
private int autoAttackTimer;
|
||||
// The path to travel
|
||||
private LinkedList<AStarNode> path;
|
||||
private LinkedList<Vector2i> path;
|
||||
/** The finder searching the map */
|
||||
private static PathFinder pathfinder;
|
||||
private Vector2i target;
|
||||
private boolean autoAttack;
|
||||
|
||||
|
|
@ -53,6 +56,9 @@ public abstract class Unit extends GameEntity{
|
|||
unitNode.setLocation(Map.getPixelByPos(pos.getX(), pos.getY()));
|
||||
setPos(pos.getX(), pos.getY());
|
||||
autoAttack = true;
|
||||
if(pathfinder == null){
|
||||
pathfinder = new AStarPathFinder(InGameState.getMap(),InGameState.getMap(),InGameState.getMap());
|
||||
}
|
||||
init();
|
||||
initGraphics();
|
||||
}
|
||||
|
|
@ -191,10 +197,15 @@ public abstract class Unit extends GameEntity{
|
|||
if(b){
|
||||
attack = null;
|
||||
}
|
||||
path = (LinkedList<AStarNode>) new AStar().startSearch(oldPos,new Vector2i(x,y));
|
||||
|
||||
System.out.println(oldPos.getX()+" "+oldPos.getY()+"-"+x+" "+y);
|
||||
Path p = pathfinder.findPath(oldPos.getX(),oldPos.getY(),x,y,600);
|
||||
pathfinder.reset();
|
||||
if(p != null) path = p.getList();
|
||||
else path = null;
|
||||
|
||||
if(path != null && !path.isEmpty() && moveTo == null){
|
||||
AStarNode temp = path.poll();
|
||||
Vector2i temp = path.poll();
|
||||
oldVect = new Vector2i((int)unitNode.getLocation().getX(), (int)unitNode.getLocation().getY());
|
||||
moveTo = Map.getPixelByPos(temp.getX(), temp.getY());
|
||||
setPos(temp.getX(), temp.getY());
|
||||
|
|
@ -317,7 +328,7 @@ public abstract class Unit extends GameEntity{
|
|||
moveTo = null;
|
||||
}
|
||||
else if(path != null && !path.isEmpty()){
|
||||
AStarNode temp = path.poll();
|
||||
Vector2i temp = path.poll();
|
||||
if(InGameState.getMap().isPosEmpty(temp.getX(), temp.getY())){
|
||||
oldVect = new Vector2i((int)moveTo.getX(), (int)moveTo.getY());
|
||||
moveTo = Map.getPixelByPos(temp.getX(), temp.getY());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue