Improvements to playability at low TPS

This commit is contained in:
Anuken 2018-06-20 15:06:53 -04:00
parent 0c7f6432d8
commit 2884e4b847
6 changed files with 114 additions and 46 deletions

View File

@ -6,10 +6,7 @@ import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureWrap; import com.badlogic.gdx.graphics.Texture.TextureWrap;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.*;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.ObjectIntMap;
import com.badlogic.gdx.utils.Pools;
import io.anuke.mindustry.content.fx.Fx; import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
@ -20,8 +17,8 @@ import io.anuke.mindustry.entities.traits.BelowLiquidTrait;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Graphics;
@ -30,7 +27,11 @@ import io.anuke.ucore.entities.EntityDraw;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.impl.BaseEntity; import io.anuke.ucore.entities.impl.BaseEntity;
import io.anuke.ucore.entities.impl.EffectEntity; import io.anuke.ucore.entities.impl.EffectEntity;
import io.anuke.ucore.entities.trait.DrawTrait;
import io.anuke.ucore.entities.trait.SolidTrait;
import io.anuke.ucore.function.Callable; import io.anuke.ucore.function.Callable;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.function.Predicate;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Hue; import io.anuke.ucore.graphics.Hue;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
@ -38,6 +39,7 @@ import io.anuke.ucore.graphics.Surface;
import io.anuke.ucore.modules.RendererModule; import io.anuke.ucore.modules.RendererModule;
import io.anuke.ucore.scene.utils.Cursors; import io.anuke.ucore.scene.utils.Cursors;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
import static io.anuke.ucore.core.Core.batch; import static io.anuke.ucore.core.Core.batch;
@ -51,7 +53,9 @@ public class Renderer extends RendererModule{
private FloatArray shieldHits = new FloatArray(); private FloatArray shieldHits = new FloatArray();
private Array<Callable> shieldDraws = new Array<>(); private Array<Callable> shieldDraws = new Array<>();
private Rectangle rect = new Rectangle(), rect2 = new Rectangle(); private Rectangle rect = new Rectangle(), rect2 = new Rectangle();
private Vector2 avgPosition = new Vector2(); private Vector2 avgPosition = new Translator();
private Vector2 tmpVector1 = new Translator();
private Vector2 tmpVector2 = new Translator();
private BlockRenderer blocks = new BlockRenderer(); private BlockRenderer blocks = new BlockRenderer();
private MinimapRenderer minimap = new MinimapRenderer(); private MinimapRenderer minimap = new MinimapRenderer();
private OverlayRenderer overlays = new OverlayRenderer(); private OverlayRenderer overlays = new OverlayRenderer();
@ -196,9 +200,9 @@ public class Renderer extends RendererModule{
blocks.drawFloor(); blocks.drawFloor();
EntityDraw.draw(groundEffectGroup, e -> e instanceof BelowLiquidTrait); drawAndInterpolate(groundEffectGroup, e -> e instanceof BelowLiquidTrait);
EntityDraw.draw(puddleGroup); drawAndInterpolate(puddleGroup);
EntityDraw.draw(groundEffectGroup, e -> !(e instanceof BelowLiquidTrait)); drawAndInterpolate(groundEffectGroup, e -> !(e instanceof BelowLiquidTrait));
blocks.processBlocks(); blocks.processBlocks();
blocks.drawBlocks(Layer.block); blocks.drawBlocks(Layer.block);
@ -213,7 +217,7 @@ public class Renderer extends RendererModule{
Shaders.outline.color.set(Team.none.color); Shaders.outline.color.set(Team.none.color);
Graphics.beginShaders(Shaders.outline); Graphics.beginShaders(Shaders.outline);
EntityDraw.draw(itemGroup); drawAndInterpolate(itemGroup);
Graphics.endShaders(); Graphics.endShaders();
} }
@ -224,12 +228,12 @@ public class Renderer extends RendererModule{
overlays.drawBottom(); overlays.drawBottom();
EntityDraw.drawWith(playerGroup, p -> true, Player::drawBuildRequests); drawAndInterpolate(playerGroup, p -> true, Player::drawBuildRequests);
drawAllTeams(true); drawAllTeams(true);
EntityDraw.draw(bulletGroup); drawAndInterpolate(bulletGroup);
EntityDraw.draw(effectGroup); drawAndInterpolate(effectGroup);
overlays.drawTop(); overlays.drawTop();
@ -238,7 +242,7 @@ public class Renderer extends RendererModule{
if(showPaths && debug) drawDebug(); if(showPaths && debug) drawDebug();
EntityDraw.drawWith(playerGroup, p -> !p.isLocal && !p.isDead(), Player::drawName); drawAndInterpolate(playerGroup, p -> !p.isLocal && !p.isDead(), Player::drawName);
batch.end(); batch.end();
} }
@ -250,25 +254,66 @@ public class Renderer extends RendererModule{
if(group.count(p -> p.isFlying() == flying) + if(group.count(p -> p.isFlying() == flying) +
playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue; playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue;
EntityDraw.drawWith(unitGroups[team.ordinal()], u -> u.isFlying() == flying, Unit::drawUnder); drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying, Unit::drawUnder);
EntityDraw.drawWith(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawUnder); drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawUnder);
Shaders.outline.color.set(team.color); Shaders.outline.color.set(team.color);
Shaders.mix.color.set(Color.WHITE); Shaders.mix.color.set(Color.WHITE);
Graphics.beginShaders(Shaders.outline); Graphics.beginShaders(Shaders.outline);
Graphics.shader(Shaders.mix, true); Graphics.shader(Shaders.mix, true);
EntityDraw.draw(unitGroups[team.ordinal()], u -> u.isFlying() == flying); drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying);
EntityDraw.draw(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team); drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team);
Graphics.shader(); Graphics.shader();
blocks.drawTeamBlocks(Layer.turret, team); blocks.drawTeamBlocks(Layer.turret, team);
Graphics.endShaders(); Graphics.endShaders();
EntityDraw.drawWith(unitGroups[team.ordinal()], u -> u.isFlying() == flying, Unit::drawOver); drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying, Unit::drawOver);
EntityDraw.drawWith(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver); drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver);
} }
} }
private <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group){
drawAndInterpolate(group, t -> true, DrawTrait::draw);
}
private <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group, Predicate<T> toDraw){
drawAndInterpolate(group, toDraw, DrawTrait::draw);
}
private <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group, Predicate<T> toDraw, Consumer<T> drawer){
EntityDraw.drawWith(group, toDraw, t -> {
float lastx = t.getX(), lasty = t.getY(), lastrot = 0f;
if(threads.doInterpolate() && threads.isEnabled() && t instanceof SolidTrait){
SolidTrait s = (SolidTrait)t;
lastrot = s.getRotation();
if(s.lastUpdated() != 0){
float timeSinceUpdate = TimeUtils.timeSinceMillis(s.lastUpdated());
float alpha = Math.min(timeSinceUpdate / s.updateSpacing(), 1f);
tmpVector1.set(s.lastPosition().x, s.lastPosition().y)
.lerp(tmpVector2.set(lastx, lasty), alpha);
s.setRotation(Mathf.slerp(s.lastPosition().z, lastrot, alpha));
s.set(tmpVector1.x, tmpVector1.y);
}
}
drawer.accept(t);
if(threads.doInterpolate() && threads.isEnabled()) {
t.set(lastx, lasty);
if (t instanceof SolidTrait) {
((SolidTrait) t).setRotation(lastrot);
}
}
});
}
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
super.resize(width, height); super.resize(width, height);
@ -284,10 +329,12 @@ public class Renderer extends RendererModule{
} }
public Vector2 averagePosition(){ public Vector2 averagePosition(){
avgPosition.setZero(); avgPosition.setZero();
for(Player player : players){
avgPosition.add(player.x, player.y); drawAndInterpolate(playerGroup, p -> p.isLocal, p -> {
} avgPosition.add(p.x, p.y);
});
avgPosition.scl(1f / players.length); avgPosition.scl(1f / players.length);
return avgPosition; return avgPosition;
} }

View File

@ -111,6 +111,10 @@ public class ThreadHandler {
return enabled; return enabled;
} }
public boolean doInterpolate(){
return enabled && Math.abs(Gdx.graphics.getFramesPerSecond() - getFPS()) > 15;
}
public boolean isOnThread(){ public boolean isOnThread(){
return impl.isOnThread(); return impl.isOnThread();
} }
@ -118,7 +122,7 @@ public class ThreadHandler {
private void runLogic(){ private void runLogic(){
try { try {
while (true) { while (true) {
long time = TimeUtils.millis(); long time = TimeUtils.nanoTime();
synchronized (toRun) { synchronized (toRun) {
for(Runnable r : toRun){ for(Runnable r : toRun){
@ -131,8 +135,8 @@ public class ThreadHandler {
logic.update(); logic.update();
logic.doUpdate = false; logic.doUpdate = false;
long elapsed = TimeUtils.timeSinceMillis(time); long elapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time));
long target = (long) (1000 / 60f); long target = (long) ((1000) / 60f);
delta = Math.max(elapsed, target) / 1000f * 60f; delta = Math.max(elapsed, target) / 1000f * 60f;

View File

@ -41,8 +41,6 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Player extends Unit implements BuilderTrait, CarryTrait { public class Player extends Unit implements BuilderTrait, CarryTrait {
private static final Vector2 movement = new Vector2();
public static int typeID = -1; public static int typeID = -1;
public static final int timerShootLeft = 0; public static final int timerShootLeft = 0;
@ -73,6 +71,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
private Tile mining; private Tile mining;
private CarriableTrait carrying; private CarriableTrait carrying;
private Trail trail = new Trail(16); private Trail trail = new Trail(16);
private Vector2 movement = new Vector2();
private boolean moved;
public Player(){ public Player(){
hitbox.setSize(5); hitbox.setSize(5);
@ -242,13 +242,18 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
@Override @Override
public float drawSize() { public float drawSize() {
return 40; return isLocal ? Float.MAX_VALUE : 40;
} }
@Override @Override
public void draw(){ public void draw(){
if((debug && (!showPlayer || !showUI)) || dead) return; if((debug && (!showPlayer || !showUI)) || dead) return;
if(!movement.isZero() && moved){
walktime += Timers.delta() * movement.len()/1.6f * getFloorOn().speedMultiplier;
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
}
boolean snap = snapCamera && isLocal; boolean snap = snapCamera && isLocal;
float px = x, py =y; float px = x, py =y;
@ -491,12 +496,9 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
velocity.add(movement); velocity.add(movement);
float prex = x, prey = y;
updateVelocityStatus(mech.drag, debug ? speed : mech.maxSpeed); updateVelocityStatus(mech.drag, debug ? speed : mech.maxSpeed);
moved = distanceTo(prex, prey) > 0.01f;
if(!movement.isZero()){
walktime += Timers.delta() * velocity.len()*(1f/0.5f)/speed * getFloorOn().speedMultiplier;
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
}
if(!isShooting()){ if(!isShooting()){
if(!movement.isZero()) { if(!movement.isZero()) {

View File

@ -51,6 +51,16 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
protected float hitTime; protected float hitTime;
protected float drownTime; protected float drownTime;
@Override
public float getRotation() {
return rotation;
}
@Override
public void setRotation(float rotation) {
this.rotation = rotation;
}
@Override @Override
public void setCarrier(CarryTrait carrier) { public void setCarrier(CarryTrait carrier) {
this.carrier = carrier; this.carrier = carrier;

View File

@ -2,6 +2,7 @@ package io.anuke.mindustry.entities.bullet;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Pools; import com.badlogic.gdx.utils.Pools;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote; import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
@ -10,6 +11,7 @@ import io.anuke.mindustry.entities.traits.TeamTrait;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.In; import io.anuke.mindustry.net.In;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.impl.BulletEntity; import io.anuke.ucore.entities.impl.BulletEntity;
import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.entities.trait.Entity;
@ -59,7 +61,15 @@ public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncT
bullet.team = team; bullet.team = team;
bullet.type = type; bullet.type = type;
bullet.set(x, y);
//translate bullets backwards, purely for visual reasons
float backDelta = Timers.delta();
bullet.lastPosition().set(x-bullet.velocity.x * backDelta, y-bullet.velocity.y * backDelta, bullet.angle());
bullet.setLastUpdated(TimeUtils.millis());
bullet.setUpdateSpacing((long)((Timers.delta() / 60f) * 1000));
bullet.set(x-bullet.velocity.x * backDelta, y-bullet.velocity.y * backDelta);
bullet.add(); bullet.add();
} }

View File

@ -1,12 +1,10 @@
package io.anuke.mindustry.entities.units; package io.anuke.mindustry.entities.units;
import com.badlogic.gdx.math.Vector2;
import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote; import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.fx.ExplosionFx; import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
@ -20,7 +18,10 @@ import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.util.*; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Timer;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@ -30,7 +31,6 @@ import static io.anuke.mindustry.Vars.*;
public abstract class BaseUnit extends Unit{ public abstract class BaseUnit extends Unit{
private static int timerIndex = 0; private static int timerIndex = 0;
private static Vector2 moveVector = new Translator();
protected static final int timerTarget = timerIndex++; protected static final int timerTarget = timerIndex++;
protected static final int timerReload = timerIndex++; protected static final int timerReload = timerIndex++;
@ -113,7 +113,7 @@ public abstract class BaseUnit extends Unit{
public void targetClosest(){ public void targetClosest(){
if(target != null) return; if(target != null) return;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange()); //target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} }
public UnitState getStartState(){ public UnitState getStartState(){
@ -236,11 +236,6 @@ public abstract class BaseUnit extends Unit{
CallEntity.onUnitDeath(this); CallEntity.onUnitDeath(this);
} }
@Override
public boolean collidesOthers() {
return !isFlying();
}
@Override @Override
public void added(){ public void added(){
hitbox.setSize(type.hitsize); hitbox.setSize(type.hitsize);