Added extremely basic path-finding algorithm

Added extremely basic path-finding algorithm
Added accessor to Zone.flags
This commit is contained in:
Collin Smith
2019-02-17 18:50:14 -08:00
parent 98348bd652
commit 95d61fc4d7
5 changed files with 380 additions and 0 deletions

View File

@ -164,6 +164,7 @@ project(":core") {
compile "com.badlogicgames.gdx:gdx:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
compile "com.badlogicgames.ashley:ashley:$ashleyVersion"
compile "com.badlogicgames.gdx:gdx-ai:1.8.+"
}
dependencies {

View File

@ -3,11 +3,14 @@ package gdx.diablo.map;
import com.google.common.base.Preconditions;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ai.pfa.DefaultGraphPath;
import com.badlogic.gdx.ai.pfa.GraphPath;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Bits;
import com.badlogic.gdx.utils.Disposable;
@ -543,6 +546,11 @@ public class Map implements Disposable {
}
}
public GraphPath<MapUtils.Point2> path(Vector3 src, Vector3 dst) {
//return new MapGraph(this).path(src, dst);
return MapUtils.path(this, src, dst, new DefaultGraphPath<MapUtils.Point2>());
}
static class Zone {
static final Array<Entity> EMPTY_ARRAY = new Array<>(0);
@ -662,6 +670,14 @@ public class Map implements Disposable {
return presets[(tx - this.tx) / gridSizeX][(ty - this.ty) / gridSizeY];
}
public int flags(int x, int y) {
x -= this.x;
if (x < 0 || x > width ) return 0xFF;
y -= this.y;
if (y < 0 || y > height) return 0xFF;
return flags[x][y] & 0xFF;
}
void load(DT1s dt1s) {
Preconditions.checkState(tiles == null, "tiles have already been loaded");
tiles = new Tile[Map.MAX_LAYERS][][];

View File

@ -1,6 +1,7 @@
package gdx.diablo.map;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ai.pfa.GraphPath;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
@ -137,6 +138,22 @@ public class MapRenderer {
return null;
}
public Vector3 getCursor() {
Vector3 coords = new Vector3();
coords.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(coords);
float adjustX = (int) coords.x;
float adjustY = (int) coords.y - Tile.SUBTILE_HEIGHT50;
float selectX = ( adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2;
float selectY = (-adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2;
if (selectX < 0) selectX--;
if (selectY < 0) selectY--;
coords.x = (int) selectX;
coords.y = (int) selectY;
return coords;
}
public void setMap(Map map) {
if (this.map != map) {
this.map = map;
@ -852,6 +869,88 @@ public class MapRenderer {
shapes.set(ShapeRenderer.ShapeType.Line);
}
public void renderDebugPath(ShapeRenderer shapes, Vector3 src, Vector3 dst) {
shapes.setColor(Color.TAN);
shapes.set(ShapeRenderer.ShapeType.Filled);
if (Math.abs(dst.y - src.y) < Math.abs(dst.x - src.x)) {
if (src.x > dst.x) {
plotLineLow(shapes, dst, src);
} else {
plotLineLow(shapes, src, dst);
}
} else {
if (src.y > dst.y) {
plotLineHigh(shapes, dst, src);
} else {
plotLineHigh(shapes, src, dst);
}
}
shapes.set(ShapeRenderer.ShapeType.Line);
}
private void plotLineLow(ShapeRenderer shapes, Vector3 src, Vector3 dst) {
float dx = dst.x - src.x;
float dy = dst.y - src.y;
int yi = 1;
if (dy < 0) {
yi = -1;
dy = -dy;
}
float D = 2*dy - dx;
float y = src.y;
for (float x = src.x; x <= dst.x; x++) {
float px = +((int) x * Tile.SUBTILE_WIDTH50) - ((int) y * Tile.SUBTILE_WIDTH50) - Tile.SUBTILE_WIDTH50;
float py = -((int) x * Tile.SUBTILE_HEIGHT50) - ((int) y * Tile.SUBTILE_HEIGHT50) - Tile.SUBTILE_HEIGHT50;
drawDiamondSolid(shapes, px, py, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT);
if (D > 0) {
y = y + yi;
D = D - 2*dx;
}
D = D + 2*dy;
}
}
private void plotLineHigh(ShapeRenderer shapes, Vector3 src, Vector3 dst) {
float dx = dst.x - src.x;
float dy = dst.y - src.y;
int xi = 1;
if (dx < 0) {
xi = -1;
dx = -dx;
}
float D = 2*dx - dy;
float x = src.x;
for (float y = src.y; y <= dst.y; y++) {
float px = +((int) x * Tile.SUBTILE_WIDTH50) - ((int) y * Tile.SUBTILE_WIDTH50) - Tile.SUBTILE_WIDTH50;
float py = -((int) x * Tile.SUBTILE_HEIGHT50) - ((int) y * Tile.SUBTILE_HEIGHT50) - Tile.SUBTILE_HEIGHT50;
drawDiamondSolid(shapes, px, py, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT);
if (D > 0) {
x = x + xi;
D = D - 2*dy;
}
D = D + 2*dx;
}
}
public void renderDebugPath2(ShapeRenderer shapes, GraphPath<MapUtils.Point2> path) {
shapes.setColor(Color.TAN);
shapes.set(ShapeRenderer.ShapeType.Filled);
final int size = path.getCount();
for (int i = 0; i < size; i++) {
MapUtils.Point2 point = path.get(i);
float px = +(point.x * Tile.SUBTILE_WIDTH50) - (point.y * Tile.SUBTILE_WIDTH50) - Tile.SUBTILE_WIDTH50;
float py = -(point.x * Tile.SUBTILE_HEIGHT50) - (point.y * Tile.SUBTILE_HEIGHT50) - Tile.SUBTILE_HEIGHT50;
drawDiamondSolid(shapes, px, py, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT);
}
shapes.set(ShapeRenderer.ShapeType.Line);
}
private static void drawDiamond(ShapeRenderer shapes, float x, float y, int width, int height) {
int hw = width >>> 1;
int hh = height >>> 1;
@ -860,4 +959,11 @@ public class MapRenderer {
shapes.line(x + width, y + hh , x + hw , y );
shapes.line(x + hw , y , x , y + hh );
}
private static void drawDiamondSolid(ShapeRenderer shapes, float x, float y, int width, int height) {
int hw = width >>> 1;
int hh = height >>> 1;
shapes.triangle(x, y + hh, x + hw, y + height, x + width, y + hh);
shapes.triangle(x, y + hh, x + hw, y , x + width, y + hh);
}
}

View File

@ -0,0 +1,192 @@
package gdx.diablo.map;
import com.badlogic.gdx.ai.pfa.GraphPath;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
public class MapUtils {
private MapUtils() {}
public static GraphPath<Point2> path(Map map, Vector3 src, Vector3 dst, GraphPath<Point2> path) {
Point2 srcP = new Point2(src);
Point2 dstP = new Point2(dst);
Array<Point2> coords = new Array<>();
Array<Point2> children = new Array<>(8);
coords.add(dstP);
boolean found = false;
do {
for (Point2 point : coords) {
found = expand(map, srcP, point, coords, children);
coords.addAll(children);
if (found) break;
}
} while (!found);
if (!found) {
return path;
}
Point2 next = null;
Point2 last = coords.removeIndex(coords.size - 1);
while (true) {
for (Point2 coord : coords) {
if (last.adjacent(coord)) {
if (next == null || coord.cost < next.cost) {
next = coord;
}
}
}
path.add(next);
last = next;
next = null;
if (dstP.x == last.x && dstP.y == last.y) break;
}
return path;
}
private static boolean expand(Map map, Point2 dst, Point2 src, Array<Point2> coords, Array<Point2> children) {
children.clear();
if (check(map, dst, src, src.x - 1, src.y - 1, coords, children)) return true;
if (check(map, dst, src, src.x - 1, src.y , coords, children)) return true;
if (check(map, dst, src, src.x - 1, src.y + 1, coords, children)) return true;
if (check(map, dst, src, src.x , src.y - 1, coords, children)) return true;
if (check(map, dst, src, src.x , src.y , coords, children)) return true;
if (check(map, dst, src, src.x , src.y + 1, coords, children)) return true;
if (check(map, dst, src, src.x + 1, src.y - 1, coords, children)) return true;
if (check(map, dst, src, src.x + 1, src.y , coords, children)) return true;
if (check(map, dst, src, src.x + 1, src.y + 1, coords, children)) return true;
return false;
}
private static boolean check(Map map, Point2 dst, Point2 src, int x, int y, Array<Point2> coords, Array<Point2> children) {
Map.Zone zone = map.getZone(x, y);
if (zone != null) {
if (zone.flags(x, y) == 0) {
float cost = src.cost;
cost += (x != src.x && y != src.y) ? 1.414213562373095f : 1;
if (!contains(coords, x, y, cost)) {
Point2 point = new Point2(x, y, cost);
children.add(point);
if (dst.x == x && dst.y == y) return true;
}
}
}
return false;
}
private static boolean contains(Array<Point2> coords, int x, int y, float cost) {
for (Point2 point : new Array.ArrayIterator<>(coords)) {
if (point.x == x && point.y == y && point.cost <= cost) {
return true;
}
}
return false;
}
static class Point2 {
int x;
int y;
float cost;
Point2(int x, int y, float cost) {
this.x = x;
this.y = y;
this.cost = cost;
}
Point2(Vector3 src) {
x = (int) src.x;
y = (int) src.y;
}
boolean adjacent(Point2 other) {
return Vector2.dst2(x, y, other.x, other.y) < 2;
}
}
/*
public static Array<Vector3> path(Map map, Vector3 src, Vector3 dst) {
ObjectSet<Vector3> open = new ObjectSet<>();
ObjectSet<Vector3> closed = new ObjectSet<>();
open.add(src);
int x, y;
while (!open.isEmpty()) {
Node cur = null;
if (cur.data.equals(dst)) {
return null;
}
x = (int) cur.data.x;
y = (int) cur.data.y;
Array<Node> neighbors = getNeighbors(map, cur, x, y);
for (Node neighbor : neighbors) {
float score = cur.cost + neighbor.cost;
}
}
return null;
}
private static Array<Node> getNeighbors(Map map, Node n, int x, int y) {
Array<Node> neighbors = new Array<>(8);
addIfValid(neighbors, map, n, x - 1, y - 1);
addIfValid(neighbors, map, n, x - 1, y );
addIfValid(neighbors, map, n, x - 1, y + 1);
addIfValid(neighbors, map, n, x , y - 1);
addIfValid(neighbors, map, n, x , y );
addIfValid(neighbors, map, n, x , y + 1);
addIfValid(neighbors, map, n, x + 1, y - 1);
addIfValid(neighbors, map, n, x + 1, y );
addIfValid(neighbors, map, n, x + 1, y + 1);
return neighbors;
}
private static boolean addIfValid(Array<Node> arr, Map map, Node n, int x, int y) {
Map.Zone zone = map.getZone(x, y);
if (zone != null) {
if (zone.flags(x, y) == 0) {
Node node = new Node();
node.prev = n;
node.data = new Vector3(x, y, 0);
if (x != node.data.x && y != node.data.y) {
node.cost = 1.414213562373095f;
} else {
node.cost = 1;
}
arr.add(node);
}
}
return false;
}
static class Node {
Node prev;
Vector3 data;
float cost;
float score;
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (!(obj instanceof Node)) return false;
return data.equals(((Node) obj).data);
}
@Override
public int hashCode() {
return data.hashCode();
}
}
*/
}

View File

@ -6,9 +6,11 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.ai.pfa.GraphPath;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
@ -16,6 +18,7 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.utils.UIUtils;
import com.badlogic.gdx.utils.GdxRuntimeException;
@ -39,6 +42,7 @@ import gdx.diablo.loader.COFLoader;
import gdx.diablo.loader.DC6Loader;
import gdx.diablo.loader.DCCLoader;
import gdx.diablo.loader.TXTLoader;
import gdx.diablo.map.DT1.Tile;
import gdx.diablo.mpq.MPQFileHandleResolver;
public class MapViewer extends ApplicationAdapter {
@ -70,6 +74,10 @@ public class MapViewer extends ApplicationAdapter {
int x, y;
Vector3 src;
Vector3 dst;
GraphPath<MapUtils.Point2> path;
boolean drawCrosshair;
boolean drawGrid;
boolean drawFlags;
@ -126,6 +134,50 @@ public class MapViewer extends ApplicationAdapter {
mapRenderer.resize();
InputMultiplexer multiplexer = new InputMultiplexer();
multiplexer.addProcessor(new InputAdapter() {
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
switch (button) {
case Input.Buttons.LEFT:
src = mapRenderer.getCursor();
dst = null;
break;
case Input.Buttons.RIGHT:
src = dst = null;
break;
}
return true;
}
@Override
public boolean touchDragged(int screenX, int screenY, int button) {
if (src != null) {
dst = mapRenderer.getCursor();
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
switch (button) {
case Input.Buttons.LEFT:
dst = mapRenderer.getCursor();
System.out.println("src = " + src);
System.out.println("dst = " + dst);
//float srcX = +(src.x * Tile.SUBTILE_WIDTH50) - (src.y * Tile.SUBTILE_WIDTH50);
//float srcY = -(src.x * Tile.SUBTILE_HEIGHT50) - (src.y * Tile.SUBTILE_HEIGHT50);
//float dstX = +(dst.x * Tile.SUBTILE_WIDTH50) - (dst.y * Tile.SUBTILE_WIDTH50);
//float dstY = -(dst.x * Tile.SUBTILE_HEIGHT50) - (dst.y * Tile.SUBTILE_HEIGHT50);
//System.out.println(new Vector2(dstX, dstY).dst(srcX, srcY));
System.out.println(src.dst(dst));
path = map.path(src, dst);
break;
}
return true;
}
});
multiplexer.addProcessor(new InputAdapter() {
private final float ZOOM_AMOUNT = 0.1f;
@ -321,6 +373,19 @@ public class MapViewer extends ApplicationAdapter {
shapes.setAutoShapeType(true);
shapes.begin(ShapeRenderer.ShapeType.Line);
mapRenderer.renderDebug(shapes);
if (src != null && dst != null) {
//mapRenderer.renderDebugPath(shapes, src, dst);
if (path != null) mapRenderer.renderDebugPath2(shapes, path);
float srcX = +(src.x * Tile.SUBTILE_WIDTH50) - (src.y * Tile.SUBTILE_WIDTH50);
float srcY = -(src.x * Tile.SUBTILE_HEIGHT50) - (src.y * Tile.SUBTILE_HEIGHT50);
float dstX = +(dst.x * Tile.SUBTILE_WIDTH50) - (dst.y * Tile.SUBTILE_WIDTH50);
float dstY = -(dst.x * Tile.SUBTILE_HEIGHT50) - (dst.y * Tile.SUBTILE_HEIGHT50);
shapes.setColor(Color.WHITE);
shapes.circle(srcX, srcY, 32);
shapes.set(ShapeRenderer.ShapeType.Filled);
shapes.setColor(Color.ORANGE);
shapes.rectLine(srcX, srcY, dstX, dstY, 1);
}
shapes.end();
final int width = Gdx.graphics.getWidth();