Groupified entities, improved performance

This commit is contained in:
Anuken 2017-11-24 21:14:01 -05:00
parent 56113cd1de
commit 25952985dd
18 changed files with 188 additions and 173 deletions

View File

@ -79,7 +79,7 @@ project(":core") {
apply plugin: "java"
dependencies {
compile 'com.github.Anuken:ucore:9d84d75ee1'
compile 'com.github.Anuken:ucore:7752d632e1'
compile "com.badlogicgames.gdx:gdx:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-ai:1.8.1"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 744 B

View File

@ -34,6 +34,7 @@ public class Pathfind{
Tile[] path = enemy.path;
//REPRODUCE BUG: load in test map, then load save 1
Tile prev = path[enemy.node - 1];
Tile target = path[enemy.node];

View File

@ -15,8 +15,7 @@ import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.ai.Pathfind;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.EnemySpawn;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.effect.Fx;
import io.anuke.mindustry.entities.enemies.*;
import io.anuke.mindustry.input.AndroidInput;
@ -29,7 +28,7 @@ import io.anuke.mindustry.world.*;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.*;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.graphics.Atlas;
import io.anuke.ucore.modules.Module;
import io.anuke.ucore.util.Mathf;
@ -44,6 +43,10 @@ public class Control extends Module{
final Array<Weapon> weapons = new Array<>();
final ObjectMap<Item, Integer> items = new ObjectMap<>();
final EntityGroup<Enemy> enemyGroup = Entities.addGroup(Enemy.class);
final EntityGroup<TileEntity> tileGroup = Entities.addGroup(TileEntity.class, false);
final EntityGroup<Bullet> bulletGroup = Entities.addGroup(Bullet.class);
Array<EnemySpawn> spawns = new Array<>();
int wave = 1;
float wavetime;
@ -252,7 +255,7 @@ public class Control extends Module{
enemy.set(tile.worldx() + Mathf.range(range), tile.worldy() + Mathf.range(range));
enemy.tier = spawn.tier(wave, fl);
Effects.effect(Fx.spawn, enemy);
enemy.add();
enemy.add(enemyGroup);
enemies ++;
}catch (Exception e){
@ -412,11 +415,7 @@ public class Control extends Module{
}
if(Inputs.keyUp(Keys.C)){
for(Entity entity : Entities.all()){
if(entity instanceof Enemy){
entity.remove();
}
}
enemyGroup.clear();
}
if(Inputs.keyUp(Keys.O)){
@ -479,7 +478,14 @@ public class Control extends Module{
Profiler.begin("entityUpdate");
Entities.update();
//TODO
Entities.update(Entities.defaultGroup());
Entities.update(bulletGroup);
Entities.update(enemyGroup);
Entities.update(tileGroup);
Entities.collideGroups(enemyGroup, bulletGroup);
Entities.collideGroups(Entities.defaultGroup(), bulletGroup);
Profiler.end("entityUpdate");
}

View File

@ -15,7 +15,6 @@ import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.effect.Shaders;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.input.AndroidInput;
@ -29,7 +28,6 @@ import io.anuke.ucore.UCore;
import io.anuke.ucore.core.*;
import io.anuke.ucore.entities.DestructibleEntity;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.graphics.Cache;
import io.anuke.ucore.graphics.Caches;
import io.anuke.ucore.modules.RendererModule;
@ -124,9 +122,9 @@ public class Renderer extends RendererModule{
}
Profiler.begin("draw");
drawDefault();
Profiler.end("draw");
if(Vars.debug && Vars.debugGL && Timers.get("profile", 60)){
@ -147,13 +145,18 @@ public class Renderer extends RendererModule{
Graphics.surface();
Profiler.begin("blockDraw");
renderTiles();
Profiler.end("blockDraw");
Profiler.begin("entityDraw");
Entities.draw();
Graphics.shader(Shaders.outline, false);
Entities.draw(control.enemyGroup);
Graphics.shader();
Entities.draw(Entities.defaultGroup());
Entities.draw(control.bulletGroup);
Profiler.end("entityDraw");
drawShield();
@ -177,18 +180,15 @@ public class Renderer extends RendererModule{
void drawEnemyMarkers(){
Draw.color(Color.RED);
Draw.alpha(0.6f);
for(Entity entity : Entities.all()){
if(entity instanceof Enemy){
Enemy enemy = (Enemy) entity;
for(Enemy enemy : control.enemyGroup.all()){
if(Tmp.r1.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y).overlaps(enemy.hitbox.getRect(enemy.x, enemy.y))){
continue;
}
float angle = Angles.angle(camera.position.x, camera.position.y, enemy.x, enemy.y);
Angles.translation(angle, Unit.dp.inPixels(20f));
Draw.rect("enemyarrow", camera.position.x + Angles.x(), camera.position.y + Angles.y(), angle);
if(Tmp.r1.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y).overlaps(enemy.hitbox.getRect(enemy.x, enemy.y))){
continue;
}
float angle = Angles.angle(camera.position.x, camera.position.y, enemy.x, enemy.y);
Angles.translation(angle, Unit.dp.inPixels(20f));
Draw.rect("enemyarrow", camera.position.x + Angles.x(), camera.position.y + Angles.y(), angle);
}
Draw.color();
}
@ -303,8 +303,7 @@ public class Renderer extends RendererModule{
if(!Mathf.inBounds(worldx, worldy, floorCache))
continue;
Draw.linerect(worldx * chunksize * tilesize, worldy * chunksize * tilesize,
chunksize * tilesize, chunksize * tilesize);
Draw.linerect(worldx * chunksize * tilesize, worldy * chunksize * tilesize, chunksize * tilesize, chunksize * tilesize);
}
}
Draw.reset();
@ -330,7 +329,7 @@ public class Renderer extends RendererModule{
}
void renderPixelOverlay(){
//draw tutorial placement point
if(Vars.control.tutorial.showBlock()){
int x = World.core.x + Vars.control.tutorial.getPlacePoint().x;
@ -348,10 +347,9 @@ public class Renderer extends RendererModule{
}
Draw.reset();
}
//draw placement box
if(player.recipe != null && Vars.control.hasItems(player.recipe.requirements)
&& (!ui.hasMouse() || android) && AndroidInput.mode == PlaceMode.cursor){
if(player.recipe != null && Vars.control.hasItems(player.recipe.requirements) && (!ui.hasMouse() || android) && AndroidInput.mode == PlaceMode.cursor){
float x = 0;
float y = 0;
@ -424,7 +422,7 @@ public class Renderer extends RendererModule{
Draw.reset();
}
}
//draw selected block health
if(player.recipe == null && !ui.hasMouse()){
Tile tile = World.tile(Input.tilex(), Input.tiley());
@ -443,23 +441,22 @@ public class Renderer extends RendererModule{
}
}
boolean smoothcam = Settings.getBool("smoothcam");
//draw entity health bars
for(Entity entity : Entities.all()){
if(entity instanceof DestructibleEntity && !(entity instanceof TileEntity)){
DestructibleEntity dest = ((DestructibleEntity) entity);
if(dest instanceof Player && Vars.snapCamera && smoothcam && Settings.getBool("pixelate")){
drawHealth((int) dest.x, (int) dest.y - 7f, dest.health, dest.maxhealth);
}else{
drawHealth(dest.x, dest.y - 7f, dest.health, dest.maxhealth);
}
}
for(Enemy entity : control.enemyGroup.all()){
drawHealth(entity);
}
drawHealth(player);
}
void drawHealth(DestructibleEntity dest){
if(dest instanceof Player && Vars.snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate")){
drawHealth((int) dest.x, (int) dest.y - 7f, dest.health, dest.maxhealth);
}else{
drawHealth(dest.x, dest.y - 7f, dest.health, dest.maxhealth);
}
}
void drawHealth(float x, float y, float health, float maxhealth){
drawBar(Color.RED, x, y, health / maxhealth);
}

View File

@ -4,9 +4,7 @@ import static io.anuke.mindustry.Vars.tilesize;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.World;
import io.anuke.ucore.entities.BulletEntity;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.entities.*;
import io.anuke.ucore.util.Mathf;
public class Bullet extends BulletEntity{
@ -75,5 +73,10 @@ public class Bullet extends BulletEntity{
public int getDamage(){
return damage == -1 ? type.damage : damage;
}
@Override
public Bullet add(){
return super.add(Entities.getGroup(Bullet.class));
}
}

View File

@ -17,6 +17,7 @@ import io.anuke.mindustry.world.blocks.ProductionBlocks;
import io.anuke.mindustry.world.blocks.types.Wall;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.util.Mathf;
@ -38,7 +39,7 @@ public class TileEntity extends Entity{
health = maxhealth;
if(added){
add();
add(Entities.getGroup(TileEntity.class));
}
return this;

View File

@ -34,26 +34,25 @@ public class TeslaOrb extends Entity{
float curx = x, cury = y;
float shake = 3f;
outer:
while(true){
int max = 7;
while(points.size < max){
if(Mathf.chance(stopchance)){
break;
}
Array<SolidEntity> enemies = Entities.getNearby(curx, cury, range*2f);
Array<SolidEntity> enemies = Entities.getNearby(Entities.getGroup(Enemy.class), curx, cury, range*2f);
for(SolidEntity entity : enemies){
if(entity instanceof Enemy && entity.distanceTo(curx, cury) < range
&& !hit.contains((Enemy)entity)){
if(entity.distanceTo(curx, cury) < range && !hit.contains((Enemy)entity)){
hit.add((Enemy)entity);
points.add(new Vector2(entity.x + Mathf.range(shake), entity.y + Mathf.range(shake)));
damageEnemy((Enemy)entity);
curx = entity.x;
cury = entity.y;
continue outer;
break;
}
}
break;
}
if(points.size == 0){
@ -122,6 +121,10 @@ public class TeslaOrb extends Entity{
Timers.run(1f, ()->{
shock();
});
}
@Override
public float drawSize(){
return 200;
}
}

View File

@ -2,7 +2,6 @@ package io.anuke.mindustry.entities.enemies;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.Vars;
@ -20,14 +19,9 @@ import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;
public class Enemy extends DestructibleEntity{
public final static Color[] tierColors = {
Color.valueOf("ffe451"),
Color.valueOf("f48e20"),
Color.valueOf("ff6757"),
Color.valueOf("ff2d86")
};
public final static Color[] tierColors = { Color.valueOf("ffe451"), Color.valueOf("f48e20"), Color.valueOf("ff6757"), Color.valueOf("ff2d86") };
public final static int maxtier = 4;
protected float speed = 0.3f;
protected float reload = 32;
protected float range = 60;
@ -38,152 +32,151 @@ public class Enemy extends DestructibleEntity{
protected BulletType bullet = BulletType.small;
protected String shootsound = "enemyshoot";
protected int damage;
public int spawn;
public int node = -1;
public Tile[] path;
public Vector2 direction = new Vector2();
public float xvelocity, yvelocity;
public Entity target;
public int tier = 1;
public Enemy(int spawn){
public Enemy(int spawn) {
this.spawn = spawn;
hitbox.setSize(5f);
hitboxTile.setSize(4f);
maxhealth = 60;
heal();
}
public float drawSize(){
return 12;
}
void move(){
boolean nearCore = distanceTo(World.core.worldx(), World.core.worldy()) <= range - 18f;
Vector2 vec;
if(nearCore){
vec = Tmp.v2.setZero();
target = World.core.entity;
}else{
vec = Pathfind.find(this);
vec = Pathfind.find(this);
vec.sub(x, y).setLength(speed);
}
Array<SolidEntity> entities = Entities.getNearby(x, y, range);
Vector2 shift = Tmp.v3.setZero();
float shiftRange = hitbox.width + 3f;
float avoidRange = 16f;
float avoidSpeed = 0.1f;
for(SolidEntity other : entities){
Entities.getNearby(Entities.getGroup(Enemy.class), x, y, range, other -> {
float dst = other.distanceTo(this);
if(other != this && other instanceof Enemy){
if(dst < shiftRange){
float scl = Mathf.clamp(1.4f - dst/shiftRange);
shift.add((x - other.x) * scl, (y - other.y) * scl);
}else if(dst < avoidRange){
Tmp.v2.set((x - other.x), (y - other.y)).setLength(avoidSpeed);
shift.add(Tmp.v2);
}
if(other == this)
return;
if(dst < shiftRange){
float scl = Mathf.clamp(1.4f - dst / shiftRange);
shift.add((x - other.x) * scl, (y - other.y) * scl);
}else if(dst < avoidRange){
Tmp.v2.set((x - other.x), (y - other.y)).setLength(avoidSpeed);
shift.add(Tmp.v2);
}
}
});
shift.limit(1f);
vec.add(shift.scl(0.5f));
move(vec.x*Timers.delta(), vec.y*Timers.delta());
move(vec.x * Timers.delta(), vec.y * Timers.delta());
if(Timers.get(this, "target", 15) && !nearCore){
target = World.findTileTarget(x, y, null, range, false);
//no tile found
if(target == null){
target = Entities.getClosest(entities, x, y, range, e -> e instanceof Player);
target = Entities.getClosest(Entities.defaultGroup(), x, y, range, e -> e instanceof Player);
}
}
if(target != null && bullet != null){
updateShooting();
}
}
void updateShooting(){
if(Timers.get(this, "reload", reload*Vars.multiplier)){
if(Timers.get(this, "reload", reload * Vars.multiplier)){
shoot(bullet);
Effects.sound(shootsound, this);
}
}
void shoot(BulletType bullet){
shoot(bullet, 0);
}
void shoot(BulletType bullet, float rotation){
vector.set(length, 0).rotate(direction.angle() + rotation);
Bullet out = new Bullet(bullet, this, x+vector.x, y+vector.y, direction.angle() + rotation).add();
out.damage = (int)(damage*Vars.multiplier);
Bullet out = new Bullet(bullet, this, x + vector.x, y + vector.y, direction.angle() + rotation).add();
out.damage = (int) (damage * Vars.multiplier);
}
public void findClosestNode(){
Pathfind.find(this);
int index = 0;
int cindex = -1;
float dst = Float.MAX_VALUE;
//find closest node index
for(Tile tile : path){
if(Vector2.dst(tile.worldx(), tile.worldy(), x, y) < dst){
dst = Vector2.dst(tile.worldx(), tile.worldy(), x, y);
cindex = index;
}
index ++;
index++;
}
node = Math.max(cindex, 1);
cindex = Math.max(cindex, 1);
//set node to that index
node = cindex;
int x2 = path[node].x, y2 = path[node].y;
//if the enemy can move to that node right now, set its position to it
if(World.raycast(Mathf.scl2(x, Vars.tilesize), Mathf.scl2(y, Vars.tilesize), x2, y2) == null){
Timers.run(Mathf.random(15f), ()->{
//if the enemy can't move to that node right now, set its position to it
if(World.raycast(Mathf.scl2(x, Vars.tilesize), Mathf.scl2(y, Vars.tilesize), x2, y2) != null){
Timers.run(Mathf.random(15f), () -> {
set(x2 * Vars.tilesize, y2 * Vars.tilesize);
});
}
}
@Override
public void added(){
if(bullet != null){
damage = (int)(bullet.damage * (1 + (tier - 1) * 1f));
damage = (int) (bullet.damage * (1 + (tier - 1) * 1f));
}
maxhealth *= tier;
speed += 0.04f*tier + Mathf.range(0.1f);
speed += 0.04f * tier + Mathf.range(0.1f);
reload /= Math.max(tier / 1.5f, 1f);
range += tier*5;
range += tier * 5;
speed = Math.max(speed, 0.07f);
heal();
}
@Override
public boolean collides(SolidEntity other){
return (other instanceof Bullet) && !(((Bullet)other).owner instanceof Enemy);
return (other instanceof Bullet) && !(((Bullet) other).owner instanceof Enemy);
}
@Override
public void onDeath(){
Effects.effect(Fx.explosion, this);
@ -192,44 +185,50 @@ public class Enemy extends DestructibleEntity{
remove();
dead = true;
}
@Override
public void removed(){
if(!dead){
Vars.control.enemyDeath();
}
}
@Override
public void update(){
float lastx = x, lasty = y;
move();
xvelocity = (x - lastx) / Timers.delta();
yvelocity = (y - lasty) / Timers.delta();
if(target == null || alwaysRotate){
direction.add(xvelocity * Timers.delta() / 3f, yvelocity * Timers.delta() / 3f);
direction.limit(speed*rotatespeed);
direction.limit(speed * rotatespeed);
}else{
float angle = angleTo(target);
direction.lerp(vector.set(0, 1).setAngle(angle), turretrotatespeed * Timers.delta());
direction.lerp(vector.set(1f, 0f).rotate(angle), turretrotatespeed * Timers.delta());
}
}
@Override
public void draw(){
String region = ClassReflection.getSimpleName(getClass()).toLowerCase() + "-t" + Mathf.clamp(tier, 1, 3);
Shaders.outline.color.set(tierColors[tier - 1]);
Shaders.outline.region = Draw.region(region);
Shaders.outline.apply();
Draw.color();
Draw.rect(region, x, y, direction.angle() - 90);
Graphics.flush();
}
@Override
public void draw(){
String region = ClassReflection.getSimpleName(getClass()).toLowerCase() + "-t" + Mathf.clamp(tier, 1, 3);
//TODO is this really necessary?
Shaders.outline.color.set(tierColors[tier-1]);
Shaders.outline.region = Draw.region(region);
Graphics.shader(Shaders.outline);
Draw.color();
Draw.rect(region, x, y, direction.angle()-90);
Graphics.shader();
public <T extends Entity> T add(){
return (T) add(Entities.getGroup(Enemy.class));
}
}

View File

@ -35,7 +35,8 @@ public class HealerEnemy extends Enemy{
move(vec.x*Timers.delta(), vec.y*Timers.delta());
if(Timers.get(this, "target", 15)){
target = Entities.getClosest(x, y, range, e->e instanceof Enemy && e != this && ((Enemy)e).healthfrac() < 1f);
target = Entities.getClosest(Entities.getGroup(Enemy.class),
x, y, range, e -> e instanceof Enemy && e != this && ((Enemy)e).healthfrac() < 1f);
}
if(target != null){

View File

@ -3,6 +3,7 @@ package io.anuke.mindustry.entities.enemies;
import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.BulletType;
import io.anuke.ucore.core.Draw;
import io.anuke.ucore.core.Timers;
@ -14,12 +15,15 @@ public class TargetEnemy extends Enemy{
maxhealth = 10;
}
@Override
void move(){
speed = 0f;
super.move();
}
void shoot(){
@Override
void shoot(BulletType bullet){
//do nothing
}
@Override

View File

@ -23,7 +23,6 @@ import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity;
/*
* Save format:
@ -182,17 +181,16 @@ public class SaveIO{
int totalEnemies = 0;
for(Entity entity : Entities.all()){
if(entity instanceof Enemy && idEnemies.containsKey((Class<? extends Enemy>) entity.getClass())){
for(Enemy entity : Entities.get(Enemy.class)){
if(idEnemies.containsKey(entity.getClass())){
totalEnemies ++;
}
}
stream.writeInt(totalEnemies); //enemy amount
for(Entity entity : Entities.all()){
if(entity instanceof Enemy && idEnemies.containsKey((Class<? extends Enemy>) entity.getClass())){
Enemy enemy = (Enemy)entity;
for(Enemy enemy : Entities.get(Enemy.class)){
if(idEnemies.containsKey(enemy.getClass())){
stream.writeByte(idEnemies.get(enemy.getClass())); //type
stream.writeByte(enemy.spawn); //lane
stream.writeFloat(enemy.x); //x
@ -333,7 +331,7 @@ public class SaveIO{
enemy.x = x;
enemy.y = y;
enemy.tier = tier;
enemy.add();
enemy.add(Entities.getGroup(Enemy.class));
enemiesToUpdate.add(enemy);
}catch (Exception e){
throw new RuntimeException(e);

View File

@ -10,6 +10,7 @@ import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.resource.Item;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Draw;
@ -109,14 +110,6 @@ public class HudFragment implements Fragment{
visible(()->!GameState.is(State.menu));
}}.end();
//settings icon
new table(){{
atop().aright();
new imagebutton("icon-info", Unit.dp.inPixels(30f), ()->{
ui.showAbout();
}).get().pad(14);
}}.end().visible(()->GameState.is(State.menu));
//respawn background table
new table("white"){{
respawntable = get();
@ -140,7 +133,7 @@ public class HudFragment implements Fragment{
new table(){{
abottom();
aleft();
new label((StringSupplier)()->"[purple]entities: " + Entities.amount()).left();
new label((StringSupplier)()->"[purple]enemies: " + Entities.getGroup(Enemy.class).amount()).left();
row();
new label((StringSupplier)()->"[orange]noclip: " + Vars.noclip).left();
row();

View File

@ -62,6 +62,14 @@ public class MenuFragment implements Fragment{
visible(()->GameState.is(State.menu));
}}.end();
}
}
//settings icon
new table(){{
atop().aright();
new imagebutton("icon-info", Unit.dp.inPixels(30f), ()->{
ui.showAbout();
}).get().pad(Unit.dp.inPixels(14));
}}.end().visible(()->GameState.is(State.menu));
}
}

View File

@ -139,9 +139,10 @@ public class Turret extends Block{
if(hasAmmo(tile) || (Vars.debug && Vars.infiniteAmmo)){
if(Timers.get(entity, "target", targetInterval)){
entity.target = (Enemy)Entities.getClosest(tile.worldx(), tile.worldy(), range, e->{
return e instanceof Enemy && !((Enemy)e).isDead();
});
entity.target = (Enemy)Entities.getClosest(Entities.getGroup(Enemy.class),
tile.worldx(), tile.worldy(), range, e->
e instanceof Enemy && !((Enemy)e).isDead()
);
}
if(entity.target != null){

Binary file not shown.

Binary file not shown.

View File

@ -17,7 +17,7 @@ public class DesktopLauncher {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setTitle("Mindustry");
config.setMaximized(true);
config.useVsync(false);
//config.useVsync(false);
config.setWindowedMode(800, 600);
config.setWindowIcon("sprites/icon.png");