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:
parent
95f10aeb06
commit
5012313dc4
@ -99,7 +99,7 @@ public class AndroidLauncher extends AndroidApplication{
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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
|
||||
|
29
core/src/io/anuke/mindustry/ai/HGraph.java
Normal file
29
core/src/io/anuke/mindustry/ai/HGraph.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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){
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user