A little bit FPS independency

This commit is contained in:
Mike Schwörer 2014-08-10 20:59:50 +02:00
parent 59b679756a
commit faf6e21500
13 changed files with 315 additions and 44 deletions

View File

@ -5,7 +5,6 @@ import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
@ -14,11 +13,14 @@ public abstract class AbstractGameRenderer {
private final static float MIN_GAME_WIDTH = 672; // ~ 512 * (4/3)
private final static float MAX_GAME_WIDTH = 1024; // ~ 512 * (2/1) // ~~ 16:9
private int debugTextCount;
private OrthographicCamera cam;
protected ShapeRenderer shapeRenderer;
protected BitmapFont font = new BitmapFont();
protected SpriteBatch spriteBatch = new SpriteBatch();
protected SpriteBatch fontBatch = new SpriteBatch();
public AbstractGameRenderer(float width, float height) {
cam = new OrthographicCamera();
@ -54,14 +56,25 @@ public abstract class AbstractGameRenderer {
Gdx.app.error("SIZE", "Maximal aspect ratio is 2:1");
cam.setToOrtho(false, cam_width, GAME_HEIGHT);
shapeRenderer.setProjectionMatrix(cam.combined);
spriteBatch.setProjectionMatrix(cam.combined);
fontBatch.setProjectionMatrix(cam.combined);
}
protected void beginDebug() {
fontBatch.begin();
font.setColor(1, 0, 1, 1);
debugTextCount = 0;
}
protected void renderDebug(String s) {
spriteBatch.begin();
font.setColor(0, 1, 0, 1);
font.draw(spriteBatch, s, 0, Gdx.graphics.getHeight());
spriteBatch.end();
font.draw(fontBatch, s, 10, GAME_HEIGHT - 10 - debugTextCount++ * 20);
}
protected void endDebug() {
fontBatch.end();
}
public abstract void doRender();

View File

@ -0,0 +1,51 @@
package de.samdev.colorrunner.game.renderer;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
public class CRAssets {
public static Texture TEX_PLAYER_UP;
public static Texture TEX_PLAYER_RIGHT;
public static Texture TEX_PLAYER_LEFT;
public static Texture TEX_PLAYER_DOWN;
public static Texture TEX_FLOORTILE;
static {
{
Pixmap pixmap = new Pixmap(64, 64, Format.RGBA8888);
pixmap.setColor(1, 0, 0, 1);
pixmap.fill();
TEX_PLAYER_UP = new Texture(pixmap);
}
{
Pixmap pixmap = new Pixmap(64, 64, Format.RGBA8888);
pixmap.setColor(0, 1, 0, 1);
pixmap.fill();
TEX_PLAYER_RIGHT = new Texture(pixmap);
}
{
Pixmap pixmap = new Pixmap(64, 64, Format.RGBA8888);
pixmap.setColor(0, 0, 1, 1);
pixmap.fill();
TEX_PLAYER_DOWN = new Texture(pixmap);
}
{
Pixmap pixmap = new Pixmap(64, 64, Format.RGBA8888);
pixmap.setColor(1, 1, 0, 1);
pixmap.fill();
TEX_PLAYER_LEFT = new Texture(pixmap);
}
{
Pixmap pixmap = new Pixmap(64, 64, Format.RGBA8888);
pixmap.setColor(1, 1, 1, 1);
pixmap.fill();
TEX_FLOORTILE = new Texture(pixmap);
}
}
}

View File

@ -1,12 +1,15 @@
package de.samdev.colorrunner.game.renderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Vector2;
import de.samdev.colorrunner.game.world.CRGameWorld;
import de.samdev.colorrunner.game.world.entities.CRGameEntity;
import de.samdev.colorrunner.game.world.entities.MovingEntity;
public class CRGameRenderer extends AbstractGameRenderer {
private CRGameWorld gameworld;
public double avgExecTime = 0;
public CRGameRenderer(CRGameWorld _world, float width, float height) {
super(width, height);
@ -16,14 +19,32 @@ public class CRGameRenderer extends AbstractGameRenderer {
@Override
public void doRender() {
beginDebug();
renderDebug("FPS: " + (int)gameworld.fps.getFPS());
renderDebug("Entitys: " + gameworld.entities.size());
renderDebug("ExecTime: " + (int)(avgExecTime*100) / 100.0+ " / " + (int)(10000/gameworld.fps.getFPS())/10.0 + "ms (" + (int)((avgExecTime/(1000/gameworld.fps.getFPS()))*1000) / 10.0 + "%)");
endDebug();
shapeRenderer.begin(ShapeType.Filled);
shapeRenderer.setColor(1, 0, 0, 1);
spriteBatch.begin();
spriteBatch.disableBlending(); // Performance, bitches
for (CRGameEntity e : gameworld.entities) {
spriteBatch.draw(e.getTexture(), e.bounds.x, e.bounds.y, e.bounds.width, e.bounds.height);
}
spriteBatch.end();
shapeRenderer.begin(ShapeType.Line);
shapeRenderer.setColor(1, 0, 1, 1);
for (CRGameEntity e : gameworld.entities) {
shapeRenderer.rect(e.bounds.x, e.bounds.y, e.bounds.width, e.bounds.height);
if (e instanceof MovingEntity) {
MovingEntity me = (MovingEntity) e;
shapeRenderer.line(me.bounds.getCenter(new Vector2()), me.bounds.getCenter(new Vector2()).add(me.getVelocity().x / 15f, 0));
shapeRenderer.line(me.bounds.getCenter(new Vector2()), me.bounds.getCenter(new Vector2()).add(0 , me.getVelocity().y / 15f));
}
}
shapeRenderer.end();
}

View File

@ -0,0 +1,44 @@
package de.samdev.colorrunner.game.world;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.TimeUtils;
public class AverageExecutionLogger {
private static final int UPDATE_COUNT = 60;
private long on_time;
private double average;
private long sum;
private int count;
public AverageExecutionLogger() {
average = 1;
sum = 0;
count = 0;
}
public void On() {
on_time = TimeUtils.nanoTime();
}
public void Off() {
sum += (TimeUtils.nanoTime() - on_time);
count++;
calc();
}
private void calc() {
if (count >= UPDATE_COUNT) {
average = (sum*1.0) / count;
count = 0;
sum = 0;
}
}
public double getAvg() {
return average/1000000.0;
}
}

View File

@ -3,11 +3,9 @@ package de.samdev.colorrunner.game.world;
import java.util.ArrayList;
import java.util.List;
import com.badlogic.gdx.Gdx;
import de.samdev.colorrunner.game.world.entities.CRGameEntity;
import de.samdev.colorrunner.game.world.entities.gameentities.PlayerEntity;
import de.samdev.colorrunner.game.world.entities.gameentities.FloorTileEntity;
import de.samdev.colorrunner.game.world.entities.gameentities.PlayerEntity;
import de.samdev.colorrunner.input.GameInputListener;
import de.samdev.colorrunner.input.SwipeDirection;
@ -32,6 +30,43 @@ public class CRGameWorld implements GameInputListener {
addEntity(new FloorTileEntity(this, 7*32, 32));
addEntity(new FloorTileEntity(this, 8*32, 32));
addEntity(new FloorTileEntity(this, 9*32, 32));
addEntity(new FloorTileEntity(this, 10*32, 32));
addEntity(new FloorTileEntity(this, 11*32, 32));
addEntity(new FloorTileEntity(this, 12*32, 32));
addEntity(new FloorTileEntity(this, 13*32, 32));
addEntity(new FloorTileEntity(this, 14*32, 32));
addEntity(new FloorTileEntity(this, 15*32, 32));
addEntity(new FloorTileEntity(this, 16*32, 32));
addEntity(new FloorTileEntity(this, 17*32, 32));
addEntity(new FloorTileEntity(this, 18*32, 1*32));
addEntity(new FloorTileEntity(this, 19*32, 2*32));
addEntity(new FloorTileEntity(this, 19*32, 3*32));
addEntity(new FloorTileEntity(this, 18*32, 4*32));
addEntity(new FloorTileEntity(this, 18*32, 5*32));
addEntity(new FloorTileEntity(this, 19*32, 6*32));
addEntity(new FloorTileEntity(this, 20*32, 7*32));
addEntity(new FloorTileEntity(this, 21*32, 32));
addEntity(new FloorTileEntity(this, 22*32, 32));
addEntity(new FloorTileEntity(this, 26*32, 32));
addEntity(new FloorTileEntity(this, 27*32, 32));
addEntity(new FloorTileEntity(this, 28*32, 32));
addEntity(new FloorTileEntity(this, 29*32, 32));
addEntity(new FloorTileEntity(this, 29*32, 64));
addEntity(new FloorTileEntity(this, 32*32, 32));
addEntity(new FloorTileEntity(this, 33*32, 32));
addEntity(new FloorTileEntity(this, 34*32, 32));
addEntity(new FloorTileEntity(this, 35*32, 32));
addEntity(new FloorTileEntity(this, 36*32, 32));
addEntity(new FloorTileEntity(this, 37*32, 32));
addEntity(new FloorTileEntity(this, 38*32, 32));
addEntity(new FloorTileEntity(this, 39*32, 32));
@ -53,7 +88,6 @@ public class CRGameWorld implements GameInputListener {
for (CRGameEntity ent : entities) {
ent.update(delta);
}
// Gdx.app.log("GameWorld", "update FPS[" + (int)fps.getFPS() + "] DELTA:[" + delta + "]");
}
public CRGameEntity addEntity(CRGameEntity ent) {
@ -65,21 +99,15 @@ public class CRGameWorld implements GameInputListener {
@Override
public void doJump() {
player.jump();
Gdx.app.log("GameScreen", "[DO] Jump");
}
@Override
public void switchColor(SwipeDirection sd) {
player.switchPhase(sd);
Gdx.app.log("GameScreen", "[DO] Switch + " + sd.toString());
}
@Override
public void doFly() {
player.fly();
Gdx.app.log("GameScreen", "[DO] Fly");
}
}

View File

@ -1,11 +1,14 @@
package de.samdev.colorrunner.game.world.entities;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import de.samdev.colorrunner.game.world.CRGameWorld;
public abstract class CRGameEntity {
public final static float F_EPSILON = 0.00004f;
protected CRGameWorld world;
public Rectangle bounds = new Rectangle();
@ -26,4 +29,6 @@ public abstract class CRGameEntity {
}
public abstract void update(float delta);
public abstract Texture getTexture();
}

View File

@ -3,7 +3,7 @@ package de.samdev.colorrunner.game.world.entities;
import de.samdev.colorrunner.game.world.CRGameWorld;
public abstract class GravityEntity extends MovingEntity {
public final static float GRAVITY_FORCE = 10f;
public final static float GRAVITY_FORCE = 600f;
public final static float TERMINAL_VELOCITY = 900f;
public GravityEntity(CRGameWorld _owner, float width, float height) {
@ -16,13 +16,13 @@ public abstract class GravityEntity extends MovingEntity {
@Override
public void update(float delta) {
velocity.y -= GRAVITY_FORCE;
velocity.y -= GRAVITY_FORCE * delta;
if (velocity.y < -TERMINAL_VELOCITY) {
velocity.y = -TERMINAL_VELOCITY;
}
if (isOnFloor && velocity.y < 0)
if (isTouching_BOTTOM() && velocity.y < 0)
velocity.y = 0;
super.update(delta);

View File

@ -1,13 +1,17 @@
package de.samdev.colorrunner.game.world.entities;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2;
import de.samdev.colorrunner.game.world.CRGameWorld;
public abstract class MovingEntity extends CRGameEntity {
protected Vector2 velocity = new Vector2();
protected boolean isOnFloor = false;
private boolean face_TOP_isTouching = false;
private boolean face_LEFT_isTouching = false;
private boolean face_BOTTOM_isTouching = false;
private boolean face_RIGHT_isTouching = false;
public MovingEntity(CRGameWorld _owner, float width, float height) {
this(_owner, 0, 0, width, height);
@ -40,6 +44,8 @@ public abstract class MovingEntity extends CRGameEntity {
bounds.x += correction;
bx += correction;
collided = true;
}
}
}
@ -70,6 +76,8 @@ public abstract class MovingEntity extends CRGameEntity {
bounds.y += correction;
by += correction;
collided = true;
}
}
}
@ -77,30 +85,60 @@ public abstract class MovingEntity extends CRGameEntity {
return collided;
}
private void updateOnFloor() {
private void updateTouchCollisions() {
face_TOP_isTouching = false;
face_LEFT_isTouching = false;
face_BOTTOM_isTouching = false;
face_RIGHT_isTouching = false;
for (CRGameEntity ent : world.entities) {
if (ent == this) continue;
if (Math.abs((ent.bounds.y + ent.bounds.height) - bounds.y) < 0.00004f && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x) {
isOnFloor = true;
return;
if (Math.abs((ent.bounds.y + ent.bounds.height) - bounds.y) < F_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x) {
face_BOTTOM_isTouching = true;
}
if (Math.abs((bounds.y + bounds.height) - ent.bounds.y) < F_EPSILON && ent.bounds.x < (bounds.x + bounds.width) && (ent.bounds.x + ent.bounds.width) > bounds.x) {
face_TOP_isTouching = true;
}
}
isOnFloor = false;
}
public Vector2 getVelocity() {
return velocity;
}
@Override
public void update(float delta) {
if (velocity.x != 0) {
moveByX(velocity.x * delta);
if (moveByX(velocity.x * delta)) {
velocity.x = 0;
}
}
if (velocity.y != 0) {
moveByY(velocity.y * delta);
if (moveByY(velocity.y * delta)) {
velocity.y = 0;
}
}
updateOnFloor();
updateTouchCollisions();
}
public boolean isTouching_TOP() {
return face_TOP_isTouching;
}
public boolean isTouching_LEFT() {
return face_LEFT_isTouching;
}
public boolean isTouching_BOTTOM() {
return face_BOTTOM_isTouching;
}
public boolean isTouching_RIGHT() {
return face_RIGHT_isTouching;
}
}

View File

@ -0,0 +1,15 @@
package de.samdev.colorrunner.game.world.entities;
import de.samdev.colorrunner.game.world.CRGameWorld;
public abstract class StaticEntity extends CRGameEntity {
public StaticEntity(CRGameWorld _owner, float width, float height) {
super(_owner, width, height);
}
public StaticEntity(CRGameWorld _owner, float x, float y, float width, float height) {
super(_owner, x, y, width, height);
}
}

View File

@ -1,9 +1,12 @@
package de.samdev.colorrunner.game.world.entities.gameentities;
import de.samdev.colorrunner.game.world.CRGameWorld;
import de.samdev.colorrunner.game.world.entities.CRGameEntity;
import com.badlogic.gdx.graphics.Texture;
public class FloorTileEntity extends CRGameEntity {
import de.samdev.colorrunner.game.renderer.CRAssets;
import de.samdev.colorrunner.game.world.CRGameWorld;
import de.samdev.colorrunner.game.world.entities.StaticEntity;
public class FloorTileEntity extends StaticEntity {
public final static float FLOORTILE_WIDTH = 32;
public final static float FLOORTILE_HEIGHT = 32;
@ -18,4 +21,10 @@ public class FloorTileEntity extends CRGameEntity {
}
@Override
public Texture getTexture() {
// TODO Auto-generated method stub
return CRAssets.TEX_FLOORTILE;
}
}

View File

@ -1,7 +1,8 @@
package de.samdev.colorrunner.game.world.entities.gameentities;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import de.samdev.colorrunner.game.renderer.CRAssets;
import de.samdev.colorrunner.game.world.CRGameWorld;
import de.samdev.colorrunner.game.world.entities.GravityEntity;
import de.samdev.colorrunner.input.SwipeDirection;
@ -10,32 +11,56 @@ public class PlayerEntity extends GravityEntity {
public final static float PLAYER_WIDTH = 32;
public final static float PLAYER_HEIGHT = 32;
public final static float PLAYER_JUMP_FORCE = 456;
public final static float PLAYER_FLY_FORCE = 1.5f;
public final static float PLAYER_JUMP_FORCE = 356f;
public final static float PLAYER_FLY_FORCE = 250f;
public final static float PLAYER_SPEED_FORCE = 166f;
public final static float PLAYER_TERMINAL_SPEED = 320f;
private SwipeDirection phase = SwipeDirection.UP;
private boolean doFly = false;
public PlayerEntity(CRGameWorld _owner, float x, float y) {
super(_owner, x, y, PLAYER_WIDTH, PLAYER_HEIGHT);
velocity.x = 320;
}
@Override
public void update(float delta) {
super.update(delta);
if (velocity.x < PLAYER_TERMINAL_SPEED)
velocity.x += PLAYER_SPEED_FORCE * delta;
if (doFly)
velocity.y += PLAYER_FLY_FORCE * delta;
doFly = false;
}
public void jump() {
if (isOnFloor)
if (isTouching_BOTTOM())
velocity.y = PLAYER_JUMP_FORCE;
}
public void switchPhase(SwipeDirection sd) {
// TODO Auto-generated method stub
phase = sd;
}
public void fly() {
if (! isOnFloor)
velocity.y += PLAYER_FLY_FORCE;
if (! isTouching_BOTTOM())
doFly = true;
}
@Override
public Texture getTexture() {
switch (phase) {
case UP: return CRAssets.TEX_PLAYER_UP;
case RIGHT: return CRAssets.TEX_PLAYER_RIGHT;
case DOWN: return CRAssets.TEX_PLAYER_DOWN;
case LEFT: return CRAssets.TEX_PLAYER_LEFT;
default: return null;
}
}
}

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.Screen;
import com.badlogic.gdx.input.GestureDetector;
import de.samdev.colorrunner.game.renderer.CRGameRenderer;
import de.samdev.colorrunner.game.world.AverageExecutionLogger;
import de.samdev.colorrunner.game.world.CRGameWorld;
import de.samdev.colorrunner.input.CRGameInputProcessor;
@ -13,6 +14,8 @@ public class GameScreen implements Screen {
private CRGameWorld world;
private CRGameRenderer renderer;
private CRGameInputProcessor input = new CRGameInputProcessor(world);
private AverageExecutionLogger execTime = new AverageExecutionLogger();
public GameScreen() {
world = new CRGameWorld(); // initialize world
@ -23,10 +26,21 @@ public class GameScreen implements Screen {
@Override
public void render(float delta) {
execTime.On();
//#######################
delta = Math.min(delta, (1/45f));
world.update(delta);
renderer.render();
input.update();
//#######################
renderer.avgExecTime = execTime.getAvg();
execTime.Off();
}
@Override

View File

@ -12,6 +12,14 @@ public class DesktopLauncher {
config.width = 1000;
config.height = 563;
/*
* For FPS Independence Testing
*/
// config.vSyncEnabled = false; // Setting to false disables vertical sync
// config.foregroundFPS = 0; // Setting to 0 disables foreground fps throttling
// config.backgroundFPS = 0; // Setting to 0 disables background fps throttling
new LwjglApplication(new CRGame(), config);
}
}