1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-09-23 06:18:00 +03:00

Implemented inefficient pathfinding

This commit is contained in:
Anuken 2018-04-21 14:26:05 -04:00
parent 95f10aeb06
commit 5012313dc4
12 changed files with 107 additions and 68 deletions

View File

@ -99,7 +99,7 @@ public class AndroidLauncher extends AndroidApplication{
@Override
public boolean isDebug() {
return false;
return true;
}
@Override

View File

@ -1,7 +1,7 @@
#Autogenerated file. Do not modify.
#Sat Apr 21 11:52:24 EDT 2018
#Sat Apr 21 14:21:33 EDT 2018
version=release
androidBuildCode=1079
androidBuildCode=1083
name=Mindustry
code=3.5
build=custom build

View File

@ -0,0 +1,29 @@
package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.Connection;
import com.badlogic.gdx.ai.pfa.HierarchicalGraph;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.world.Tile;
public class HGraph implements HierarchicalGraph<Tile> {
@Override
public int getLevelCount() {
return 0;
}
@Override
public void setLevel(int level) {
}
@Override
public Tile convertNodeBetweenLevels(int inputLevel, Tile node, int outputLevel) {
return null;
}
@Override
public Array<Connection<Tile>> getConnections(Tile fromNode) {
return null;
}
}

View File

@ -22,6 +22,7 @@ public class OptimizedPathFinder {
NodeRecord current;
private int searchId;
private Tile end;
private static final byte UNVISITED = 0;
private static final byte OPEN = 1;
@ -29,14 +30,12 @@ public class OptimizedPathFinder {
private static final boolean debug = false;
public static boolean unop = false;
public static boolean step = true;
public OptimizedPathFinder() {
this.openList = new BinaryHeap<>();
}
public boolean searchNodePath(Tile startNode, Tile endNode, GraphPath<Tile> outPath) {
this.end = endNode;
// Perform AStar
boolean found = search(startNode, endNode);
@ -135,13 +134,13 @@ public class OptimizedPathFinder {
protected void visitChildren(Tile endNode) {
if(debug) Effects.effect(Fx.node3, current.node.worldx(), current.node.worldy());
jps(current.node, current.from == null ? -1 : relDirection(current.node, current.from), endNode, node -> {
nodes(current.node, node -> {
float addCost = estimate(current.node, node);
float nodeCost = current.costSoFar + addCost;
float nodeHeuristic;
NodeRecord nodeRecord = getNodeRecord(node);;
NodeRecord nodeRecord = getNodeRecord(node);
if (nodeRecord.category == CLOSED) { // The node is closed
@ -182,19 +181,17 @@ public class OptimizedPathFinder {
});
}
protected void nodes(Tile current, Consumer<Tile> cons){
if(obstacle(current)) return;
for(int i = 0; i < 4; i ++){
Tile n = current.getNearby(i);
if(!obstacle(n)) cons.accept(n);
}
}
protected void jps(Tile current, int direction, Tile end, Consumer<Tile> cons){
if(obstacle(current)) return; //skip solid or off-the-screen stuff
//Log.info("jps {0} {1} // {2}", current.x, current.y, direction);
if(unop){
for(int i = 0; i < 4; i ++){
if(!obstacle(current.getNearby(i))) cons.accept(current.getNearby(i));
}
return;
}
//if there's no start point, scan everything.
if(direction == -1){
for(int i = 0; i < 8; i ++){
@ -267,7 +264,6 @@ public class OptimizedPathFinder {
}
protected Tile scanDir(Tile tile, Tile end, int direction){
while(!obstacle(tile)){
if(debug) Effects.effect(Fx.node2, tile.worldx(), tile.worldy());
if(tile == end) return tile;
@ -303,12 +299,12 @@ public class OptimizedPathFinder {
}
protected boolean obstacle(Tile tile){
return tile == null || tile.solid();
return tile == null || (tile.solid() && end.target() != tile && tile.target() != end);
}
protected float estimate(Tile tile, Tile other){
return Math.abs(tile.worldx() - other.worldx()) + Math.abs(tile.worldy() - other.worldy()) +
(tile.occluded ? tilesize*2 : 0) + (other.occluded ? tilesize*2 : 0);
(tile.occluded ? tilesize : 0) + (other.occluded ? tilesize : 0);
}
protected int relDirection(Tile from, Tile current){

View File

@ -1,46 +1,42 @@
package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.DefaultGraphPath;
import com.badlogic.gdx.ai.pfa.PathSmoother;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.async.AsyncExecutor;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.function.Listenable;
import io.anuke.ucore.util.Log;
public class Pathfinder {
OptimizedPathFinder find = new OptimizedPathFinder();
OptimizedPathFinder find2 = new OptimizedPathFinder();
Tile start, end;
private OptimizedPathFinder find;
private AsyncExecutor executor = new AsyncExecutor(8);
private PathSmoother<Tile, Vector2> smoother = new PathSmoother<>(new Raycaster());
//private HierarchicalPathFinder<Tile> hfinder = new HierarchicalPathFinder<>();
public Pathfinder(){
Events.on(WorldLoadEvent.class, this::clear);
}
public void findPath(Tile start, Tile end, SmoothGraphPath path,
OptimizedPathFinder finder, Listenable completed){
executor.submit(() -> {
finder.searchNodePath(start, end, path);
smoother.smoothPath(path);
completed.listen();
return path;
});
}
public void test(Tile start, Tile end){
this.start = start;
this.end = end;
DefaultGraphPath<Tile> p = new DefaultGraphPath<>();
OptimizedPathFinder.unop = false;
Timers.markNs();
find.searchNodePath(start, end, p);
Log.info("JSFSAF elapsed: {0}", Timers.elapsedNs());
for(Tile tile : p){
Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy());
}
SmoothGraphPath p2 = new SmoothGraphPath();
OptimizedPathFinder.unop = true;
Timers.markNs();
find2.searchNodePath(start, end, p2);
find.searchNodePath(start, end, p2);
new PathSmoother<Tile, Vector2>(new Raycaster()).smoothPath(p2);
Log.info("UNOP elapsed: {0}", Timers.elapsedNs());
@ -56,6 +52,6 @@ public class Pathfinder {
}
private void clear(){
find = new OptimizedPathFinder();
}
}

View File

@ -1,6 +1,10 @@
package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.ai.OptimizedPathFinder;
import io.anuke.mindustry.ai.SmoothGraphPath;
import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.game.Team;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.util.Mathf;
@ -19,6 +23,10 @@ public class BaseUnit extends Unit{
public float walkTime = 0f;
public Entity target;
protected OptimizedPathFinder finder;
protected SmoothGraphPath path;
protected int node = -2;
public BaseUnit(UnitType type, Team team){
this.type = type;
this.team = team;
@ -97,6 +105,11 @@ public class BaseUnit extends Unit{
hitbox.setSize(type.hitsize);
hitboxTile.setSize(type.hitsizeTile);
if(!isFlying()){
finder = new OptimizedPathFinder();
path = new SmoothGraphPath();
}
heal();
}

View File

@ -15,19 +15,23 @@ import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world;
public abstract class GroundUnitType extends UnitType{
private static final int nodeStateNone = -2;
private static final int nodeStateCalculating = -1;
//only use for drawing!
protected Translator tr1 = new Translator();
//only use for updating!
protected Translator tr2 = new Translator();
protected float stopDistance = 30f;
protected float jumpDistance = 4f;
public GroundUnitType(String name) {
super(name);
maxVelocity = 1.1f;
speed = 0.05f;
speed = 0.1f;
drag = 0.4f;
}
@ -103,16 +107,29 @@ public abstract class GroundUnitType extends UnitType{
@Override
public void behavior(BaseUnit unit) {
//TODO actually pathfind
tr2.set(unit.target.x, unit.target.y).sub(unit.x, unit.y);
if(unit.node == nodeStateNone){
world.pathfinder().findPath(world.tileWorld(unit.x, unit.y), world.tileWorld(unit.target.x, unit.target.y), unit.path, unit.finder, () -> {
unit.node = 0;
});
if(tr2.len() > stopDistance){
tr2.limit(speed);
unit.walkTime += Timers.delta();
unit.velocity.add(tr2);
unit.node = nodeStateCalculating;
}
if(!(unit.node >= 0 && unit.node < unit.path.nodes.size)) return;
Tile nodeTarget = unit.path.get(unit.node);
tr2.set(nodeTarget.worldx(), nodeTarget.worldy()).sub(unit.x, unit.y);
if(tr2.len() < jumpDistance){
unit.node ++;
}
tr2.limit(speed);
unit.walkTime += Timers.delta();
unit.velocity.add(tr2);
if(unit.timer.get(timerReload, reload)){
//shoot(unit, BulletType.shot, tr2.angle(), 4f);
}

View File

@ -5,7 +5,7 @@ import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Rectangle;
import com.sun.media.jfxmediaimpl.MediaDisposer.Disposable;
import com.badlogic.gdx.utils.Disposable;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;

View File

@ -194,10 +194,6 @@ public class DesktopInput extends InputHandler{
world.pathfinder().test(world.tileWorld(player.x, player.y), world.tileWorld(Graphics.mouseWorld().x, Graphics.mouseWorld().y));
}
if(Inputs.keyTap(Input.L)){
world.pathfinder().step();
}
if(!ui.hasMouse()) {
if (showCursor)
Cursors.setHand();

View File

@ -23,7 +23,6 @@ public class Tile{
private byte floor, wall;
private byte rotation;
private byte dump;
private byte extra;
private byte team;
/**The coordinates of the core tile this is linked to, in the form of two bytes packed into one.
* This is relative to the block it is linked to; negate coords to find the link.*/
@ -32,6 +31,9 @@ public class Tile{
/**Whether this tile has any solid blocks near it.*/
public boolean occluded = false;
public TileEntity entity;
public float pathDistance = -1;
public float vecx, vecy;
public Tile(int x, int y){
this.x = (short)x;
@ -167,10 +169,6 @@ public class Tile{
this.dump = dump;
}
public void setExtra(byte extra){
this.extra = extra;
}
public byte getRotation(){
return rotation;
}
@ -178,10 +176,6 @@ public class Tile{
public byte getDump(){
return dump;
}
public byte getExtra(){
return extra;
}
public boolean passable(){
Block block = block();

View File

@ -48,7 +48,6 @@ public class Router extends Block{
public void handleItem(Item item, Tile tile, Tile source){
super.handleItem(item, tile, source);
tile.entity.wakeUp();
tile.setExtra(tile.relativeTo(source.x, source.y));
}
@Override

View File

@ -27,7 +27,6 @@ public class Vault extends StorageBlock {
@Override
public void handleItem(Item item, Tile tile, Tile source){
super.handleItem(item, tile, source);
tile.setExtra(tile.relativeTo(source.x, source.y));
}
@Override