Smoothed enemy paths, AI should no longer get stuck on walls
This commit is contained in:
@@ -16,6 +16,11 @@ public class MHueristic implements Heuristic<Tile>{
|
||||
//TODO balance multiplier
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize*multiplier;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize*multiplier;
|
||||
for(Tile tile : node.getNearby()){
|
||||
if(tile != null && tile.solid()){
|
||||
cost += Vars.tilesize*2;
|
||||
}
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.DefaultGraphPath;
|
||||
import com.badlogic.gdx.ai.pfa.PathFinder;
|
||||
import com.badlogic.gdx.ai.pfa.PathSmoother;
|
||||
import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.World;
|
||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
public class Pathfind{
|
||||
static MHueristic heuristic = new MHueristic();
|
||||
static PassTileGraph passgraph = new PassTileGraph();
|
||||
static PathFinder<Tile> passpathfinder;
|
||||
static Array<DefaultGraphPath<Tile>> paths = new Array<>();
|
||||
static Array<SmoothGraphPath> paths = new Array<>();
|
||||
static Tile[][] pathSequences;
|
||||
static PathSmoother<Tile, Vector2> smoother = new PathSmoother<Tile, Vector2>(new Raycaster());
|
||||
static Vector2 vector = new Vector2();
|
||||
|
||||
static public Vector2 find(Enemy enemy){
|
||||
@@ -53,19 +56,21 @@ public class Pathfind{
|
||||
if(paths.size == 0){
|
||||
pathSequences = new Tile[World.spawnpoints.size][0];
|
||||
for(int i = 0; i < World.spawnpoints.size; i ++){
|
||||
DefaultGraphPath<Tile> path = new DefaultGraphPath<>();
|
||||
SmoothGraphPath path = new SmoothGraphPath();
|
||||
paths.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < paths.size; i ++){
|
||||
DefaultGraphPath<Tile> path = paths.get(i);
|
||||
SmoothGraphPath path = paths.get(i);
|
||||
|
||||
path.clear();
|
||||
passpathfinder.searchNodePath(
|
||||
World.spawnpoints.get(i),
|
||||
World.core, heuristic, path);
|
||||
|
||||
smoother.smoothPath(path);
|
||||
|
||||
pathSequences[i] = new Tile[path.getCount()];
|
||||
|
||||
for(int node = 0; node < path.getCount(); node ++){
|
||||
@@ -75,10 +80,10 @@ public class Pathfind{
|
||||
}
|
||||
|
||||
|
||||
//if(debug)
|
||||
//for(Tile tile : path){
|
||||
// Effects.effect("ind", tile.worldx(), tile.worldy());
|
||||
//}
|
||||
if(Vars.debug)
|
||||
for(Tile tile : path){
|
||||
Effects.effect("ind", tile.worldx(), tile.worldy());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.utils.Collision;
|
||||
import com.badlogic.gdx.ai.utils.Ray;
|
||||
import com.badlogic.gdx.ai.utils.RaycastCollisionDetector;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.World;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.util.Geometry;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
public class Raycaster implements RaycastCollisionDetector<Vector2>{
|
||||
private boolean found = false;
|
||||
|
||||
@Override
|
||||
public boolean collides(Ray<Vector2> ray){
|
||||
found = false;
|
||||
|
||||
Geometry.iterateLine(0f, ray.start.x, ray.start.y, ray.end.x, ray.end.y, Vars.tilesize, (x, y)->{
|
||||
if(solid(x, y)){
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean findCollision(Collision<Vector2> collision, Ray<Vector2> ray){
|
||||
Vector2 v = vectorCast(ray.start.x, ray.start.y, ray.end.x, ray.end.y);
|
||||
if(v == null) return false;
|
||||
collision.point = v;
|
||||
collision.normal = v.nor();
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector2 vectorCast(float x0f, float y0f, float x1f, float y1f){
|
||||
int x0 = (int)x0f;
|
||||
int y0 = (int)y0f;
|
||||
int x1 = (int)x1f;
|
||||
int y1 = (int)y1f;
|
||||
int dx = Math.abs(x1 - x0);
|
||||
int dy = Math.abs(y1 - y0);
|
||||
|
||||
int sx = x0 < x1 ? 1 : -1;
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
|
||||
int err = dx - dy;
|
||||
int e2;
|
||||
while(true){
|
||||
|
||||
if(solid(x0, y0)){
|
||||
return new Vector2(x0, y0);
|
||||
}
|
||||
if(x0 == x1 && y0 == y1) break;
|
||||
|
||||
e2 = 2 * err;
|
||||
if(e2 > -dy){
|
||||
err = err - dy;
|
||||
x0 = x0 + sx;
|
||||
}
|
||||
|
||||
if(e2 < dx){
|
||||
err = err + dx;
|
||||
y0 = y0 + sy;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean solid(float x, float y){
|
||||
Tile tile = World.tile(Mathf.scl2(x, Vars.tilesize), Mathf.scl2(y, Vars.tilesize));
|
||||
|
||||
if(tile == null || tile.solid()) return true;
|
||||
|
||||
for(Tile near : tile.getNearby()){
|
||||
if(near == null || near.solid()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.DefaultGraphPath;
|
||||
import com.badlogic.gdx.ai.pfa.SmoothableGraphPath;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.util.Tmp;
|
||||
|
||||
public class SmoothGraphPath extends DefaultGraphPath<Tile> implements SmoothableGraphPath<Tile, Vector2>{
|
||||
|
||||
@Override
|
||||
public Vector2 getNodePosition(int index){
|
||||
Tile tile = nodes.get(index);
|
||||
return Tmp.v1.set(tile.worldx(), tile.worldy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapNodes(int index1, int index2){
|
||||
nodes.swap(index1, index2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void truncatePath(int newLength){
|
||||
nodes.truncate(newLength);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user