Compare commits

..

1 Commits

Author SHA1 Message Date
7a94db1383 Add sharing asteroids methods 2020-01-30 22:58:29 +04:00
42 changed files with 542 additions and 1191 deletions

View File

@ -1,102 +0,0 @@
info face="Retro Gaming" size=32 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-1,-1
common lineHeight=42 base=32 scaleW=256 scaleH=256 pages=1 packed=0
page id=0 file="Retro_Font.png"
chars count=97
char id=0 x=0 y=0 width=15 height=43 xoffset=4 yoffset=-1 xadvance=20 page=0 chnl=0
char id=10 x=0 y=0 width=15 height=43 xoffset=4 yoffset=-1 xadvance=20 page=0 chnl=0
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=31 xadvance=13 page=0 chnl=0
char id=33 x=246 y=69 width=8 height=26 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0
char id=34 x=218 y=173 width=17 height=11 xoffset=2 yoffset=7 xadvance=20 page=0 chnl=0
char id=35 x=22 y=147 width=31 height=26 xoffset=2 yoffset=7 xadvance=34 page=0 chnl=0
char id=36 x=15 y=0 width=22 height=38 xoffset=2 yoffset=1 xadvance=25 page=0 chnl=0
char id=37 x=0 y=147 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=38 x=53 y=147 width=26 height=26 xoffset=2 yoffset=7 xadvance=29 page=0 chnl=0
char id=39 x=242 y=147 width=8 height=11 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0
char id=40 x=238 y=95 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=41 x=110 y=121 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=42 x=193 y=173 width=25 height=17 xoffset=2 yoffset=10 xadvance=28 page=0 chnl=0
char id=43 x=174 y=173 width=19 height=17 xoffset=2 yoffset=11 xadvance=22 page=0 chnl=0
char id=44 x=0 y=190 width=11 height=11 xoffset=-1 yoffset=25 xadvance=11 page=0 chnl=0
char id=45 x=77 y=190 width=14 height=5 xoffset=2 yoffset=17 xadvance=17 page=0 chnl=0
char id=46 x=69 y=190 width=8 height=8 xoffset=2 yoffset=25 xadvance=11 page=0 chnl=0
char id=47 x=180 y=121 width=23 height=26 xoffset=2 yoffset=7 xadvance=26 page=0 chnl=0
char id=48 x=66 y=121 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=49 x=114 y=95 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=50 x=128 y=95 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=51 x=150 y=95 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=52 x=172 y=95 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=53 x=194 y=95 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=54 x=216 y=95 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=55 x=0 y=121 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=56 x=22 y=121 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=57 x=44 y=121 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=58 x=124 y=147 width=8 height=20 xoffset=2 yoffset=13 xadvance=11 page=0 chnl=0
char id=59 x=79 y=147 width=11 height=23 xoffset=-1 yoffset=13 xadvance=11 page=0 chnl=0
char id=60 x=90 y=147 width=17 height=23 xoffset=2 yoffset=10 xadvance=20 page=0 chnl=0
char id=61 x=33 y=190 width=17 height=11 xoffset=2 yoffset=16 xadvance=20 page=0 chnl=0
char id=62 x=107 y=147 width=17 height=23 xoffset=2 yoffset=10 xadvance=20 page=0 chnl=0
char id=63 x=88 y=121 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=64 x=203 y=121 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=65 x=62 y=0 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=66 x=84 y=0 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=67 x=106 y=0 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=68 x=128 y=0 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=69 x=150 y=0 width=23 height=26 xoffset=2 yoffset=7 xadvance=26 page=0 chnl=0
char id=70 x=173 y=0 width=23 height=26 xoffset=2 yoffset=7 xadvance=26 page=0 chnl=0
char id=71 x=196 y=0 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=72 x=218 y=0 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=73 x=0 y=43 width=19 height=26 xoffset=2 yoffset=7 xadvance=22 page=0 chnl=0
char id=74 x=19 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=75 x=41 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=76 x=63 y=43 width=20 height=26 xoffset=2 yoffset=7 xadvance=23 page=0 chnl=0
char id=77 x=83 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=78 x=105 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=79 x=127 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=80 x=149 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=81 x=171 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=82 x=193 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=83 x=215 y=43 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=84 x=0 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=85 x=22 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=86 x=44 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=87 x=66 y=69 width=25 height=26 xoffset=2 yoffset=7 xadvance=28 page=0 chnl=0
char id=88 x=91 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=89 x=113 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=90 x=135 y=69 width=23 height=26 xoffset=2 yoffset=7 xadvance=26 page=0 chnl=0
char id=91 x=124 y=121 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=92 x=225 y=121 width=23 height=26 xoffset=2 yoffset=7 xadvance=26 page=0 chnl=0
char id=93 x=138 y=121 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=94 x=11 y=190 width=22 height=11 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=95 x=91 y=190 width=17 height=5 xoffset=2 yoffset=28 xadvance=20 page=0 chnl=0
char id=96 x=235 y=173 width=14 height=11 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=97 x=132 y=147 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=98 x=158 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=99 x=154 y=147 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=100 x=180 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=101 x=176 y=147 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=102 x=237 y=43 width=17 height=26 xoffset=2 yoffset=7 xadvance=20 page=0 chnl=0
char id=103 x=202 y=69 width=22 height=26 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=104 x=224 y=69 width=22 height=26 xoffset=2 yoffset=7 xadvance=25 page=0 chnl=0
char id=105 x=240 y=0 width=8 height=26 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0
char id=106 x=45 y=0 width=17 height=29 xoffset=2 yoffset=7 xadvance=20 page=0 chnl=0
char id=107 x=0 y=95 width=20 height=26 xoffset=2 yoffset=7 xadvance=23 page=0 chnl=0
char id=108 x=20 y=95 width=11 height=26 xoffset=2 yoffset=7 xadvance=14 page=0 chnl=0
char id=109 x=198 y=147 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=110 x=220 y=147 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=111 x=0 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=112 x=31 y=95 width=22 height=26 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=113 x=53 y=95 width=22 height=26 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=114 x=22 y=173 width=20 height=17 xoffset=2 yoffset=16 xadvance=23 page=0 chnl=0
char id=115 x=42 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=116 x=75 y=95 width=17 height=26 xoffset=2 yoffset=7 xadvance=20 page=0 chnl=0
char id=117 x=64 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=118 x=86 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=119 x=108 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=120 x=130 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=121 x=92 y=95 width=22 height=26 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=122 x=152 y=173 width=22 height=17 xoffset=2 yoffset=16 xadvance=25 page=0 chnl=0
char id=123 x=152 y=121 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=124 x=37 y=0 width=8 height=32 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0
char id=125 x=166 y=121 width=14 height=26 xoffset=2 yoffset=7 xadvance=17 page=0 chnl=0
char id=126 x=50 y=190 width=19 height=11 xoffset=2 yoffset=13 xadvance=22 page=0 chnl=0
kernings count=0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -12,9 +12,8 @@ public class Asteroids extends ApplicationAdapter {
@Override @Override
public void create() { public void create() {
graphics = new Graphics(); graphics = new Graphics(Constants.WIDTH, Constants.HEIGHT);
graphics.initialize(); graphics.initialize();
updateViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
game = new Game(); game = new Game();
game.setNextScreen(new GameScreen(graphics)); game.setNextScreen(new GameScreen(graphics));
@ -34,12 +33,7 @@ public class Asteroids extends ApplicationAdapter {
@Override @Override
public void resize(int width, int height) { public void resize(int width, int height) {
updateViewport(width, height); graphics.setScreenSize(width, height);
}
private void updateViewport(int width, int height) {
Constants.updateDimensions(width, height);
graphics.updateViewport(Constants.WIDTH, Constants.HEIGHT, Constants.getUIWidth(), Constants.getUIHeight());
} }
} }

View File

@ -1,44 +0,0 @@
package com.me.asteroids;
import com.me.asteroids.components.AccelerationComponent;
import com.me.asteroids.components.AsteroidComponent;
import com.me.asteroids.components.BulletTagComponent;
import com.me.asteroids.components.ColliderTagComponent;
import com.me.asteroids.components.DebrisTagComponent;
import com.me.asteroids.components.DecayComponent;
import com.me.asteroids.components.GameDataComponent;
import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PlayerComponent;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent;
import com.me.common.ecs.ComponentMapper;
public class Components {
public static final ComponentMapper<AccelerationComponent> ACCELERATION;
public static final ComponentMapper<AsteroidComponent> ASTEROID;
public static final ComponentMapper<BulletTagComponent> BULLET;
public static final ComponentMapper<ColliderTagComponent> COLLIDER;
public static final ComponentMapper<DebrisTagComponent> DEBRIS;
public static final ComponentMapper<DecayComponent> DECAY;
public static final ComponentMapper<GameDataComponent> GAME_DATA;
public static final ComponentMapper<ModelComponent> MODEL;
public static final ComponentMapper<PlayerComponent> PLAYER;
public static final ComponentMapper<PositionComponent> POSITION;
public static final ComponentMapper<VelocityComponent> VELOCITY;
static {
ACCELERATION = ComponentMapper.getFor(AccelerationComponent.class);
ASTEROID = ComponentMapper.getFor(AsteroidComponent.class);
BULLET = ComponentMapper.getFor(BulletTagComponent.class);
COLLIDER = ComponentMapper.getFor(ColliderTagComponent.class);
DEBRIS = ComponentMapper.getFor(DebrisTagComponent.class);
DECAY = ComponentMapper.getFor(DecayComponent.class);
GAME_DATA = ComponentMapper.getFor(GameDataComponent.class);
MODEL = ComponentMapper.getFor(ModelComponent.class);
PLAYER = ComponentMapper.getFor(PlayerComponent.class);
POSITION = ComponentMapper.getFor(PositionComponent.class);
VELOCITY = ComponentMapper.getFor(VelocityComponent.class);
}
}

View File

@ -2,50 +2,19 @@ package com.me.asteroids;
import com.me.common.Random; import com.me.common.Random;
public final class Constants { public class Constants {
public static final boolean DEBUG = false; public static final boolean DEBUG = false;
public static final Random rand = new Random(); public static final Random rand = new Random();
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
public static final int HALF_WIDTH = WIDTH / 2;
public static final int HALF_HEIGHT = HEIGHT / 2;
public static final float ASTEROID_SPAWN_DELAY = 1f; public static final float ASTEROID_SPAWN_DELAY = 1f;
public static final int ASTEROID_SPAWN_COUNT = 4; public static final int ASTEROID_SPAWN_COUNT = 4;
public static final int NEW_LIFE_SCORE = 10000;
public static int SCREEN_WIDTH;
public static int SCREEN_HEIGHT;
public static final float HEIGHT = 10f;
public static float WIDTH;
public static final float HALF_HEIGHT = HEIGHT / 2;
public static float HALF_WIDTH;
public static float getUIWidth() {
return SCREEN_WIDTH * getUIScale();
}
public static float getUIHeight() {
return SCREEN_HEIGHT * getUIScale();
}
public static float getHalfUIWidth() {
return getUIWidth() / 2;
}
public static float getHalfUIHeight() {
return getUIHeight() / 2;
}
public static float getUIScale() {
return 1.5f / (Math.min(SCREEN_HEIGHT, SCREEN_WIDTH) / 400f);
}
public static void updateDimensions(int screenWidth, int screenHeight) {
SCREEN_WIDTH = screenWidth;
SCREEN_HEIGHT = screenHeight;
WIDTH = (HEIGHT / SCREEN_HEIGHT) * SCREEN_WIDTH;
HALF_WIDTH = WIDTH / 2;
}
} }

View File

@ -5,9 +5,9 @@ import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.me.asteroids.components.AccelerationComponent; import com.me.asteroids.components.AccelerationComponent;
import com.me.asteroids.components.AsteroidComponent; import com.me.asteroids.components.AsteroidComponent;
import com.me.asteroids.components.BulletTagComponent; import com.me.asteroids.components.BulletComponent;
import com.me.asteroids.components.ColliderTagComponent; import com.me.asteroids.components.ColliderComponent;
import com.me.asteroids.components.DebrisTagComponent; import com.me.asteroids.components.DebrisComponent;
import com.me.asteroids.components.DecayComponent; import com.me.asteroids.components.DecayComponent;
import com.me.asteroids.components.ModelComponent; import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PlayerComponent; import com.me.asteroids.components.PlayerComponent;
@ -21,10 +21,6 @@ import com.me.common.ecs.Entity;
import java.util.Arrays; import java.util.Arrays;
import static com.me.asteroids.Components.ASTEROID;
import static com.me.asteroids.Components.MODEL;
import static com.me.asteroids.Components.POSITION;
import static com.me.asteroids.Components.VELOCITY;
import static com.me.asteroids.Constants.rand; import static com.me.asteroids.Constants.rand;
public class EntityFactory { public class EntityFactory {
@ -34,9 +30,7 @@ public class EntityFactory {
private static final Vector2 tmpA = new Vector2(); private static final Vector2 tmpA = new Vector2();
private static final Vector2 tmpB = new Vector2(); private static final Vector2 tmpB = new Vector2();
private static final ColliderTagComponent COLLIDER_TAG = new ColliderTagComponent(); private static final ColliderComponent COLLIDER = new ColliderComponent();
private static final DebrisTagComponent DEBRIS_TAG = new DebrisTagComponent();
private static final BulletTagComponent BULLET_TAG = new BulletTagComponent();
private static Entity createEntity(Engine engine) { private static Entity createEntity(Engine engine) {
Entity entity = engine.createEntity(); Entity entity = engine.createEntity();
@ -47,39 +41,35 @@ public class EntityFactory {
} }
public static Entity createPlayer(Engine engine) { public static Entity createPlayer(Engine engine) {
VelocityComponent velocity = new VelocityComponent(0f, 0f, 0f); PositionComponent position = new PositionComponent();
position.position = new Vector2(Constants.HALF_WIDTH, Constants.HALF_HEIGHT);
position.rotation = 90;
ModelComponent playerModel = new ModelComponent(new PolygonModel(Color.WHITE)); VelocityComponent velocity = new VelocityComponent();
playerModel.setVertices(new float[]{ velocity.velocity = new Vector2(0f, 0f);
0f, 0.5f, // tip velocity.maxVelocity = 400f;
-5/16f, -0.5f, // bottom left
-1/8f, -5/16f, // indent
1/8f, -5/16f, // indent
5/16f, -0.5f, // bottom right
});
ModelComponent afterburnerModel = new ModelComponent(new LineModel(Color.CYAN)); ModelComponent model = new ModelComponent();
afterburnerModel.setVertices(new float[]{ model.model = new PolygonModel(Color.WHITE);
-2/16f, -5/16f, model.model.setVertices(new float[]{
0f, -0.8f, 0f, 4f, // tip
2/16f, -5/16f -2.5f, -4f, // bottom left
-1f, -2.5f, // indent
1f, -2.5f, // indent
2.5f, -4f, // bottom right
}); });
model.model.setScale(5);
AccelerationComponent accel = new AccelerationComponent();
accel.acceleration = new Vector2(0, 1f);
Entity player = createEntity(engine); Entity player = createEntity(engine);
Entity afterBurner = createEntity(engine); player.addComponent(position);
player.addComponent(new PositionComponent(Constants.HALF_WIDTH, Constants.HALF_HEIGHT, 90));
player.addComponent(velocity); player.addComponent(velocity);
player.addComponent(playerModel); player.addComponent(model);
player.addComponent(new AccelerationComponent(10f)); player.addComponent(accel);
player.addComponent(COLLIDER_TAG); player.addComponent(COLLIDER);
player.addComponent(new PlayerComponent(afterBurner)); player.addComponent(new PlayerComponent());
player.setTag("PLAYER");
afterBurner.addComponent(new PositionComponent(Constants.HALF_WIDTH, Constants.HALF_HEIGHT, 90));
afterBurner.addComponent(velocity);
afterBurner.addComponent(afterburnerModel);
return player; return player;
} }
@ -113,31 +103,35 @@ public class EntityFactory {
} }
public static Entity[] createDebris(Engine engine, Entity entity) { public static Entity[] createDebris(Engine engine, Entity entity) {
Vector2 playerVelocity = VELOCITY.get(entity).velocity; Vector2 playerVelocity = entity.getComponent(VelocityComponent.class).velocity;
PositionComponent playerPosition = POSITION.get(entity); PositionComponent playerPosition = entity.getComponent(PositionComponent.class);
LineModel[] models = getLineModels(MODEL.get(entity).model); LineModel[] models = getLineModels(entity.getComponent(ModelComponent.class).model);
Vector2 explosionCenter = tmp.set(playerPosition.position).sub(Utils.setUnitVectorAngle(tmp2, playerPosition.rotation).scl(5));
Vector2 explosionCenter = tmp.set(playerPosition.position).sub(Utils.setUnitVectorAngle(tmp2, playerPosition.rotation).scl(0.125f));
Entity[] entities = new Entity[models.length]; Entity[] entities = new Entity[models.length];
for (int i = 0, n = models.length; i < n; i++) { for (int i = 0, n = models.length; i < n; i++) {
ModelComponent model = new ModelComponent(models[i]); ModelComponent model = new ModelComponent();
model.model = models[i];
PositionComponent position = new PositionComponent();
position.position = new Vector2(model.model.getPosition());
position.rotation = 90;
VelocityComponent velocity = new VelocityComponent(); VelocityComponent velocity = new VelocityComponent();
velocity.set(new Vector2(models[i].getPosition()) velocity.velocity = new Vector2(models[i].getPosition())
.sub(explosionCenter) .sub(explosionCenter)
.nor() // Direction from explosion center to center of piece .nor() // Direction from explosion center to center of piece
.rotate(rand.nextFloat(-15, 15)) // Slightly alter the direction each piece flies off in .rotate(rand.nextFloat(-15, 15)) // Slightly alter the direction each piece flies off in
.scl(rand.nextFloat(2f, 2.5f)) // Give each piece a slightly different speed .scl(rand.nextFloat(75, 100)) // Give each piece a slightly different speed
.add(tmp2.set(playerVelocity).scl(0.75f))); // Maintain 75% of the player's velocity at impact .add(tmp2.set(playerVelocity).scl(0.75f)); // Maintain 75% of the player's velocity at impact
velocity.angularVelocity = rand.nextFloat(-60, 60); // Make each piece spin at a different rate velocity.angularVelocity = rand.nextFloat(-60, 60); // Make each piece spin at a different rate
Entity debris = createEntity(engine); Entity debris = createEntity(engine);
debris.addComponent(new PositionComponent(new Vector2(model.getPosition()), 90)); debris.addComponent(position);
debris.addComponent(velocity); debris.addComponent(velocity);
debris.addComponent(model); debris.addComponent(model);
debris.addComponent(DEBRIS_TAG);
debris.addComponent(new DecayComponent(rand.nextFloat(0.5f, 2.5f))); debris.addComponent(new DecayComponent(rand.nextFloat(0.5f, 2.5f)));
debris.addComponent(new DebrisComponent());
entities[i] = debris; entities[i] = debris;
} }
return entities; return entities;
@ -145,37 +139,49 @@ public class EntityFactory {
public static Entity createBullet(Engine engine, Entity player) { public static Entity createBullet(Engine engine, Entity player) {
float[] modelVertices = MODEL.get(player).model.getVertices(); float[] modelVertices = player.getComponent(ModelComponent.class).model.getVertices();
float rotation = POSITION.get(player).rotation; float rotation = player.getComponent(PositionComponent.class).rotation;
PositionComponent position = new PositionComponent(modelVertices[0], modelVertices[1], rotation);
ModelComponent model = new ModelComponent(new PolygonModel(Color.YELLOW));
model.setVertices(new float[]{
1/40f, 0f,
-1/40f, 0f,
-1/40f, -4/40f,
1/40f, -4/40f,
});
model.setRotation(position.rotation);
model.setPosition(position.position);
Vector2 direction = Utils.setUnitVectorAngle(tmp, rotation); Vector2 direction = Utils.setUnitVectorAngle(tmp, rotation);
VelocityComponent velocity = new VelocityComponent();
velocity.velocity = new Vector2(direction).scl(500);
PositionComponent position = new PositionComponent();
position.position = new Vector2(modelVertices[0], modelVertices[1]).add(direction.scl(4));
position.rotation = rotation;
ModelComponent model = new ModelComponent();
model.model = new PolygonModel(Color.YELLOW);
model.model.setVertices(new float[]{
1f, 0f,
-1f, 0f,
-1f, -4f,
1f, -4f,
});
model.model.setRotation(position.rotation);
model.model.setPosition(position.position);
Entity bullet = createEntity(engine); Entity bullet = createEntity(engine);
bullet.addComponent(position); bullet.addComponent(position);
bullet.addComponent(new VelocityComponent(new Vector2(direction).scl(12.5f))); bullet.addComponent(velocity);
bullet.addComponent(model); bullet.addComponent(model);
bullet.addComponent(COLLIDER_TAG); bullet.addComponent(COLLIDER);
bullet.addComponent(BULLET_TAG); bullet.addComponent(new BulletComponent());
return bullet; return bullet;
} }
public static Entity createAsteroid(Engine engine) { public static Entity createAsteroid(Engine engine) {
// Creates an asteroid entity with position and velocity unset - the AsteroidSpawningSystem // Creates an asteroid entity with position and velocity unset - the AsteroidSpawningSystem
// is responsible for setting its position and velocity // is responsible for setting its position and velocity
ModelComponent model = new ModelComponent(new PolygonModel(Color.WHITE)); PositionComponent position = new PositionComponent();
float size = rand.nextFloat(1f, 1.75f); VelocityComponent velocity = new VelocityComponent();
model.setVertices(new AsteroidFactory()
ModelComponent model = new ModelComponent();
model.model = new PolygonModel(Color.WHITE);
int size = rand.nextInt(45, 75);
model.model.setVertices(new AsteroidFactory()
.setVertexCount(32) .setVertexCount(32)
.setSize(size) .setSize(size)
.setSizeVariation(size * 0.5f) .setSizeVariation(size * 0.5f)
@ -183,12 +189,11 @@ public class EntityFactory {
.generate()); .generate());
Entity asteroid = createEntity(engine); Entity asteroid = createEntity(engine);
asteroid.addComponent(new PositionComponent()); asteroid.addComponent(position);
asteroid.addComponent(new VelocityComponent()); asteroid.addComponent(velocity);
asteroid.addComponent(model); asteroid.addComponent(model);
asteroid.addComponent(COLLIDER_TAG); asteroid.addComponent(COLLIDER);
asteroid.addComponent(new AsteroidComponent()); asteroid.addComponent(new AsteroidComponent());
asteroid.addGroup("ASTEROIDS");
return asteroid; return asteroid;
} }
@ -202,8 +207,8 @@ public class EntityFactory {
} }
public static Entity[] splitAsteroidIntoChunks(Engine engine, Entity asteroid, int chunkCount, float chunkScale) { public static Entity[] splitAsteroidIntoChunks(Engine engine, Entity asteroid, int chunkCount, float chunkScale) {
Vector2 asteroidVelocity = VELOCITY.get(asteroid).velocity; Vector2 asteroidVelocity = asteroid.getComponent(VelocityComponent.class).velocity;
ModelComponent asteroidModel = MODEL.get(asteroid); Model asteroidModel = asteroid.getComponent(ModelComponent.class).model;
Vector2 asteroidPosition = asteroidModel.getPosition(); Vector2 asteroidPosition = asteroidModel.getPosition();
float[] scaledVertices = scaleAndRelativizeVertices(asteroidPosition, asteroidModel.getVertices(), chunkScale); float[] scaledVertices = scaleAndRelativizeVertices(asteroidPosition, asteroidModel.getVertices(), chunkScale);
@ -213,31 +218,123 @@ public class EntityFactory {
Entity[] entities = new Entity[chunkCount]; Entity[] entities = new Entity[chunkCount];
for (int i = 0; i < chunkCount; i++) { for (int i = 0; i < chunkCount; i++) {
Vector2 chunkPosition = tmp2.set(asteroidPosition).add(tmp.scl(0.5f)); Vector2 chunkPosition = tmp2.set(asteroidPosition).add(tmp.scl(25));
ModelComponent model = new ModelComponent(new PolygonModel(asteroidModel.getColor())); ModelComponent model = new ModelComponent();
model.setVertices(Arrays.copyOf(scaledVertices, scaledVertices.length)); model.model = new PolygonModel(asteroidModel.getColor());
model.setPosition(chunkPosition); model.model.setVertices(Arrays.copyOf(scaledVertices, scaledVertices.length));
model.model.setPosition(chunkPosition);
PositionComponent position = new PositionComponent();
position.position = new Vector2(chunkPosition);
position.rotation = 90;
VelocityComponent velocity = new VelocityComponent(); VelocityComponent velocity = new VelocityComponent();
velocity.set(tmp); velocity.velocity = new Vector2(tmp).nor().rotate(rand.nextFloat(-45, 45)).scl(asteroidVelocity.len() * 1.10f);
velocity.velocity.nor().rotate(rand.nextFloat(-45, 45)).scl(asteroidVelocity.len() * 1.10f);
velocity.angularVelocity = rand.nextFloat(-30, 30); velocity.angularVelocity = rand.nextFloat(-30, 30);
AsteroidComponent asteroidComponent = new AsteroidComponent();
asteroidComponent.generation = asteroid.getComponent(AsteroidComponent.class).generation + 1;
angle += angleStep; angle += angleStep;
Utils.setUnitVectorAngleRad(tmp, angle); Utils.setUnitVectorAngleRad(tmp, angle);
Entity split = createEntity(engine); Entity split = createEntity(engine);
split.addComponent(model); split.addComponent(model);
split.addComponent(new PositionComponent(new Vector2(chunkPosition), 90)); split.addComponent(position);
split.addComponent(velocity); split.addComponent(velocity);
split.addComponent(new AsteroidComponent(ASTEROID.get(asteroid).generation + 1)); split.addComponent(asteroidComponent);
split.addComponent(COLLIDER_TAG); split.addComponent(COLLIDER);
split.addGroup("ASTEROIDS");
entities[i] = split; entities[i] = split;
} }
return entities; return entities;
} }
public static PolygonModel[] getPolygonShards(PolygonModel model, int shardCount) {
PolygonModel[] shards = new PolygonModel[shardCount];
Vector2 position = tmp.set(model.getPosition());
float[] vertices = model.getVertices();
int verticesPerShard = vertices.length / (shardCount * 2);
int remaining = vertices.length % (shardCount * 2);
int vertice = 0;
for (int i = 0; i < shardCount; i++) {
if (i + 1 == shardCount && remaining > 0) {
// Put the remaining vertices onto the last shard
verticesPerShard += remaining / 2;
}
float[] shardVertices = new float[(verticesPerShard + 1) * 2];
shardVertices[0] = 0;
shardVertices[1] = 0;
float minX = shardVertices[0], maxX = shardVertices[0];
float minY = shardVertices[1], maxY = shardVertices[1];
for (int j = 2, n = shardVertices.length; j < n; j += 2) {
float x = shardVertices[j] = vertices[vertice++] - position.x;
float y = shardVertices[j + 1] = vertices[vertice++] - position.y;
minX = x < minX ? x : minX;
maxX = x > maxX ? x : maxX;
minY = y < minY ? y : minY;
maxY = y > maxY ? y : maxY;
}
float centerX = minX + ((maxX - minX) / 2);
float centerY = minY + ((maxY - minY) / 2);
for (int j = 0, n = shardVertices.length; j < n; j += 2) {
shardVertices[j] -= centerX;
shardVertices[j + 1] -= centerY;
}
PolygonModel shard = new PolygonModel(model.getColor());
shard.setVertices(shardVertices);
shard.setPosition(tmp2.set(position).add(centerX, centerY));
shards[i] = shard;
}
return shards;
}
public static Entity[] splitAsteroidIntoShards(Engine engine, Entity asteroid, int shardCount) {
Entity[] entities = new Entity[shardCount];
PolygonModel asteroidModel = (PolygonModel) asteroid.getComponent(ModelComponent.class).model;
PolygonModel[] shards = getPolygonShards(asteroidModel, shardCount);
Vector2 asteroidPosition = asteroidModel.getPosition();
Vector2 asteroidVelocity = asteroid.getComponent(VelocityComponent.class).velocity;
int generation = asteroid.getComponent(AsteroidComponent.class).generation;
for (int i = 0; i < entities.length; i++) {
ModelComponent model = new ModelComponent();
model.model = shards[i];
PositionComponent position = new PositionComponent();
position.position = new Vector2(model.model.getPosition());
position.rotation = 90;
VelocityComponent velocity = new VelocityComponent();
velocity.velocity = new Vector2(shards[i].getPosition())
.sub(asteroidPosition)
.nor()
.rotate(rand.nextFloat(-15, 15)) // Slightly alter the direction each piece flies off in
.scl(asteroidVelocity.len() * 1f) // Set speed to asteroid's original velocity
.add(tmp2.set(asteroidVelocity).scl(0.25f));
velocity.angularVelocity = rand.nextFloat(-30, 30);
AsteroidComponent asteroidComponent = new AsteroidComponent();
asteroidComponent.generation = generation + 1;
Entity entity = createEntity(engine);
entity.addComponent(model);
entity.addComponent(position);
entity.addComponent(velocity);
entity.addComponent(asteroidComponent);
entity.addComponent(COLLIDER);
entities[i] = entity;
}
return entities;
}
} }

View File

@ -1,23 +1,22 @@
package com.me.asteroids; package com.me.asteroids;
import com.me.asteroids.components.AsteroidComponent;
import com.me.asteroids.components.BulletComponent;
import com.me.asteroids.components.PlayerComponent;
import com.me.asteroids.events.BulletAsteroidCollisionEvent; import com.me.asteroids.events.BulletAsteroidCollisionEvent;
import com.me.asteroids.events.CollisionEvent; import com.me.asteroids.events.CollisionEvent;
import com.me.asteroids.events.PlayerASteroidCollisionEvent; import com.me.asteroids.events.PlayerASteroidCollisionEvent;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import static com.me.asteroids.Components.ASTEROID;
import static com.me.asteroids.Components.BULLET;
import static com.me.asteroids.Components.PLAYER;
public class EventFactory { public class EventFactory {
public static CollisionEvent getNewCollisionEvent(Entity a, Entity b) { public static CollisionEvent getNewCollisionEvent(Entity a, Entity b) {
boolean isEntityAPlayer = PLAYER.has(a); boolean isEntityAPlayer = a.hasComponent(PlayerComponent.class);
boolean isEntityBPlayer = PLAYER.has(b); boolean isEntityBPlayer = b.hasComponent(PlayerComponent.class);
boolean isEntityABullet = !isEntityAPlayer && BULLET.has(a); boolean isEntityABullet = !isEntityAPlayer && a.hasComponent(BulletComponent.class);
boolean isEntityBBullet = !isEntityBPlayer && BULLET.has(b); boolean isEntityBBullet = !isEntityBPlayer && b.hasComponent(BulletComponent.class);
boolean isEntityAAsteroid = !isEntityAPlayer && !isEntityABullet && ASTEROID.has(a); boolean isEntityAAsteroid = !isEntityAPlayer && !isEntityABullet && a.hasComponent(AsteroidComponent.class);
boolean isEntityBAsteroid = !isEntityBPlayer && !isEntityBBullet && ASTEROID.has(b); boolean isEntityBAsteroid = !isEntityBPlayer && !isEntityBBullet && b.hasComponent(AsteroidComponent.class);
if (isEntityAAsteroid || isEntityBAsteroid) { if (isEntityAAsteroid || isEntityBAsteroid) {
if (isEntityAPlayer && isEntityBAsteroid) { if (isEntityAPlayer && isEntityBAsteroid) {

View File

@ -1,24 +0,0 @@
package com.me.asteroids;
public enum GameMode {
PLAYING,
DIED(3f),
GAME_OVER(4f),
;
private final float modeTimer;
GameMode() {
this(0f);
}
GameMode(float modeTimer) {
this.modeTimer = modeTimer;
}
public float getTimer() {
return modeTimer;
}
}

View File

@ -1,62 +1,63 @@
package com.me.asteroids; package com.me.asteroids;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
public class Graphics { public class Graphics {
private OrthographicCamera camera; private int worldWidth, worldHeight;
private OrthographicCamera uiCamera; private int screenWidth, screenHeight;
private Camera camera;
private Viewport viewport;
private ShapeRenderer shapeRenderer; private ShapeRenderer shapeRenderer;
private SpriteBatch spriteBatch;
private SpriteBatch uiSpriteBatch;
public Graphics() { public Graphics(int worldWidth, int worldHeight) {
this.worldWidth = worldWidth;
this.worldHeight = worldHeight;
this.screenWidth = Gdx.graphics.getWidth();
this.screenHeight = Gdx.graphics.getHeight();
this.camera = new OrthographicCamera(); this.camera = new OrthographicCamera();
this.uiCamera = new OrthographicCamera(); this.viewport = new FitViewport(worldWidth, worldHeight, camera);
this.shapeRenderer = new ShapeRenderer(); this.shapeRenderer = new ShapeRenderer();
this.spriteBatch = new SpriteBatch();
this.uiSpriteBatch = new SpriteBatch();
} }
public void initialize() { public void initialize() {
Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClearColor(0, 0, 0, 1);
updateDimensions();
} }
public void reset() { public void reset() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
} }
public void setScreenSize(int width, int height) {
screenWidth = width;
screenHeight = height;
updateDimensions();
}
public void dispose() { public void dispose() {
shapeRenderer.dispose(); shapeRenderer.dispose();
spriteBatch.dispose();
uiSpriteBatch.dispose();
} }
public ShapeRenderer getShapeRenderer() { public ShapeRenderer getShapeRenderer() {
return shapeRenderer; return shapeRenderer;
} }
public SpriteBatch getSpriteBatch() { private void updateDimensions() {
return spriteBatch; viewport.setWorldSize(worldWidth, worldHeight);
} viewport.update(screenWidth, screenHeight, true);
public SpriteBatch getUISpriteBatch() {
return uiSpriteBatch;
}
public void updateViewport(float worldWidth, float worldHeight, float uiWidth, float uiHeight) {
camera.setToOrtho(false, worldWidth, worldHeight);
uiCamera.setToOrtho(false, uiWidth, uiHeight);
shapeRenderer.setProjectionMatrix(camera.combined); shapeRenderer.setProjectionMatrix(camera.combined);
spriteBatch.setProjectionMatrix(camera.combined);
uiSpriteBatch.setProjectionMatrix(uiCamera.combined);
} }
} }

View File

@ -6,48 +6,5 @@ import com.me.common.ecs.Component;
public class AccelerationComponent implements Component { public class AccelerationComponent implements Component {
public Vector2 acceleration; public Vector2 acceleration;
public float maxVelocity;
public AccelerationComponent() {
this(0f, 0f, Float.MAX_VALUE);
}
public AccelerationComponent(float maxVelocity) {
this(0f, 0f, maxVelocity);
}
public AccelerationComponent(float x, float y, float maxVelocity) {
this(new Vector2(x, y), maxVelocity);
}
public AccelerationComponent(Vector2 acceleration, float maxVelocity) {
this.acceleration = acceleration;
this.maxVelocity = maxVelocity;
}
public float getX() {
return acceleration.x;
}
public float getY() {
return acceleration.y;
}
public void setX(float x) {
acceleration.x = x;
}
public void setY(float y) {
acceleration.y = y;
}
public void set(Vector2 vector) {
set(vector.x, vector.y);
}
public void set(float x, float y) {
acceleration.x = x;
acceleration.y = y;
}
} }

View File

@ -6,12 +6,4 @@ public class AsteroidComponent implements Component {
public int generation; public int generation;
public AsteroidComponent() {
this(0);
}
public AsteroidComponent(int generation) {
this.generation = generation;
}
} }

View File

@ -2,6 +2,6 @@ package com.me.asteroids.components;
import com.me.common.ecs.Component; import com.me.common.ecs.Component;
public class BulletTagComponent implements Component { public class BulletComponent implements Component {
// TODO: See PlayerComponent's TODO // TODO: See PlayerComponent's TODO
} }

View File

@ -5,5 +5,5 @@ import com.me.common.ecs.Component;
/** /**
* Add this to Entities that should be checked for collisions with other entities. * Add this to Entities that should be checked for collisions with other entities.
*/ */
public class ColliderTagComponent implements Component { public class ColliderComponent implements Component {
} }

View File

@ -2,6 +2,6 @@ package com.me.asteroids.components;
import com.me.common.ecs.Component; import com.me.common.ecs.Component;
public class DebrisTagComponent implements Component { public class DebrisComponent implements Component {
// TODO: See PlayerComponent's TODO // TODO: See PlayerComponent's TODO
} }

View File

@ -1,28 +0,0 @@
package com.me.asteroids.components;
import com.me.asteroids.GameMode;
import com.me.common.ecs.Component;
public class GameDataComponent implements Component {
public int score;
public int lives;
public int newLifeScore;
public GameMode gameMode;
public float gameModeTimer;
public GameDataComponent() {
reset();
}
public void reset() {
this.score = 0;
this.lives = 3;
this.newLifeScore = 0;
this.gameMode = GameMode.PLAYING;
this.gameModeTimer = 0f;
}
}

View File

@ -1,9 +1,5 @@
package com.me.asteroids.components; package com.me.asteroids.components;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.me.asteroids.components.model.Model; import com.me.asteroids.components.model.Model;
import com.me.common.ecs.Component; import com.me.common.ecs.Component;
@ -11,52 +7,4 @@ public class ModelComponent implements Component {
public Model model; public Model model;
public ModelComponent(Model model) {
this.model = model;
}
public Color getColor() {
return model.getColor();
}
public ModelComponent setVertices(float[] vertices) {
model.setVertices(vertices);
return this;
}
public float[] getVertices() {
return model.getVertices();
}
public ModelComponent setPosition(Vector2 position) {
model.setPosition(position);
return this;
}
public Vector2 getPosition() {
return model.getPosition();
}
public ModelComponent setRotation(float degrees) {
model.setRotation(degrees);
return this;
}
public float getRotation() {
return model.getRotation();
}
public ModelComponent setScale(float scale) {
model.setScale(scale);
return this;
}
public float getScale() {
return model.getScale();
}
public Rectangle getBoundingBox() {
return model.getBoundingBox();
}
} }

View File

@ -1,14 +1,7 @@
package com.me.asteroids.components; package com.me.asteroids.components;
import com.me.common.ecs.Component; import com.me.common.ecs.Component;
import com.me.common.ecs.Entity;
public class PlayerComponent implements Component { public class PlayerComponent implements Component {
// TODO: implement engine feature for tagging entities (as player, for e.g.) // TODO: implement engine feature for tagging entities (as player, for e.g.)
public Entity afterBurner;
public PlayerComponent(Entity afterBurner) {
this.afterBurner = afterBurner;
}
} }

View File

@ -8,42 +8,4 @@ public class PositionComponent implements Component {
public Vector2 position; public Vector2 position;
public float rotation; public float rotation;
public PositionComponent() {
this(0f, 0f, 0f);
}
public PositionComponent(float x, float y, float rotation) {
this(new Vector2(x, y), rotation);
}
public PositionComponent(Vector2 position, float rotation) {
this.position = position;
this.rotation = rotation;
}
public float getX() {
return position.x;
}
public float getY() {
return position.y;
}
public void setX(float x) {
position.x = x;
}
public void setY(float y) {
position.y = y;
}
public void set(Vector2 vector) {
set(vector.x, vector.y);
}
public void set(float x, float y) {
position.x = x;
position.y = y;
}
} }

View File

@ -8,50 +8,6 @@ public class VelocityComponent implements Component {
public Vector2 velocity; public Vector2 velocity;
public float angularVelocity; public float angularVelocity;
public VelocityComponent() { public float maxVelocity = Float.MAX_VALUE;
this(0f, 0f, 0f);
}
public VelocityComponent(Vector2 vector) {
this(vector, 0f);
}
public VelocityComponent(float x, float y) {
this(x, y, 0f);
}
public VelocityComponent(float x, float y, float angularVelocity) {
this(new Vector2(x, y), angularVelocity);
}
public VelocityComponent(Vector2 velocity, float angularVelocity) {
this.velocity = velocity;
this.angularVelocity = angularVelocity;
}
public float getX() {
return velocity.x;
}
public float getY() {
return velocity.y;
}
public void setX(float x) {
velocity.x = x;
}
public void setY(float y) {
velocity.y = y;
}
public void set(Vector2 vector) {
set(vector.x, vector.y);
}
public void set(float x, float y) {
velocity.x = x;
velocity.y = y;
}
} }

View File

@ -1,14 +0,0 @@
package com.me.asteroids.events;
import com.me.asteroids.components.AsteroidComponent;
import com.me.common.ecs.event.Event;
public class AsteroidHitEvent extends Event {
public AsteroidComponent asteroid;
public AsteroidHitEvent(AsteroidComponent asteroid) {
this.asteroid = asteroid;
}
}

View File

@ -1,14 +0,0 @@
package com.me.asteroids.events;
import com.me.common.ecs.Entity;
import com.me.common.ecs.event.Event;
public class PlayerDeathEvent extends Event {
public Entity player;
public PlayerDeathEvent(Entity player) {
this.player = player;
}
}

View File

@ -10,43 +10,32 @@ import com.me.asteroids.EntityFactory;
import com.me.asteroids.Graphics; import com.me.asteroids.Graphics;
import com.me.asteroids.components.AccelerationComponent; import com.me.asteroids.components.AccelerationComponent;
import com.me.asteroids.components.AsteroidComponent; import com.me.asteroids.components.AsteroidComponent;
import com.me.asteroids.components.BulletTagComponent; import com.me.asteroids.components.BulletComponent;
import com.me.asteroids.components.ColliderTagComponent; import com.me.asteroids.components.ColliderComponent;
import com.me.asteroids.components.DebrisTagComponent; import com.me.asteroids.components.DebrisComponent;
import com.me.asteroids.components.DecayComponent; import com.me.asteroids.components.DecayComponent;
import com.me.asteroids.components.GameDataComponent;
import com.me.asteroids.components.ModelComponent; import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PlayerComponent; import com.me.asteroids.components.PlayerComponent;
import com.me.asteroids.components.PositionComponent; import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent; import com.me.asteroids.components.VelocityComponent;
import com.me.asteroids.components.model.PolygonModel; import com.me.asteroids.components.model.PolygonModel;
import com.me.asteroids.events.AsteroidHitEvent;
import com.me.asteroids.events.BulletAsteroidCollisionEvent; import com.me.asteroids.events.BulletAsteroidCollisionEvent;
import com.me.asteroids.events.PlayerASteroidCollisionEvent; import com.me.asteroids.events.PlayerASteroidCollisionEvent;
import com.me.asteroids.events.PlayerDeathEvent;
import com.me.asteroids.events.ScreenWrapEvent; import com.me.asteroids.events.ScreenWrapEvent;
import com.me.asteroids.systems.AsteroidSpawningSystem; import com.me.asteroids.systems.AsteroidSpawningSystem;
import com.me.asteroids.systems.CollisionSystem; import com.me.asteroids.systems.CollisionSystem;
import com.me.asteroids.systems.DecaySystem; import com.me.asteroids.systems.DecaySystem;
import com.me.asteroids.systems.GameDataRenderSystem;
import com.me.asteroids.systems.GameDataSystem;
import com.me.asteroids.systems.ModelRenderSystem; import com.me.asteroids.systems.ModelRenderSystem;
import com.me.asteroids.systems.MovementSystem; import com.me.asteroids.systems.MovementSystem;
import com.me.asteroids.systems.PlayerInputSystem; import com.me.asteroids.systems.PlayerInputSystem;
import com.me.asteroids.systems.ScreenWrapSystem; import com.me.asteroids.systems.ScreenWrapSystem;
import com.me.common.Screen; import com.me.common.Screen;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.event.EventHandler; import com.me.common.ecs.event.EventHandler;
import com.me.common.ecs.event.Listener; import com.me.common.ecs.event.Listener;
import static com.me.asteroids.Components.ASTEROID;
import static com.me.asteroids.Components.BULLET;
import static com.me.asteroids.Components.DEBRIS;
import static com.me.asteroids.Components.MODEL;
import static com.me.asteroids.Components.PLAYER;
import static com.me.asteroids.Components.POSITION;
public class GameScreen extends Screen implements Listener { public class GameScreen extends Screen implements Listener {
Engine engine; Engine engine;
@ -62,27 +51,23 @@ public class GameScreen extends Screen implements Listener {
@Override @Override
public void setup() { public void setup() {
batch = graphics.getUISpriteBatch(); batch = new SpriteBatch();
font = new BitmapFont(Gdx.files.internal("Retro_Font.fnt")); font = new BitmapFont();
font.setColor(Color.RED);
engine = new Engine(); engine = new Engine();
engine.registerComponentClass(BulletTagComponent.class);
engine.registerComponentClass(ColliderTagComponent.class);
engine.registerComponentClass(DebrisTagComponent.class);
engine.registerComponentClass(AccelerationComponent.class);
engine.registerComponentClass(AsteroidComponent.class);
engine.registerComponentClass(DecayComponent.class);
engine.registerComponentClass(GameDataComponent.class);
engine.registerComponentClass(ModelComponent.class);
engine.registerComponentClass(PlayerComponent.class); engine.registerComponentClass(PlayerComponent.class);
engine.registerComponentClass(BulletComponent.class);
engine.registerComponentClass(AsteroidComponent.class);
engine.registerComponentClass(DebrisComponent.class);
engine.registerComponentClass(DecayComponent.class);
engine.registerComponentClass(ColliderComponent.class);
engine.registerComponentClass(PositionComponent.class); engine.registerComponentClass(PositionComponent.class);
engine.registerComponentClass(VelocityComponent.class); engine.registerComponentClass(VelocityComponent.class);
engine.registerComponentClass(AccelerationComponent.class);
engine.registerComponentClass(ModelComponent.class);
GameDataSystem system = new GameDataSystem(engine);
engine.registerSystem(system);
engine.registerSystem(new PlayerInputSystem(engine)); engine.registerSystem(new PlayerInputSystem(engine));
engine.registerSystem(new AsteroidSpawningSystem(engine)); engine.registerSystem(new AsteroidSpawningSystem(engine));
engine.registerSystem(new DecaySystem(engine)); engine.registerSystem(new DecaySystem(engine));
@ -90,14 +75,10 @@ public class GameScreen extends Screen implements Listener {
engine.registerSystem(new CollisionSystem(engine)); engine.registerSystem(new CollisionSystem(engine));
engine.registerSystem(new ScreenWrapSystem(engine)); engine.registerSystem(new ScreenWrapSystem(engine));
engine.registerSystem(new ModelRenderSystem(engine, graphics)); engine.registerSystem(new ModelRenderSystem(engine, graphics));
engine.registerSystem(new GameDataRenderSystem(engine, graphics, font));
engine.registerListener(system); engine.registerListener(this.new EventListener(engine));
engine.registerListener(this.new EventListener());
Entity gameData = engine.createEntity(); engine.ready();
gameData.addComponent(new GameDataComponent());
gameData.activate();
Entity player = EntityFactory.createPlayer(engine); Entity player = EntityFactory.createPlayer(engine);
player.activate(); player.activate();
@ -110,22 +91,35 @@ public class GameScreen extends Screen implements Listener {
if (Constants.DEBUG) { if (Constants.DEBUG) {
batch.begin(); batch.begin();
font.setColor(Color.RED); font.draw(batch, String.format("FPS: %d, Entities: %d", Gdx.graphics.getFramesPerSecond(), engine.getEntityCount()), 50, 50);
font.draw(batch, String.format("FPS: %d, Entities: %d", Gdx.graphics.getFramesPerSecond(), engine.getEntities().size), 15, 15 + font.getCapHeight());
batch.end(); batch.end();
} }
} }
@Override @Override
public void dispose() { public void dispose() {
font.dispose();
} }
private class EventListener implements Listener { private class EventListener implements Listener {
ComponentMapper<PositionComponent> positionMapper;
ComponentMapper<ModelComponent> modelMapper;
ComponentMapper<BulletComponent> bulletMapper;
ComponentMapper<DebrisComponent> debrisMapper;
ComponentMapper<AsteroidComponent> asteroidMapper;
EventListener(Engine engine) {
this.positionMapper = engine.getComponentMapper(PositionComponent.class);
this.modelMapper = engine.getComponentMapper(ModelComponent.class);
this.bulletMapper = engine.getComponentMapper(BulletComponent.class);
this.debrisMapper = engine.getComponentMapper(DebrisComponent.class);
this.asteroidMapper = engine.getComponentMapper(AsteroidComponent.class);
}
@EventHandler @EventHandler
public void onScreenWrap(ScreenWrapEvent event) { public void onScreenWrap(ScreenWrapEvent event) {
if (BULLET.has(event.entity) || DEBRIS.has(event.entity)) { if (bulletMapper.has(event.entity) || debrisMapper.has(event.entity)) {
// Remove bullets when they leave the screen // Remove bullets when they leave the screen
event.setCancelled(true); event.setCancelled(true);
event.entity.remove(); event.entity.remove();
@ -134,16 +128,15 @@ public class GameScreen extends Screen implements Listener {
@EventHandler @EventHandler
public void onBulletAsteroidCollision(BulletAsteroidCollisionEvent event) { public void onBulletAsteroidCollision(BulletAsteroidCollisionEvent event) {
Vector2 bulletPosition = POSITION.get(event.getBullet()).position; Vector2 bulletPosition = positionMapper.get(event.getBullet()).position;
PolygonModel model = (PolygonModel) MODEL.get(event.getAsteroid()).model; PolygonModel model = (PolygonModel) modelMapper.get(event.getAsteroid()).model;
if (model.contains(bulletPosition)) { if (model.contains(bulletPosition)) {
// AABBs intersect but let's only consider it a hit if the bullet's position // AABBs intersect but let's only consider it a hit if the bullet's position
// is actually inside the asteroid // is actually inside the asteroid
AsteroidComponent asteroid = ASTEROID.get(event.getAsteroid());
engine.callEvent(new AsteroidHitEvent(asteroid));
event.getBullet().remove(); event.getBullet().remove();
if (asteroid.generation < 2) { int generation = asteroidMapper.get(event.getAsteroid()).generation;
if (generation < 2) {
for (Entity shard : EntityFactory.splitAsteroidIntoChunks(engine, event.getAsteroid(), 2, 2/3f)) { for (Entity shard : EntityFactory.splitAsteroidIntoChunks(engine, event.getAsteroid(), 2, 2/3f)) {
shard.activate(); shard.activate();
} }
@ -153,17 +146,16 @@ public class GameScreen extends Screen implements Listener {
} }
} }
event.getAsteroid().remove(); event.getAsteroid().remove();
} }
} }
@EventHandler @EventHandler
public void onPlayerAsteroidCollision(PlayerASteroidCollisionEvent event) { public void onPlayerAsteroidCollision(PlayerASteroidCollisionEvent event) {
PolygonModel asteroid = (PolygonModel) MODEL.get(event.getAsteroid()).model; PolygonModel asteroid = (PolygonModel) modelMapper.get(event.getAsteroid()).model;
PolygonModel player = (PolygonModel) MODEL.get(event.getPlayer()).model; PolygonModel player = (PolygonModel) modelMapper.get(event.getPlayer()).model;
if (asteroid.contains(player.getVertices()) || player.contains(asteroid.getVertices())) { if (asteroid.contains(player.getVertices()) || player.contains(asteroid.getVertices())) {
engine.callEvent(new PlayerDeathEvent(event.getPlayer()));
PLAYER.get(event.getPlayer()).afterBurner.deactivate();
event.getPlayer().deactivate(); event.getPlayer().deactivate();
for (Entity debris : EntityFactory.createDebris(engine, event.getPlayer())) { for (Entity debris : EntityFactory.createDebris(engine, event.getPlayer())) {
debris.activate(); debris.activate();

View File

@ -2,26 +2,37 @@ package com.me.asteroids.systems;
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.me.asteroids.Constants; import com.me.asteroids.Constants;
import com.me.asteroids.EntityFactory; import com.me.asteroids.EntityFactory;
import com.me.asteroids.Utils; import com.me.asteroids.Utils;
import com.me.asteroids.components.AsteroidComponent;
import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent; import com.me.asteroids.components.VelocityComponent;
import com.me.asteroids.components.model.Model; import com.me.asteroids.components.model.Model;
import com.me.common.ecs.BaseSystem; import com.me.common.ecs.BaseSystem;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import static com.me.asteroids.Components.MODEL;
import static com.me.asteroids.Components.POSITION;
import static com.me.asteroids.Components.VELOCITY;
import static com.me.asteroids.Constants.rand; import static com.me.asteroids.Constants.rand;
public class AsteroidSpawningSystem extends BaseSystem { public class AsteroidSpawningSystem extends BaseSystem {
private ComponentMapper<AsteroidComponent> asteroidMapper;
private ComponentMapper<PositionComponent> positionMapper;
private ComponentMapper<VelocityComponent> velocityMapper;
private ComponentMapper<ModelComponent> modelMapper;
private float asteroidSpawnDelay = 0f; private float asteroidSpawnDelay = 0f;
public AsteroidSpawningSystem(Engine engine) { public AsteroidSpawningSystem(Engine engine) {
super(engine); super(engine);
asteroidMapper = engine.getComponentMapper(AsteroidComponent.class);
positionMapper = engine.getComponentMapper(PositionComponent.class);
velocityMapper = engine.getComponentMapper(VelocityComponent.class);
modelMapper = engine.getComponentMapper(ModelComponent.class);
} }
private Vector2 getRandomSpawnLocation(float asteroidWidth, float asteroidHeight) { private Vector2 getRandomSpawnLocation(float asteroidWidth, float asteroidHeight) {
@ -44,16 +55,16 @@ public class AsteroidSpawningSystem extends BaseSystem {
private void spawnAsteroid() { private void spawnAsteroid() {
Entity asteroid = EntityFactory.createAsteroid(engine); Entity asteroid = EntityFactory.createAsteroid(engine);
Model model = MODEL.get(asteroid).model; Model model = modelMapper.get(asteroid).model;
Rectangle aabb = model.getBoundingBox(); Rectangle aabb = model.getBoundingBox();
Vector2 position Vector2 position
= POSITION.get(asteroid).position = positionMapper.get(asteroid).position
= getRandomSpawnLocation(aabb.getWidth(), aabb.getHeight()); = getRandomSpawnLocation(aabb.getWidth(), aabb.getHeight());
model.setPosition(position); model.setPosition(position);
VelocityComponent velocityComponent = VELOCITY.get(asteroid); VelocityComponent velocityComponent = velocityMapper.get(asteroid);
velocityComponent.velocity = new Vector2().setToRandomDirection().scl(rand.nextFloat(1.875f, 3.125f)); velocityComponent.velocity = new Vector2().setToRandomDirection().scl(rand.nextFloat(75, 125));
velocityComponent.angularVelocity = rand.nextFloat(-30, 30); velocityComponent.angularVelocity = rand.nextFloat(-30, 30);
asteroid.activate(); asteroid.activate();
@ -61,7 +72,16 @@ public class AsteroidSpawningSystem extends BaseSystem {
@Override @Override
public void process(float dt) { public void process(float dt) {
int asteroidCount = engine.getEntitiesInGroup("ASTEROIDS").size; Array<Entity> entities = engine.getEntities();
int asteroidCount = 0;
for (Entity entity : entities) {
// It's rather inefficient to have to check our entire entity list every frame
// to count how many entities have a specific component. Maybe we should keep a count of
// how many entites a given component?
if (asteroidMapper.has(entity)) {
asteroidCount++;
}
}
if (asteroidCount++ < Constants.ASTEROID_SPAWN_COUNT && (asteroidSpawnDelay -= dt) <= 0) { if (asteroidCount++ < Constants.ASTEROID_SPAWN_COUNT && (asteroidSpawnDelay -= dt) <= 0) {
spawnAsteroid(); spawnAsteroid();

View File

@ -2,18 +2,20 @@ package com.me.asteroids.systems;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.me.asteroids.EventFactory; import com.me.asteroids.EventFactory;
import com.me.asteroids.components.ColliderTagComponent; import com.me.asteroids.components.ColliderComponent;
import com.me.asteroids.components.ModelComponent; import com.me.asteroids.components.ModelComponent;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem; import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Components.MODEL;
public class CollisionSystem extends EntitySystem { public class CollisionSystem extends EntitySystem {
private ComponentMapper<ModelComponent> modelMapper;
public CollisionSystem(Engine engine) { public CollisionSystem(Engine engine) {
super(engine, ColliderTagComponent.class, ModelComponent.class); super(engine, ColliderComponent.class, ModelComponent.class);
modelMapper = engine.getComponentMapper(ModelComponent.class);
} }
@Override @Override
@ -21,18 +23,19 @@ public class CollisionSystem extends EntitySystem {
Entity[] entities = getEntities().items; Entity[] entities = getEntities().items;
for (int i = 0, n = getEntities().size; i < n-1; i++) { for (int i = 0, n = getEntities().size; i < n-1; i++) {
Entity entityA = entities[i]; Entity entityA = entities[i];
if (!entityA.isActive()) { if (!entityA.isActive() || entityA.isRemoved()) {
continue; continue;
} }
Rectangle aabbA = MODEL.get(entityA).model.getBoundingBox(); Rectangle aabbA = modelMapper.get(entityA).model.getBoundingBox();
for (int j = i + 1; j < n; j++) { for (int j = i + 1; j < n; j++) {
Entity entityB = entities[j]; Entity entityB = entities[j];
if (!entityB.isActive()) { if (!entityB.isActive() || entityB.isRemoved()) {
continue; continue;
} }
Rectangle aabbB = MODEL.get(entityB).model.getBoundingBox(); Rectangle aabbB = modelMapper.get(entityB).model.getBoundingBox();
if (aabbA.overlaps(aabbB)) { if (aabbA.overlaps(aabbB)) {
engine.callEvent(EventFactory.getNewCollisionEvent(entityA, entityB)); engine.callEvent(EventFactory.getNewCollisionEvent(entityA, entityB));
} }

View File

@ -1,27 +1,30 @@
package com.me.asteroids.systems; package com.me.asteroids.systems;
import com.me.asteroids.components.DecayComponent; import com.me.asteroids.components.DecayComponent;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem; import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Components.DECAY;
public class DecaySystem extends EntitySystem { public class DecaySystem extends EntitySystem {
private ComponentMapper<DecayComponent> decayTimerMapper;
public DecaySystem(Engine engine) { public DecaySystem(Engine engine) {
super(engine, DecayComponent.class); super(engine, DecayComponent.class);
decayTimerMapper = engine.getComponentMapper(DecayComponent.class);
} }
@Override @Override
public void processEntity(Entity entity, float dt) { public void processEntity(Entity entity, float dt) {
DecayComponent decayComponent = DECAY.get(entity); DecayComponent decayComponent = decayTimerMapper.get(entity);
if ((decayComponent.decayTimer -= dt) <= 0) { if ((decayComponent.decayTimer -= dt) <= 0) {
if (decayComponent.remove) { if (decayComponent.remove) {
entity.remove(); entity.remove();
} else { } else {
entity.deactivate(); entity.deactivate();
} }
} }
} }

View File

@ -1,60 +0,0 @@
package com.me.asteroids.systems;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.me.asteroids.Constants;
import com.me.asteroids.Graphics;
import com.me.asteroids.components.GameDataComponent;
import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Components.GAME_DATA;
public class GameDataRenderSystem extends EntitySystem {
private SpriteBatch batch;
private BitmapFont font;
private GlyphLayout gameOverLayout;
private GameDataComponent gameData;
public GameDataRenderSystem(Engine engine, Graphics graphics, BitmapFont font) {
super(engine, GameDataComponent.class);
this.batch = graphics.getUISpriteBatch();
this.font = font;
this.gameOverLayout = new GlyphLayout(font, "GAME OVER");
}
@Override
public void preProcess() {
batch.begin();
}
@Override
public void processEntity(Entity entity, float dt) {
if(gameData == null) {
gameData = GAME_DATA.get(entity);
}
switch (gameData.gameMode) {
case GAME_OVER:
font.setColor(Color.RED);
font.draw(batch, "GAME OVER", Constants.getHalfUIWidth() - (gameOverLayout.width / 2), Constants.getHalfUIHeight() + (font.getCapHeight() / 2));
break;
default:
font.setColor(Color.CHARTREUSE);
font.draw(batch, "Score: " + gameData.score, 15, Constants.getUIHeight() - 15);
font.draw(batch, "Lives: " + gameData.lives, 15, Constants.getUIHeight() - font.getCapHeight() - 30);
break;
}
}
@Override
public void postProcess() {
batch.end();
}
}

View File

@ -1,101 +0,0 @@
package com.me.asteroids.systems;
import com.me.asteroids.Constants;
import com.me.asteroids.GameMode;
import com.me.asteroids.components.GameDataComponent;
import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.events.AsteroidHitEvent;
import com.me.asteroids.events.PlayerDeathEvent;
import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem;
import com.me.common.ecs.event.EventHandler;
import com.me.common.ecs.event.Listener;
import static com.me.asteroids.Components.GAME_DATA;
import static com.me.asteroids.Components.POSITION;
import static com.me.asteroids.Components.VELOCITY;
public class GameDataSystem extends EntitySystem implements Listener {
private GameDataComponent gameData;
public GameDataSystem(Engine engine) {
super(engine, GameDataComponent.class);
}
@Override
public void processEntity(Entity entity, float dt) {
if(gameData == null) {
gameData = GAME_DATA.get(entity);
}
if (gameData.gameModeTimer > 0 && (gameData.gameModeTimer -= dt) < 0) {
switch (gameData.gameMode) {
case DIED:
if (--gameData.lives >= 0) {
resetPlayer();
setGameMode(GameMode.PLAYING);
} else {
setGameMode(GameMode.GAME_OVER);
}
break;
case GAME_OVER:
gameData.reset();
resetPlayer();
setGameMode(GameMode.PLAYING);
}
}
}
@EventHandler
public void onAsteroidHit(AsteroidHitEvent event) {
if (gameData == null) {
return;
}
switch (event.asteroid.generation) {
case 0:
gameData.score += 20;
gameData.newLifeScore += 20;
break;
case 1:
gameData.score += 50;
gameData.newLifeScore += 50;
break;
default:
gameData.score += 100;
gameData.newLifeScore += 100;
}
if (gameData.newLifeScore >= Constants.NEW_LIFE_SCORE) {
gameData.newLifeScore -= Constants.NEW_LIFE_SCORE;
gameData.lives++;
}
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
if (gameData == null) {
return;
}
setGameMode(GameMode.DIED);
}
private void resetPlayer() {
Entity player = engine.getEntityByTag("PLAYER");
PositionComponent position = POSITION.get(player);
position.set(Constants.HALF_WIDTH, Constants.HALF_HEIGHT);
position.rotation = 90;
VELOCITY.get(player).set(0, 0);
player.activate();
}
private void setGameMode(GameMode mode) {
gameData.gameMode = mode;
gameData.gameModeTimer = mode.getTimer();
}
}

View File

@ -4,18 +4,20 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.me.asteroids.Graphics; import com.me.asteroids.Graphics;
import com.me.asteroids.components.ModelComponent; import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.model.Model; import com.me.asteroids.components.model.Model;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem; import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Components.MODEL;
public class ModelRenderSystem extends EntitySystem { public class ModelRenderSystem extends EntitySystem {
private ComponentMapper<ModelComponent> modelMapper;
private ShapeRenderer renderer; private ShapeRenderer renderer;
public ModelRenderSystem(Engine engine, Graphics graphics) { public ModelRenderSystem(Engine engine, Graphics graphics) {
super(engine, ModelComponent.class); super(engine, ModelComponent.class);
this.modelMapper = engine.getComponentMapper(ModelComponent.class);
this.renderer = graphics.getShapeRenderer(); this.renderer = graphics.getShapeRenderer();
} }
@ -26,7 +28,7 @@ public class ModelRenderSystem extends EntitySystem {
@Override @Override
public void processEntity(Entity entity, float dt) { public void processEntity(Entity entity, float dt) {
Model model = MODEL.get(entity).model; Model model = modelMapper.get(entity).model;
renderer.setColor(model.getColor()); renderer.setColor(model.getColor());
model.render(renderer); model.render(renderer);

View File

@ -8,50 +8,46 @@ import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PositionComponent; import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent; import com.me.asteroids.components.VelocityComponent;
import com.me.asteroids.components.model.Model; import com.me.asteroids.components.model.Model;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem; import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Components.ACCELERATION;
import static com.me.asteroids.Components.MODEL;
import static com.me.asteroids.Components.POSITION;
import static com.me.asteroids.Components.VELOCITY;
public class MovementSystem extends EntitySystem { public class MovementSystem extends EntitySystem {
private Vector2 tmp = new Vector2(); private Vector2 tmp = new Vector2();
private ComponentMapper<PositionComponent> positionMapper;
private ComponentMapper<VelocityComponent> velocityMapper;
private ComponentMapper<AccelerationComponent> accelMapper;
private ComponentMapper<ModelComponent> modelMapper;
public MovementSystem(Engine engine) { public MovementSystem(Engine engine) {
super(engine, PositionComponent.class, VelocityComponent.class); super(engine, PositionComponent.class, VelocityComponent.class);
positionMapper = engine.getComponentMapper(PositionComponent.class);
velocityMapper = engine.getComponentMapper(VelocityComponent.class);
accelMapper = engine.getComponentMapper(AccelerationComponent.class);
modelMapper = engine.getComponentMapper(ModelComponent.class);
} }
@Override @Override
public void processEntity(Entity entity, float dt) { public void processEntity(Entity entity, float dt) {
PositionComponent positionComponent = POSITION.get(entity); PositionComponent positionComponent = positionMapper.get(entity);
VelocityComponent velocityComponent = VELOCITY.get(entity); VelocityComponent velocityComponent = velocityMapper.get(entity);
AccelerationComponent accelComponent = ACCELERATION.get(entity);
Vector2 velocity = velocityComponent.velocity; Vector2 velocity = velocityComponent.velocity;
AccelerationComponent accelComponent = accelMapper.get(entity);
if (accelComponent != null && !accelComponent.acceleration.isZero()) { if (accelComponent != null && !accelComponent.acceleration.isZero()) {
tmp.set(accelComponent.acceleration).scl(dt).add(velocity); velocity.add(tmp.set(accelComponent.acceleration).scl(dt));
if (velocity.dot(tmp) < 0) {
// current velocity is opposite of velocity with acceleration applied, which means
// we're coming to a stop. Set position correctly and velocity to zero
velocity.set(0, 0);
positionComponent.position.add(tmp.scl(dt));
} else {
// Else just use the accelerated velocity
velocity.set(tmp);
velocity.clamp(0, accelComponent.maxVelocity);
}
} }
float maxVelocity = velocityComponent.maxVelocity;
Vector2 position = positionComponent.position; Vector2 position = positionComponent.position;
if (!velocity.isZero()) { if (!velocity.isZero()) {
positionComponent.position.add(tmp.set(velocity).scl(dt)); velocity.clamp(0, maxVelocity);
position.add(tmp.set(velocity).scl(dt));
} }
float angularVelocity = velocityComponent.angularVelocity; float angularVelocity = velocityComponent.angularVelocity;
@ -59,7 +55,7 @@ public class MovementSystem extends EntitySystem {
positionComponent.rotation = Utils.wrapAngle(positionComponent.rotation + (angularVelocity * dt)); positionComponent.rotation = Utils.wrapAngle(positionComponent.rotation + (angularVelocity * dt));
} }
ModelComponent modelComponent = MODEL.get(entity); ModelComponent modelComponent = modelMapper.get(entity);
if (modelComponent != null) { if (modelComponent != null) {
Model model = modelComponent.model; Model model = modelComponent.model;
model.setPosition(position); model.setPosition(position);

View File

@ -9,21 +9,19 @@ import com.me.asteroids.components.AccelerationComponent;
import com.me.asteroids.components.PlayerComponent; import com.me.asteroids.components.PlayerComponent;
import com.me.asteroids.components.PositionComponent; import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.components.VelocityComponent; import com.me.asteroids.components.VelocityComponent;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem; import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Constants.rand;
import static com.me.asteroids.Components.ACCELERATION;
import static com.me.asteroids.Components.MODEL;
import static com.me.asteroids.Components.PLAYER;
import static com.me.asteroids.Components.POSITION;
import static com.me.asteroids.Components.VELOCITY;
public class PlayerInputSystem extends EntitySystem { public class PlayerInputSystem extends EntitySystem {
public Vector2 tmp = new Vector2(0, 1); public Vector2 tmp = new Vector2(0, 1);
private ComponentMapper<PositionComponent> positionMapper;
private ComponentMapper<VelocityComponent> velocityMapper;
private ComponentMapper<AccelerationComponent> accelMapper;
public PlayerInputSystem(Engine engine) { public PlayerInputSystem(Engine engine) {
super( super(
engine, engine,
@ -32,14 +30,16 @@ public class PlayerInputSystem extends EntitySystem {
AccelerationComponent.class, AccelerationComponent.class,
PlayerComponent.class PlayerComponent.class
); );
positionMapper = engine.getComponentMapper(PositionComponent.class);
velocityMapper = engine.getComponentMapper(VelocityComponent.class);
accelMapper = engine.getComponentMapper(AccelerationComponent.class);
} }
@Override @Override
public void processEntity(Entity entity, float dt) { public void processEntity(Entity entity, float dt) {
PositionComponent positionComponent = POSITION.get(entity); PositionComponent positionComponent = positionMapper.get(entity);
VelocityComponent velocityComponent = VELOCITY.get(entity); VelocityComponent velocityComponent = velocityMapper.get(entity);
AccelerationComponent accelComponent = ACCELERATION.get(entity); AccelerationComponent accelComponent = accelMapper.get(entity);
PlayerComponent playerComponent = PLAYER.get(entity);
boolean isLeftPressed = Gdx.input.isKeyPressed(Input.Keys.A); boolean isLeftPressed = Gdx.input.isKeyPressed(Input.Keys.A);
boolean isRightPressed = Gdx.input.isKeyPressed(Input.Keys.D); boolean isRightPressed = Gdx.input.isKeyPressed(Input.Keys.D);
@ -51,27 +51,16 @@ public class PlayerInputSystem extends EntitySystem {
velocityComponent.angularVelocity = 0; velocityComponent.angularVelocity = 0;
} }
Vector2 acceleration = accelComponent.acceleration;
Vector2 velocity = velocityComponent.velocity; Vector2 velocity = velocityComponent.velocity;
MODEL.get(playerComponent.afterBurner).setPosition(positionComponent.position);
if (Gdx.input.isKeyPressed(Input.Keys.W)) { if (Gdx.input.isKeyPressed(Input.Keys.W)) {
accelComponent.set(Utils.setUnitVectorAngle(tmp, positionComponent.rotation).scl(12.5f)); acceleration.set(Utils.setUnitVectorAngle(tmp, positionComponent.rotation).scl(500));
if (rand.nextFloat() < 0.85) {
PositionComponent afterBurnerPosition = POSITION.get(playerComponent.afterBurner);
afterBurnerPosition.position.set(positionComponent.position);
afterBurnerPosition.rotation = positionComponent.rotation;
playerComponent.afterBurner.activate();
} else { } else {
playerComponent.afterBurner.deactivate(); if (!velocity.isZero(1f)) {
} acceleration.set(Utils.setUnitVectorAngleRad(tmp, velocity.angleRad()).scl(-100));
} else { } else {
playerComponent.afterBurner.deactivate(); acceleration.set(0, 0);
if (velocity.isZero(0f)) {
accelComponent.set(0, 0);
} else {
accelComponent.set(Utils.setUnitVectorAngleRad(tmp, velocity.angleRad()).scl(-2.5f));
} }
} }

View File

@ -6,17 +6,20 @@ import com.me.asteroids.Constants;
import com.me.asteroids.components.ModelComponent; import com.me.asteroids.components.ModelComponent;
import com.me.asteroids.components.PositionComponent; import com.me.asteroids.components.PositionComponent;
import com.me.asteroids.events.ScreenWrapEvent; import com.me.asteroids.events.ScreenWrapEvent;
import com.me.common.ecs.ComponentMapper;
import com.me.common.ecs.Engine; import com.me.common.ecs.Engine;
import com.me.common.ecs.Entity; import com.me.common.ecs.Entity;
import com.me.common.ecs.EntitySystem; import com.me.common.ecs.EntitySystem;
import static com.me.asteroids.Components.MODEL;
import static com.me.asteroids.Components.POSITION;
public class ScreenWrapSystem extends EntitySystem { public class ScreenWrapSystem extends EntitySystem {
private ComponentMapper<PositionComponent> positionMapper;
private ComponentMapper<ModelComponent> modelMapper;
public ScreenWrapSystem(Engine engine) { public ScreenWrapSystem(Engine engine) {
super(engine, PositionComponent.class, ModelComponent.class); super(engine, PositionComponent.class, ModelComponent.class);
positionMapper = engine.getComponentMapper(PositionComponent.class);
modelMapper = engine.getComponentMapper(ModelComponent.class);
} }
private void updatePosition(Entity entity, Vector2 position, float newX, float newY) { private void updatePosition(Entity entity, Vector2 position, float newX, float newY) {
@ -30,8 +33,8 @@ public class ScreenWrapSystem extends EntitySystem {
@Override @Override
public void processEntity(Entity entity, float dt) { public void processEntity(Entity entity, float dt) {
Vector2 position = POSITION.get(entity).position; Vector2 position = positionMapper.get(entity).position;
Rectangle aabb = MODEL.get(entity).model.getBoundingBox(); Rectangle aabb = modelMapper.get(entity).model.getBoundingBox();
// Check top/bottom edges // Check top/bottom edges
float minY = aabb.y; float minY = aabb.y;

View File

@ -2,7 +2,7 @@ package com.me.common.ecs;
public abstract class BaseSystem { public abstract class BaseSystem {
protected final Engine engine; protected Engine engine;
public BaseSystem(Engine engine) { public BaseSystem(Engine engine) {
this.engine = engine; this.engine = engine;

View File

@ -1,33 +1,38 @@
package com.me.common.ecs; package com.me.common.ecs;
class ComponentBag { public class ComponentBag {
private Component[] items; private Component[] items;
private int size; private int size;
ComponentBag() { public ComponentBag() {
this.items = new Component[16]; this.items = new Component[16];
this.size = items.length; this.size = items.length;
} }
Component get(int index) { public Component get(int index) {
if (index >= size) { if (index >= size) {
return null; return null;
} }
return items[index]; return items[index];
} }
void insert(int index, Component item) { public boolean contains(int index) {
return index < size && items[index] != null;
}
public void insert(int index, Component item) {
if (index >= size) { if (index >= size) {
grow((int) (index * 1.5f)); grow((int) (index * 1.5f));
} }
items[index] = item; items[index] = item;
} }
void remove(int index) { public void remove(int index) {
if (index < size) { if (index >= size) {
items[index] = null; throw new IndexOutOfBoundsException("index must be < size");
} }
items[index] = null;
} }
private void grow(int newSize) { private void grow(int newSize) {

View File

@ -1,36 +1,23 @@
package com.me.common.ecs; package com.me.common.ecs;
import java.util.HashMap; public class ComponentMapper<T extends Component> {
import java.util.Map;
public final class ComponentMapper<T extends Component> { private Engine engine;
private ComponentType type;
private Class<T> typeClass;
private static final Map<Class<? extends Component>, ComponentMapper> mappers = new HashMap<>(); public ComponentMapper(Engine engine, Class<T> typeClass) {
this.engine = engine;
private final ComponentType type;
private final Class<T> typeClass;
private ComponentMapper(Class<T> typeClass) {
this.type = ComponentType.getComponentType(typeClass); this.type = ComponentType.getComponentType(typeClass);
this.typeClass = typeClass; this.typeClass = typeClass;
} }
public T get(Entity entity) { public T get(Entity entity) {
return typeClass.cast(entity.getComponent(type)); return typeClass.cast(engine.getEntityComponent(entity, type));
} }
public boolean has(Entity entity) { public boolean has(Entity entity) {
return type.isInBits(entity.componentBits); return type.isTypeInMask(entity.componentBits);
}
@SuppressWarnings("unchecked")
public static <T extends Component> ComponentMapper<T> getFor(Class<T> typeClass) {
ComponentMapper<T> mapper = mappers.get(typeClass);
if (mapper == null) {
mapper = new ComponentMapper<>(typeClass);
mappers.put(typeClass, mapper);
}
return mapper;
} }
} }

View File

@ -6,44 +6,64 @@ import java.util.Map;
final class ComponentType { final class ComponentType {
private static final Map<Class<? extends Component>, ComponentType> types = new HashMap<>(); private static final Map<Class<? extends Component>, ComponentType> types = new HashMap<>();
private static final ComponentType[] typeById = new ComponentType[Long.SIZE];
private static long nextBit = 1l; private static long nextBit = 1l;
private static int nextId = 0; private static int nextId = 0;
final long bit; protected long bits;
final int id; protected int id;
private ComponentType() { private ComponentType() {
this.bit = nextBit; this.bits = nextBit;
this.id = nextId++; this.id = nextId++;
nextBit <<= 1; this.nextBit <<= 1;
} }
boolean isInBits(long bits) { protected long getBits() {
return (bits & bit) == bit;
}
@SafeVarargs
static long getBitsFor(Class<? extends Component>... components) {
long bits = 0l;
for (Class<? extends Component> clazz : components) {
bits |= getComponentType(clazz).bit;
}
return bits; return bits;
} }
static ComponentType registerComponentType(Class<? extends Component> component) { protected int getId() {
return id;
}
protected static long getMaskBits(Class<? extends Component>... components) {
long mask = 0l;
for (Class<? extends Component> clazz : components) {
mask |= getTypeBits(clazz);
}
return mask;
}
protected static long getTypeBits(Class<? extends Component> component) {
return getComponentType(component).bits;
}
protected static int getTypeId(Class<? extends Component> component) {
return getComponentType(component).id;
}
protected static ComponentType getById(int id) {
return typeById[id];
}
protected boolean isTypeInMask(long mask) {
return (bits & mask) == bits;
}
protected static void registerComponentType(Class<? extends Component> component) {
ComponentType type = types.get(component); ComponentType type = types.get(component);
if (type != null) { if (type != null) {
throw new IllegalArgumentException(component.getName() + " has already been registered."); throw new IllegalArgumentException(component.getName() + " has already been registered.");
} }
type = new ComponentType(); type = new ComponentType();
types.put(component, type); types.put(component, type);
return type; typeById[type.id] = type;
} }
static ComponentType getComponentType(Class<? extends Component> component) { protected static ComponentType getComponentType(Class<? extends Component> component) {
ComponentType type = types.get(component); ComponentType type = types.get(component);
if (type == null) { if (type == null) {
throw new IllegalArgumentException(component.getName() + " has not been registered."); throw new IllegalArgumentException(component.getName() + " has not been registered.");
@ -51,4 +71,8 @@ final class ComponentType {
return type; return type;
} }
protected static int getRegisteredComponentTypeCount() {
return types.size();
}
} }

View File

@ -1,55 +1,49 @@
package com.me.common.ecs; package com.me.common.ecs;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectSet;
import com.me.common.ecs.event.Event; import com.me.common.ecs.event.Event;
import com.me.common.ecs.event.Listener; import com.me.common.ecs.event.Listener;
import java.util.HashMap;
import java.util.Map;
public class Engine { public class Engine {
private final EntityManager entityManager; private ComponentBag[] components;
private final ComponentBag[] components; protected Array<BaseSystem> systems;
private final Array<BaseSystem> systems;
private final ListenerRegistry listenerRegistry; private EntityManager entityManager;
private ListenerRegistry listenerRegistry;
private Map<Class<? extends Component>, ComponentMapper> componentMappers;
public Engine() { public Engine() {
this.entityManager = new EntityManager(this); this.entityManager = new EntityManager(this);
this.components = new ComponentBag[Long.SIZE];
this.systems = new Array<>(); this.systems = new Array<>();
this.listenerRegistry = new ListenerRegistry(); this.listenerRegistry = new ListenerRegistry();
this.componentMappers = new HashMap<>();
} }
/**
* Register a Component class.
* @param clazz
*/
public void registerComponentClass(Class<? extends Component> clazz) { public void registerComponentClass(Class<? extends Component> clazz) {
ComponentType type = ComponentType.registerComponentType(clazz); ComponentType.registerComponentType(clazz);
components[type.id] = new ComponentBag();
} }
/**
* Register a BaseSystem.
* @param system
*/
public void registerSystem(BaseSystem system) { public void registerSystem(BaseSystem system) {
systems.add(system); this.systems.add(system);
} }
/**
* Register an event Listener.
* @param listener
*/
public void registerListener(Listener listener) { public void registerListener(Listener listener) {
listenerRegistry.registerListener(listener); listenerRegistry.registerListener(listener);
} }
/** public void ready() {
* Process all systems this.components = new ComponentBag[ComponentType.getRegisteredComponentTypeCount()];
* @param dt the time (in seconds) passed since the last update for (int i = 0; i < components.length; i++) {
*/ components[i] = new ComponentBag();
}
}
public void update(float dt) { public void update(float dt) {
entityManager.update(); entityManager.update();
@ -60,103 +54,61 @@ public class Engine {
} }
} }
/**
* @return all entities known by the engine - both those active and inactive.
*/
public Array<Entity> getEntities() { public Array<Entity> getEntities() {
return entityManager.getEntities(); return entityManager.entities;
} }
/** public int getEntityCount() {
* @return all currently registered systems. return entityManager.entities.size;
*/
public Array<BaseSystem> getSystems() {
return systems;
} }
/**
* @return a new Entity object which can have fresh components added to it
*/
public Entity createEntity() { public Entity createEntity() {
return entityManager.create(); return entityManager.create();
} }
/**
* Calls the given event, dispatching it to all registered Listeners that have an EventHandler
* for this particular event.
* @param event the event to call
*/
public void callEvent(Event event) { public void callEvent(Event event) {
listenerRegistry.callEvent(event); listenerRegistry.callEvent(event);
} }
/** protected void refreshEntity(Entity entity) {
* @param group the group to look up
* @return the list of entities that are a part of the given group.
*/
public Array<Entity> getEntitiesInGroup(String group) {
return entityManager.getGroupEntities(group);
}
/**
* @param tag the tag to look up
* @return the entity with the given tag, or null
*/
public Entity getEntityByTag(String tag) {
return entityManager.getEntityByTag(tag);
}
void addEntityGroup(Entity entity, String group) {
entityManager.addEntityToGroup(entity, group);
}
void removeEntityGroup(Entity entity, String group) {
entityManager.removeEntityFromGroup(entity, group);
}
void tagEntity(Entity entity, String tag) {
entityManager.tagEntity(entity, tag);
}
void removeEntityTag(Entity entity) {
entityManager.removeEntityTag(entity);
}
void refreshEntity(Entity entity) {
entityManager.queueRefresh(entity); entityManager.queueRefresh(entity);
} }
void removeAllEntityComponents(int entityId) { protected void removeAllEntityComponents(int entityId) {
for (ComponentBag bag : components) { for (int i = 0; i < components.length; i++) {
if (bag == null) { components[i].insert(entityId, null);
// The rest of the array should also be null given that we never nullify
// an index after it has been set, and they are set sequentially. So, break.
break;
}
bag.remove(entityId);
} }
} }
void addEntityComponent(Entity entity, Component component) { protected void addEntityComponent(Entity entity, Component component) {
ComponentType type = ComponentType.getComponentType(component.getClass()); ComponentType type = ComponentType.getComponentType(component.getClass());
components[type.id].insert(entity.id, component); components[type.id].insert(entity.id, component);
entity.addComponentType(type); entity.addComponentType(type);
} }
void removeEntityComponent(Entity entity, Component component) { protected void removeEntityComponent(Entity entity, Component component) {
ComponentType type = ComponentType.getComponentType(component.getClass()); ComponentType type = ComponentType.getComponentType(component.getClass());
components[type.id].remove(entity.id); components[type.id].remove(entity.id);
entity.removeComponentType(type); entity.removeComponentType(type);
} }
<T extends Component> T getEntityComponent(Entity entity, Class<T> clazz) { protected <T extends Component> T getEntityComponent(Entity entity, Class<T> clazz) {
return clazz.cast(getEntityComponent(entity, ComponentType.getComponentType(clazz))); return clazz.cast(components[ComponentType.getTypeId(clazz)].get(entity.id));
} }
Component getEntityComponent(Entity entity, ComponentType type) { protected Component getEntityComponent(Entity entity, ComponentType type) {
return components[type.id].get(entity.id); return components[type.id].get(entity.id);
} }
@SuppressWarnings("unchecked")
public <T extends Component> ComponentMapper<T> getComponentMapper(Class<T> typeClass) {
ComponentMapper<T> mapper = componentMappers.get(typeClass);
if (mapper == null) {
mapper = new ComponentMapper<>(this, typeClass);
componentMappers.put(typeClass, mapper);
}
return mapper;
}
} }

View File

@ -1,31 +1,25 @@
package com.me.common.ecs; package com.me.common.ecs;
import com.badlogic.gdx.utils.ObjectSet;
public final class Entity { public final class Entity {
private static int nextId = 0; private static int nextId = 0;
private static long nextUniqueId = 0; private static long nextUniqueId = 0;
private final Engine engine; private Engine engine;
final int id; protected final int id;
long componentBits; protected long componentBits;
long systemEnabledBits; protected long systemEnabledBits;
boolean pendingRefresh;
boolean active; protected boolean active;
boolean removed; protected boolean removed;
protected boolean pendingRefresh;
ObjectSet<String> groups;
String tag;
private long uniqueId; private long uniqueId;
protected Entity(Engine engine) { protected Entity(Engine engine) {
this.engine = engine; this.engine = engine;
this.id = nextId++; this.id = nextId++;
this.groups = new ObjectSet<>();
} }
public int getId() { public int getId() {
@ -36,10 +30,6 @@ public final class Entity {
return uniqueId; return uniqueId;
} }
public Engine getEngine() {
return engine;
}
public boolean isActive() { public boolean isActive() {
return active; return active;
} }
@ -49,25 +39,30 @@ public final class Entity {
} }
public void activate() { public void activate() {
if (!active) {
active = true; active = true;
refresh(); refresh();
} }
}
public void deactivate() { public void deactivate() {
if (active) {
active = false; active = false;
refresh(); refresh();
} }
}
public void remove() { public void remove() {
if (!removed) {
removed = true; removed = true;
active = false;
refresh(); refresh();
} }
public Engine getEngine() {
return this.engine;
}
public boolean hasComponent(Class<? extends Component> clazz) {
return ComponentType.getComponentType(clazz).isTypeInMask(componentBits);
}
public <T extends Component> T getComponent(Class<T> clazz) {
return engine.getEntityComponent(this, clazz);
} }
public void addComponent(Component component) { public void addComponent(Component component) {
@ -80,81 +75,39 @@ public final class Entity {
refresh(); refresh();
} }
private void refresh() { protected void addComponentType(ComponentType type) {
componentBits |= type.bits;
}
protected void removeComponentType(ComponentType type) {
componentBits &= ~type.bits;
}
protected void addSystemEnabledBit(long bit) {
systemEnabledBits |= bit;
}
protected void removeSystemEnabledBit(long bit) {
systemEnabledBits &= ~bit;
}
public void refresh() {
if (!pendingRefresh) { if (!pendingRefresh) {
engine.refreshEntity(this); engine.refreshEntity(this);
pendingRefresh = true; pendingRefresh = true;
} }
} }
public boolean hasComponent(Class<? extends Component> clazz) { protected void reset() {
return ComponentType.getComponentType(clazz).isInBits(componentBits);
}
public <T extends Component> T getComponent(Class<T> clazz) {
return engine.getEntityComponent(this, clazz);
}
public ObjectSet<String> getGroups() {
return groups;
}
public void addGroup(String group) {
engine.addEntityGroup(this, group);
}
public void removeGroup(String group) {
engine.removeEntityGroup(this, group);
}
public boolean inGroup(String group) {
return groups.contains(group);
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
engine.tagEntity(this, tag);
}
public void removeTag() {
engine.removeEntityTag(this);
}
public boolean hasTag(String tag) {
return tag.equals(this.tag);
}
// Internal getComponent method accessed only by ComponentMapper
Component getComponent(ComponentType type) {
return engine.getEntityComponent(this, type);
}
void addComponentType(ComponentType type) {
componentBits |= type.bit;
}
void removeComponentType(ComponentType type) {
componentBits &= ~type.bit;
}
void addSystemEnabledBit(long systemBit) {
systemEnabledBits |= systemBit;
}
void removeSystemEnabledBit(long systemBit) {
systemEnabledBits &= ~systemBit;
}
void reset() {
componentBits = 0; componentBits = 0;
systemEnabledBits = 0; systemEnabledBits = 0;
active = false; active = false;
removed = false; removed = false;
pendingRefresh = false; pendingRefresh = false;
uniqueId = nextUniqueId++; }
protected void updateUniqueId() {
this.uniqueId = nextUniqueId++;
} }
public String toString() { public String toString() {

View File

@ -1,18 +1,14 @@
package com.me.common.ecs; package com.me.common.ecs;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
final class EntityManager { final class EntityManager {
private final Engine engine; private Engine engine;
private final Array<Entity> entities; protected Array<Entity> entities;
private final Array<Entity> removedEntities; protected Array<Entity> removedEntities;
private final Array<Entity> toRefresh; protected Array<Entity> toRefresh;
private final ObjectMap<String, Array<Entity>> groupEntities;
private final ObjectMap<String, Entity> taggedEntities;
EntityManager(Engine engine) { EntityManager(Engine engine) {
this.engine = engine; this.engine = engine;
@ -20,16 +16,9 @@ final class EntityManager {
this.entities = new Array<>(false, 16); this.entities = new Array<>(false, 16);
this.removedEntities = new Array<>(false, 16); this.removedEntities = new Array<>(false, 16);
this.toRefresh = new Array<>(false, 16); this.toRefresh = new Array<>(false, 16);
this.groupEntities = new ObjectMap<>();
this.taggedEntities = new ObjectMap<>();
} }
Array<Entity> getEntities() { public Entity create() {
return entities;
}
Entity create() {
Entity entity; Entity entity;
if (!removedEntities.isEmpty()) { if (!removedEntities.isEmpty()) {
entity = removedEntities.removeIndex(0); entity = removedEntities.removeIndex(0);
@ -37,11 +26,12 @@ final class EntityManager {
entity = new Entity(engine); entity = new Entity(engine);
} }
entity.reset(); entity.reset();
entity.updateUniqueId();
entities.add(entity); entities.add(entity);
return entity; return entity;
} }
void update() { public void update() {
if (toRefresh.isEmpty()) { if (toRefresh.isEmpty()) {
return; return;
} }
@ -54,52 +44,12 @@ final class EntityManager {
toRefresh.clear(); toRefresh.clear();
} }
Array<Entity> getGroupEntities(String group) { public void queueRefresh(Entity entity) {
Array<Entity> entities = groupEntities.get(group);
if (entities == null) {
entities = new Array<>();
groupEntities.put(group, entities);
}
return entities;
}
void addEntityToGroup(Entity entity, String group) {
if (!entity.groups.contains(group)) {
getGroupEntities(group).add(entity);
entity.groups.add(group);
}
}
void removeEntityFromGroup(Entity entity, String group) {
getGroupEntities(group).removeValue(entity, true);
entity.groups.remove(group);
}
Entity getEntityByTag(String tag) {
return taggedEntities.get(tag);
}
void tagEntity(Entity entity, String tag) {
Entity previouslyTagged = taggedEntities.put(tag, entity);
if (previouslyTagged != null) {
previouslyTagged.tag = null;
}
entity.tag = tag;
}
void removeEntityTag(Entity entity) {
if (entity.tag != null) {
taggedEntities.remove(entity.tag);
entity.tag = null;
}
}
void queueRefresh(Entity entity) {
toRefresh.add(entity); toRefresh.add(entity);
} }
private void refreshEntity(Entity entity) { private void refreshEntity(Entity entity) {
for (BaseSystem system : engine.getSystems()) { for (BaseSystem system : engine.systems) {
if (!(system instanceof EntitySystem)) { if (!(system instanceof EntitySystem)) {
continue; continue;
} }
@ -107,20 +57,11 @@ final class EntityManager {
((EntitySystem) system).refresh(entity); ((EntitySystem) system).refresh(entity);
} }
if (entity.isRemoved()) { if (entity.removed) {
engine.removeAllEntityComponents(entity.id); engine.removeAllEntityComponents(entity.id);
entities.removeValue(entity, true); entities.removeValue(entity, true);
removedEntities.add(entity); removedEntities.add(entity);
// remove the entity from any groups it is in
for (String group : entity.groups) {
getGroupEntities(group).removeValue(entity, true);
}
entity.groups.clear();
removeEntityTag(entity);
} }
} }
} }

View File

@ -10,16 +10,15 @@ public abstract class EntitySystem extends BaseSystem {
private static final Map<Class<? extends EntitySystem>, Long> systemBits = new HashMap<>(); private static final Map<Class<? extends EntitySystem>, Long> systemBits = new HashMap<>();
private static long nextBit = 1l; private static long nextBit = 1l;
private final long systemBit; private long systemBit;
private final long componentBits; private long typeBits;
private final Array<Entity> entities; protected Array<Entity> entities;
@SafeVarargs
public EntitySystem(Engine engine, Class<? extends Component>... components) { public EntitySystem(Engine engine, Class<? extends Component>... components) {
super(engine); super(engine);
this.systemBit = getBitFor(getClass()); this.systemBit = getBitFor(getClass());
this.componentBits = ComponentType.getBitsFor(components); this.typeBits = ComponentType.getMaskBits(components);
this.entities = new Array<>(true, 16, Entity.class); this.entities = new Array<>(true, 16, Entity.class);
} }
@ -30,7 +29,7 @@ public abstract class EntitySystem extends BaseSystem {
public void process(float dt) { public void process(float dt) {
for (int i = 0, n = entities.size; i < n; i++) { for (int i = 0, n = entities.size; i < n; i++) {
Entity entity = entities.items[i]; Entity entity = entities.items[i];
if (entity.isActive()) { if (!entity.removed && entity.active) {
processEntity(entity, dt); processEntity(entity, dt);
} }
} }
@ -38,13 +37,13 @@ public abstract class EntitySystem extends BaseSystem {
public abstract void processEntity(Entity entity, float dt); public abstract void processEntity(Entity entity, float dt);
void refresh(Entity entity) { protected void refresh(Entity entity) {
boolean enabled = (entity.systemEnabledBits & systemBit) == systemBit; boolean enabled = (entity.systemEnabledBits & systemBit) == systemBit;
boolean interested = (entity.componentBits & componentBits) == componentBits; boolean interested = (entity.componentBits & typeBits) == typeBits;
if (interested && !enabled && entity.active && !entity.removed) { if (interested && !enabled && entity.active && !entity.removed) {
add(entity); add(entity);
} else if (enabled && (!interested || !entity.active) || entity.removed) { } else if (enabled && (!interested || !entity.active || entity.removed)) {
remove(entity); remove(entity);
} }
} }
@ -60,7 +59,7 @@ public abstract class EntitySystem extends BaseSystem {
} }
private static long getBitFor(Class<? extends EntitySystem> es) { static long getBitFor(Class<? extends EntitySystem> es) {
Long bits = systemBits.get(es); Long bits = systemBits.get(es);
if (bits == null) { if (bits == null) {
bits = nextBit; bits = nextBit;

View File

@ -11,15 +11,15 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
class ListenerRegistry { public class ListenerRegistry {
private final Map<Class<? extends Event>, List<RegisteredListener>> registeredListeners; private final Map<Class<? extends Event>, List<RegisteredListener>> registeredListeners;
ListenerRegistry() { protected ListenerRegistry() {
this.registeredListeners = new HashMap<>(); this.registeredListeners = new HashMap<>();
} }
void registerListener(Listener listener) { protected void registerListener(Listener listener) {
for (Method method : listener.getClass().getMethods()) { for (Method method : listener.getClass().getMethods()) {
EventHandler eh = method.getAnnotation(EventHandler.class); EventHandler eh = method.getAnnotation(EventHandler.class);
if (eh == null) { if (eh == null) {
@ -37,9 +37,10 @@ class ListenerRegistry {
List<RegisteredListener> executors = getRegisteredListeners(eventClass); List<RegisteredListener> executors = getRegisteredListeners(eventClass);
executors.add(new RegisteredListener(listener, method, eventClass, eh)); executors.add(new RegisteredListener(listener, method, eventClass, eh));
} }
} }
void callEvent(Event event) { protected void callEvent(Event event) {
List<RegisteredListener> listeners = getRegisteredListeners(event.getClass()); List<RegisteredListener> listeners = getRegisteredListeners(event.getClass());
if (listeners.isEmpty()) { if (listeners.isEmpty()) {
return; return;

View File

@ -4,10 +4,11 @@ import java.lang.reflect.Method;
public class RegisteredListener { public class RegisteredListener {
private final Listener listener; private Listener listener;
private final Method method; private Method method;
private final Class<? extends Event> eventType; private Class<? extends Event> eventType;
private final EventHandler eh;
public EventHandler eh;
public RegisteredListener(Listener listener, Method method, Class<? extends Event> eventType, EventHandler eh) { public RegisteredListener(Listener listener, Method method, Class<? extends Event> eventType, EventHandler eh) {
this.listener = listener; this.listener = listener;

View File

@ -8,10 +8,9 @@ public class DesktopLauncher {
public static void main (String[] arg) { public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "Asteroids"; config.title = "Asteroids";
config.resizable = true; config.resizable = false;
// config.fullscreen = false;
config.width = 800; config.width = 800;
config.height = 400; config.height = 600;
new LwjglApplication(new Asteroids(), config); new LwjglApplication(new Asteroids(), config);
} }
} }