Smoothed enemy paths, AI should no longer get stuck on walls

This commit is contained in:
Anuken
2017-08-13 13:57:39 -04:00
parent cd4014f86a
commit 4da9d730bf
9 changed files with 159 additions and 17 deletions

View File

@@ -8,7 +8,7 @@ buildscript {
dependencies { dependencies {
classpath 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6' classpath 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6'
classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'org.akhikhl.gretty:gretty:+' //classpath 'org.akhikhl.gretty:gretty:+'
} }
} }
@@ -80,7 +80,7 @@ project(":core") {
dependencies { dependencies {
//compile fileTree(dir: '../core/lib', include: '*.jar') //compile fileTree(dir: '../core/lib', include: '*.jar')
compile 'com.github.anuken:ucore:b8222ac95e' compile 'com.github.anuken:ucore:c93833a2b5'
compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-ai:1.8.1" compile "com.badlogicgames.gdx:gdx-ai:1.8.1"
} }

View File

@@ -62,6 +62,8 @@ public class Control extends RendererModule{
"corexplode.wav", "break.wav", "spawn.wav", "flame.wav", "die.wav", "corexplode.wav", "break.wav", "spawn.wav", "flame.wav", "die.wav",
"respawn.wav", "purchase.wav", "flame2.wav"); "respawn.wav", "purchase.wav", "flame2.wav");
Sounds.setFalloff(9000f);
Musics.load("1.mp3", "2.mp3", "3.mp3"); Musics.load("1.mp3", "2.mp3", "3.mp3");
World.loadMaps(); World.loadMaps();
@@ -80,8 +82,6 @@ public class Control extends RendererModule{
for(String map : maps) for(String map : maps)
Settings.defaults("hiscore"+map, 0); Settings.defaults("hiscore"+map, 0);
Sounds.setFalloff(9000f);
player = new Player(); player = new Player();
} }
@@ -391,6 +391,12 @@ public class Control extends RendererModule{
Renderer.renderPixelOverlay(); Renderer.renderPixelOverlay();
} }
@Override
public void dispose(){
super.dispose();
World.disposeMaps();
}
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
super.resize(width, height); super.resize(width, height);

View File

@@ -435,10 +435,11 @@ public class UI extends SceneModule{
row(); row();
if(Gdx.app.getType() != ApplicationType.WebGL) if(Gdx.app.getType() != ApplicationType.WebGL && !android){
new button("Exit", () -> { new button("Exit", () -> {
Gdx.app.exit(); Gdx.app.exit();
}); });
}
get().pad(Unit.dp.inPixels(20)); get().pad(Unit.dp.inPixels(20));
}}; }};

View File

@@ -225,4 +225,14 @@ public class World{
public static int tiley(){ public static int tiley(){
return Mathf.scl2(Graphics.mouseWorld().y, tilesize); return Mathf.scl2(Graphics.mouseWorld().y, tilesize);
} }
public static void disposeMaps(){
for(Pixmap pixmap : mapPixmaps){
pixmap.dispose();
}
for(Texture texture : mapTextures){
texture.dispose();
}
}
} }

View File

@@ -16,6 +16,11 @@ public class MHueristic implements Heuristic<Tile>{
//TODO balance multiplier //TODO balance multiplier
if(node.breakable() && node.block().solid) cost += Vars.tilesize*multiplier; if(node.breakable() && node.block().solid) cost += Vars.tilesize*multiplier;
if(other.breakable() && other.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; return cost;
} }

View File

@@ -1,20 +1,23 @@
package io.anuke.mindustry.ai; 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.PathFinder;
import com.badlogic.gdx.ai.pfa.PathSmoother;
import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder; import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.World; import io.anuke.mindustry.World;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects;
public class Pathfind{ public class Pathfind{
static MHueristic heuristic = new MHueristic(); static MHueristic heuristic = new MHueristic();
static PassTileGraph passgraph = new PassTileGraph(); static PassTileGraph passgraph = new PassTileGraph();
static PathFinder<Tile> passpathfinder; static PathFinder<Tile> passpathfinder;
static Array<DefaultGraphPath<Tile>> paths = new Array<>(); static Array<SmoothGraphPath> paths = new Array<>();
static Tile[][] pathSequences; static Tile[][] pathSequences;
static PathSmoother<Tile, Vector2> smoother = new PathSmoother<Tile, Vector2>(new Raycaster());
static Vector2 vector = new Vector2(); static Vector2 vector = new Vector2();
static public Vector2 find(Enemy enemy){ static public Vector2 find(Enemy enemy){
@@ -53,19 +56,21 @@ public class Pathfind{
if(paths.size == 0){ if(paths.size == 0){
pathSequences = new Tile[World.spawnpoints.size][0]; pathSequences = new Tile[World.spawnpoints.size][0];
for(int i = 0; i < World.spawnpoints.size; i ++){ for(int i = 0; i < World.spawnpoints.size; i ++){
DefaultGraphPath<Tile> path = new DefaultGraphPath<>(); SmoothGraphPath path = new SmoothGraphPath();
paths.add(path); paths.add(path);
} }
} }
for(int i = 0; i < paths.size; i ++){ for(int i = 0; i < paths.size; i ++){
DefaultGraphPath<Tile> path = paths.get(i); SmoothGraphPath path = paths.get(i);
path.clear(); path.clear();
passpathfinder.searchNodePath( passpathfinder.searchNodePath(
World.spawnpoints.get(i), World.spawnpoints.get(i),
World.core, heuristic, path); World.core, heuristic, path);
smoother.smoothPath(path);
pathSequences[i] = new Tile[path.getCount()]; pathSequences[i] = new Tile[path.getCount()];
for(int node = 0; node < path.getCount(); node ++){ for(int node = 0; node < path.getCount(); node ++){
@@ -75,10 +80,10 @@ public class Pathfind{
} }
//if(debug) if(Vars.debug)
//for(Tile tile : path){ for(Tile tile : path){
// Effects.effect("ind", tile.worldx(), tile.worldy()); Effects.effect("ind", tile.worldx(), tile.worldy());
//} }
} }
} }

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -1,5 +1,6 @@
apply plugin: "java" apply plugin: "java"
apply plugin: 'org.akhikhl.gretty' apply plugin: "jetty"
//apply plugin: 'org.akhikhl.gretty'
gwt { gwt {
gwtVersion='2.8.0' // Should match the gwt version used for building the gwt backend gwtVersion='2.8.0' // Should match the gwt version used for building the gwt backend