From 3e9f0a9d296b0012ab0b43a1f57c3c137dc7370a Mon Sep 17 00:00:00 2001 From: Collin Smith Date: Fri, 22 Feb 2019 16:12:56 -0800 Subject: [PATCH] Implemented ray-casting-based movement for touchpad controls Implemented ray-casting-based movement for touchpad controls Works great in open spaces, some issues with wall detection and following --- core/src/gdx/diablo/entity/Entity.java | 18 ++++-- core/src/gdx/diablo/map/Map.java | 6 ++ core/src/gdx/diablo/map/MapGraph.java | 65 +++++++++++++++++++++- core/src/gdx/diablo/screen/GameScreen.java | 54 ++++++++++++++++-- 4 files changed, 129 insertions(+), 14 deletions(-) diff --git a/core/src/gdx/diablo/entity/Entity.java b/core/src/gdx/diablo/entity/Entity.java index df6a0a26..c5253441 100644 --- a/core/src/gdx/diablo/entity/Entity.java +++ b/core/src/gdx/diablo/entity/Entity.java @@ -263,17 +263,24 @@ public class Entity { } } - public void setPath(Map map, Vector3 dst) { - setPath(map, dst, -1); + public boolean setPath(Map map, Vector3 dst) { + return setPath(map, dst, -1); } - public void setPath(Map map, Vector3 dst, int maxSteps) { + public boolean setPath(Map map, Vector3 dst, int maxSteps) { + if (dst == null) { + path.clear(); + targets = Collections.emptyIterator(); + target.set(position); + return false; + } + boolean success = map.findPath(position, dst, path); - if (!success) return; + if (!success) return false; if (maxSteps != -1 && path.getCount() > maxSteps) { path.clear(); targets = Collections.emptyIterator(); - return; + return false; } if (DEBUG_PATH) Gdx.app.debug(TAG, "path=" + path); @@ -288,6 +295,7 @@ public class Entity { } //if (DEBUG_TARGET) Gdx.app.debug(TAG, "target=" + target); + return true; } public void update(float delta) { diff --git a/core/src/gdx/diablo/map/Map.java b/core/src/gdx/diablo/map/Map.java index 76035c2d..9751a070 100644 --- a/core/src/gdx/diablo/map/Map.java +++ b/core/src/gdx/diablo/map/Map.java @@ -6,6 +6,8 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.ai.pfa.GraphPath; import com.badlogic.gdx.ai.pfa.SmoothableGraphPath; import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder; +import com.badlogic.gdx.ai.utils.Collision; +import com.badlogic.gdx.ai.utils.Ray; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.graphics.Color; @@ -559,6 +561,10 @@ public class Map implements Disposable { mapGraph.smoothPath(path); } + public boolean castRay(Collision dst, Ray ray) { + return mapGraph.rayCaster.findCollision(dst, ray); + } + static class Zone { static final Array EMPTY_ARRAY = new Array<>(0); diff --git a/core/src/gdx/diablo/map/MapGraph.java b/core/src/gdx/diablo/map/MapGraph.java index 2cfc3fcf..36b65fee 100644 --- a/core/src/gdx/diablo/map/MapGraph.java +++ b/core/src/gdx/diablo/map/MapGraph.java @@ -28,11 +28,13 @@ public class MapGraph implements IndexedGraph { Map map; IntMap points = new IntMap<>(); + MapRaycastCollisionDetector rayCaster; PathSmoother pathSmoother; public MapGraph(Map map) { this.map = map; - pathSmoother = new PathSmoother<>(new MapRaycastCollisionDetector(this)); + rayCaster = new MapRaycastCollisionDetector(this); + pathSmoother = new PathSmoother<>(rayCaster); } public GraphPath path(Vector3 src, Vector3 dst, GraphPath path) { @@ -294,8 +296,65 @@ public class MapGraph implements IndexedGraph { } @Override - public boolean findCollision(Collision outputCollision, Ray inputRay) { - throw new UnsupportedOperationException(); + public boolean findCollision(Collision dst, Ray ray) { + int x0 = (int) ray.start.x; + int y0 = (int) ray.start.y; + int x1 = (int) ray.end.x; + int y1 = (int) ray.end.y; + + int tmp; + boolean steep = Math.abs(y1 - y0) > Math.abs(x1 - x0); + if (steep) { + // Swap x0 and y0 + tmp = x0; + x0 = y0; + y0 = tmp; + // Swap x1 and y1 + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + // Swap x0 and x1 + tmp = x0; + x0 = x1; + x1 = tmp; + // Swap y0 and y1 + tmp = y0; + y0 = y1; + y1 = tmp; + } + + int deltax = x1 - x0; + int deltay = Math.abs(y1 - y0); + int error = 0; + int y = y0; + int ystep = (y0 < y1 ? 1 : -1); + dst.point.set(steep ? y0 : x0, steep ? x0 : y0); + //dst.normal.setZero(); + for (int x = x0; x <= x1; x++) { + if (steep) { + Map.Zone zone = map.getZone(y, x); + if (zone == null || zone.flags(y, x) != 0) { + //dst.normal.set(y, x); + return true; + } + } else { + Map.Zone zone = map.getZone(x, y); + if (zone == null || zone.flags(x, y) != 0) { + //dst.normal.set(x, y); + return true; + } + } + dst.point.set(steep ? y : x, steep ? x : y); + error += deltay; + if (error + error >= deltax) { + y += ystep; + error -= deltax; + } + } + + return false; } } } diff --git a/core/src/gdx/diablo/screen/GameScreen.java b/core/src/gdx/diablo/screen/GameScreen.java index 0088420b..27d37239 100644 --- a/core/src/gdx/diablo/screen/GameScreen.java +++ b/core/src/gdx/diablo/screen/GameScreen.java @@ -6,12 +6,14 @@ import com.badlogic.gdx.Input; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.ScreenAdapter; +import com.badlogic.gdx.ai.utils.Collision; +import com.badlogic.gdx.ai.utils.Ray; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.net.Socket; import com.badlogic.gdx.scenes.scene2d.Actor; @@ -34,13 +36,13 @@ import java.io.PrintWriter; import gdx.diablo.Diablo; import gdx.diablo.Keys; -import gdx.diablo.entity.Direction; import gdx.diablo.entity.Entity; import gdx.diablo.entity.Player; import gdx.diablo.graphics.PaletteIndexedBatch; import gdx.diablo.graphics.PaletteIndexedColorDrawable; import gdx.diablo.key.MappedKey; import gdx.diablo.key.MappedKeyStateAdapter; +import gdx.diablo.map.DT1; import gdx.diablo.map.Map; import gdx.diablo.map.MapLoader; import gdx.diablo.map.MapRenderer; @@ -368,10 +370,50 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable if (x == 0 && y == 0) { player.setPath(map, null); } else { - float rad = MathUtils.atan2(y, x); - x = Direction.getOffX(rad); - y = Direction.getOffY(rad); - player.setPath(map, new Vector3(x, y, 0).add(player.position()), 3); + //float rad = MathUtils.atan2(y, x); + //x = Direction.getOffX(rad); + //y = Direction.getOffY(rad); + //player.setPath(map, new Vector3(x, y, 0).add(player.position()), 3); + + Vector2 position = new Vector2(player.position().x, player.position().y); + Vector2 target = new Vector2(x, y).scl(DT1.Tile.WIDTH).add(mapRenderer.project(position.x, position.y, new Vector2())); + GridPoint2 coords = mapRenderer.coords(target.x, target.y, new GridPoint2()); + target.set(coords.x, coords.y); + Ray ray = new Ray<>(position, target); + Collision collision = new Collision<>(new Vector2(), new Vector2()); + boolean hit = map.castRay(collision, ray); + if (hit) { + if (position.epsilonEquals(collision.point, 1.0f)) { + /*System.out.println("against wall"); + + float rad = MathUtils.atan2(y, x); + if (rad > MathUtils.PI - 0.46365f) { + rad -= MathUtils.PI - 0.46365f; + System.out.println("1 " + rad); + } else if (rad < -0.46365f) { + rad += MathUtils.PI + 0.46365f; + System.out.println("2 " + rad); + } + + if (rad > 2.0944f) { + Vector3 newTarget = new Vector3(player.position()).add(1, 0, 0); + player.setPath(map, newTarget, 2); + System.out.println("down " + player.position() + "; " + newTarget); + } else if (rad > 0 && rad < 1.0472f) { + Vector3 newTarget = new Vector3(player.position()).add(-1, 0, 0); + player.setPath(map, newTarget, 2); + System.out.println("up " + player.position() + "; " + newTarget); + }*/ + } else { + //System.out.println("headed for wall " + position + ", " + collision.point); + player.setPath(map, new Vector3(collision.point, 0), 3); + } + } else { + //System.out.println("freedom baby"); + player.target().set(target, 0); + } + + //System.out.println("hit " + hit + "; " + collision.point + "; " + collision.normal); } } else { if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {