Netcode changes, new pathfinding, fixed enemies jittering when stuck
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Heuristic;
|
||||
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public class HeuristicImpl implements Heuristic<Tile>{
|
||||
/**How many times more it costs to go through a destructible block than an empty block.*/
|
||||
static final float solidMultiplier = 5f;
|
||||
/**How many times more it costs to go through a tile that touches a solid block.*/
|
||||
static final float occludedMultiplier = 5f;
|
||||
|
||||
@Override
|
||||
public float estimate(Tile node, Tile other){
|
||||
return estimateStatic(node, other);
|
||||
}
|
||||
|
||||
/**Estimate the cost of walking between two tiles.*/
|
||||
public static float estimateStatic(Tile node, Tile other){
|
||||
//Get Manhattan distance cost
|
||||
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
|
||||
|
||||
//If either one of the tiles is a breakable solid block (that is, it's player-made),
|
||||
//increase the cost by the tilesize times the solid block multiplier
|
||||
//Also add the block health, so blocks with more health cost more to traverse
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
|
||||
|
||||
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
|
||||
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Heuristic;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.types.defense.Turret;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Drill;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Generator;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Pump;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Smelter;
|
||||
|
||||
public class Heuristics {
|
||||
/**How many times more it costs to go through a destructible block than an empty block.*/
|
||||
static final float solidMultiplier = 5f;
|
||||
/**How many times more it costs to go through a tile that touches a solid block.*/
|
||||
static final float occludedMultiplier = 5f;
|
||||
|
||||
public static class FastestHeuristic implements Heuristic<Tile> {
|
||||
|
||||
@Override
|
||||
public float estimate(Tile node, Tile other){
|
||||
//Get Manhattan distance cost
|
||||
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
|
||||
|
||||
//If either one of the tiles is a breakable solid block (that is, it's player-made),
|
||||
//increase the cost by the tilesize times the solid block multiplier
|
||||
//Also add the block health, so blocks with more health cost more to traverse
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
|
||||
|
||||
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
|
||||
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
|
||||
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DestrutiveHeuristic implements Heuristic<Tile> {
|
||||
|
||||
@Override
|
||||
public float estimate(Tile node, Tile other){
|
||||
//Get Manhattan distance cost
|
||||
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
|
||||
|
||||
//If either one of the tiles is a breakable solid block (that is, it's player-made),
|
||||
//increase the cost by the tilesize times the solid block multiplier
|
||||
//Also add the block health, so blocks with more health cost more to traverse
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
|
||||
|
||||
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
|
||||
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
|
||||
|
||||
//generators are free!
|
||||
if(generator(other) || generator(node)) cost = 0;
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
private boolean generator(Tile tile){
|
||||
return tile.block() instanceof Generator || tile.block() instanceof Turret
|
||||
|| tile.block() instanceof Pump || tile.block() instanceof Drill || tile.block() instanceof Smelter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Heuristic;
|
||||
import com.badlogic.gdx.ai.pfa.PathFinderRequest;
|
||||
import com.badlogic.gdx.ai.pfa.PathSmoother;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.ai.Heuristics.DestrutiveHeuristic;
|
||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||
import io.anuke.mindustry.entities.enemies.EnemyType;
|
||||
import io.anuke.mindustry.game.SpawnPoint;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.UCore;
|
||||
@@ -20,7 +21,7 @@ public class Pathfind{
|
||||
private static final long maxTime = 1000000 * 5;
|
||||
|
||||
/**Heuristic for determining cost between two tiles*/
|
||||
HeuristicImpl heuristic = new HeuristicImpl();
|
||||
Heuristic<Tile> heuristic = new DestrutiveHeuristic();
|
||||
/**Tile graph, for determining conenctions between two tiles*/
|
||||
TileGraph graph = new TileGraph();
|
||||
/**Smoother that removes extra nodes from a path.*/
|
||||
@@ -54,17 +55,17 @@ public class Pathfind{
|
||||
}
|
||||
|
||||
//if an enemy is idle for a while, it's probably stuck
|
||||
if(enemy.idletime > EnemyType.maxIdle){
|
||||
//if(enemy.idletime > EnemyType.maxIdle){
|
||||
|
||||
Tile target = path[enemy.node];
|
||||
if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) {
|
||||
if (enemy.node > 1)
|
||||
enemy.node = enemy.node - 1;
|
||||
enemy.idletime = 0;
|
||||
}
|
||||
//Tile target = path[enemy.node];
|
||||
//if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) {
|
||||
// if (enemy.node > 1)
|
||||
// enemy.node = enemy.node - 1;
|
||||
// enemy.idletime = 0;
|
||||
//}
|
||||
|
||||
//else, must be blocked by a playermade block, do nothing
|
||||
}
|
||||
//}
|
||||
|
||||
//-1 is only possible here if both pathfindings failed, which should NOT happen
|
||||
//check graph code
|
||||
@@ -82,6 +83,12 @@ public class Pathfind{
|
||||
Tile prev = path[enemy.node - 1];
|
||||
|
||||
Tile target = path[enemy.node];
|
||||
|
||||
//a bridge has broken
|
||||
if(!Vars.world.passable(target.x, target.y)){
|
||||
remakePath();
|
||||
return vector.set(enemy.x, enemy.y);
|
||||
}
|
||||
|
||||
float projectLen = Vector2.dst(prev.worldx(), prev.worldy(), target.worldx(), target.worldy()) / 6f;
|
||||
|
||||
@@ -124,6 +131,15 @@ public class Pathfind{
|
||||
|
||||
}
|
||||
|
||||
private void remakePath(){
|
||||
for(int i = 0; i < Vars.control.enemyGroup.amount(); i ++){
|
||||
Enemy enemy = Vars.control.enemyGroup.all().get(i);
|
||||
enemy.node = -1;
|
||||
}
|
||||
|
||||
resetPaths();
|
||||
}
|
||||
|
||||
/**Update the pathfinders and continue calculating the path if it hasn't been calculated yet.
|
||||
* This method is run each frame.*/
|
||||
public void update(){
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Connection;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public class TileConnection implements Connection<Tile>{
|
||||
Tile a, b;
|
||||
|
||||
public TileConnection(Tile a, Tile b){
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCost(){
|
||||
return HeuristicImpl.estimateStatic(a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getFromNode(){
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getToNode(){
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,23 +7,10 @@ import io.anuke.mindustry.world.Tile;
|
||||
|
||||
/**Tilegraph that ignores player-made tiles.*/
|
||||
public class TileGraph implements OptimizedGraph<Tile> {
|
||||
private Array<Connection<Tile>> tempConnections = new Array<Connection<Tile>>(4);
|
||||
|
||||
/**Used for the default Graph implementation. Returns a result similar to connectionsOf()*/
|
||||
/**return nothing, as this isn't used*/
|
||||
@Override
|
||||
public Array<Connection<Tile>> getConnections(Tile fromNode){
|
||||
tempConnections.clear();
|
||||
|
||||
if(!fromNode.passable())
|
||||
return tempConnections;
|
||||
|
||||
for(Tile tile : fromNode.getNearby()){
|
||||
if(tile != null && (tile.passable()))
|
||||
tempConnections.add(new TileConnection(fromNode, tile));
|
||||
}
|
||||
|
||||
return tempConnections;
|
||||
}
|
||||
public Array<Connection<Tile>> getConnections(Tile fromNode){ return null; }
|
||||
|
||||
/**Used for the OptimizedPathFinder implementation.*/
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user