diff --git a/core/src/com/me/asteroids/Constants.java b/core/src/com/me/asteroids/Constants.java index 8f7b847..ef5b1fa 100644 --- a/core/src/com/me/asteroids/Constants.java +++ b/core/src/com/me/asteroids/Constants.java @@ -17,4 +17,6 @@ public class Constants { public static final float ASTEROID_SPAWN_DELAY = 1f; public static final int ASTEROID_SPAWN_COUNT = 4; + public static final int NEW_LIFE_SCORE = 10000; + } diff --git a/core/src/com/me/asteroids/GameMode.java b/core/src/com/me/asteroids/GameMode.java new file mode 100644 index 0000000..d86505a --- /dev/null +++ b/core/src/com/me/asteroids/GameMode.java @@ -0,0 +1,24 @@ +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; + } + +} diff --git a/core/src/com/me/asteroids/Graphics.java b/core/src/com/me/asteroids/Graphics.java index e48ea20..33dd8a0 100644 --- a/core/src/com/me/asteroids/Graphics.java +++ b/core/src/com/me/asteroids/Graphics.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.utils.viewport.FitViewport; import com.badlogic.gdx.utils.viewport.Viewport; @@ -17,6 +18,7 @@ public class Graphics { private Viewport viewport; private ShapeRenderer shapeRenderer; + private SpriteBatch spriteBatch; public Graphics(int worldWidth, int worldHeight) { this.worldWidth = worldWidth; @@ -28,6 +30,7 @@ public class Graphics { this.viewport = new FitViewport(worldWidth, worldHeight, camera); this.shapeRenderer = new ShapeRenderer(); + this.spriteBatch = new SpriteBatch(); } public void initialize() { @@ -53,6 +56,10 @@ public class Graphics { return shapeRenderer; } + public SpriteBatch getSpriteBatch() { + return spriteBatch; + } + private void updateDimensions() { viewport.setWorldSize(worldWidth, worldHeight); viewport.update(screenWidth, screenHeight, true); diff --git a/core/src/com/me/asteroids/components/GameDataComponent.java b/core/src/com/me/asteroids/components/GameDataComponent.java new file mode 100644 index 0000000..4059ddd --- /dev/null +++ b/core/src/com/me/asteroids/components/GameDataComponent.java @@ -0,0 +1,28 @@ +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; + } + +} diff --git a/core/src/com/me/asteroids/events/AsteroidHitEvent.java b/core/src/com/me/asteroids/events/AsteroidHitEvent.java new file mode 100644 index 0000000..91e624f --- /dev/null +++ b/core/src/com/me/asteroids/events/AsteroidHitEvent.java @@ -0,0 +1,14 @@ +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; + } + +} diff --git a/core/src/com/me/asteroids/events/PlayerDeathEvent.java b/core/src/com/me/asteroids/events/PlayerDeathEvent.java new file mode 100644 index 0000000..3305b12 --- /dev/null +++ b/core/src/com/me/asteroids/events/PlayerDeathEvent.java @@ -0,0 +1,14 @@ +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; + } + +} diff --git a/core/src/com/me/asteroids/screens/GameScreen.java b/core/src/com/me/asteroids/screens/GameScreen.java index 5836124..ac08f61 100644 --- a/core/src/com/me/asteroids/screens/GameScreen.java +++ b/core/src/com/me/asteroids/screens/GameScreen.java @@ -14,17 +14,22 @@ import com.me.asteroids.components.BulletComponent; import com.me.asteroids.components.ColliderComponent; import com.me.asteroids.components.DebrisComponent; 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.asteroids.components.model.PolygonModel; +import com.me.asteroids.events.AsteroidHitEvent; import com.me.asteroids.events.BulletAsteroidCollisionEvent; import com.me.asteroids.events.PlayerASteroidCollisionEvent; +import com.me.asteroids.events.PlayerDeathEvent; import com.me.asteroids.events.ScreenWrapEvent; import com.me.asteroids.systems.AsteroidSpawningSystem; import com.me.asteroids.systems.CollisionSystem; 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.MovementSystem; import com.me.asteroids.systems.PlayerInputSystem; @@ -53,10 +58,10 @@ public class GameScreen extends Screen implements Listener { public void setup() { batch = new SpriteBatch(); font = new BitmapFont(); - font.setColor(Color.RED); engine = new Engine(); + engine.registerComponentClass(GameDataComponent.class); engine.registerComponentClass(PlayerComponent.class); engine.registerComponentClass(BulletComponent.class); engine.registerComponentClass(AsteroidComponent.class); @@ -68,6 +73,9 @@ public class GameScreen extends Screen implements Listener { engine.registerComponentClass(AccelerationComponent.class); engine.registerComponentClass(ModelComponent.class); + GameDataSystem system = new GameDataSystem(engine); + + engine.registerSystem(system); engine.registerSystem(new PlayerInputSystem(engine)); engine.registerSystem(new AsteroidSpawningSystem(engine)); engine.registerSystem(new DecaySystem(engine)); @@ -75,11 +83,17 @@ public class GameScreen extends Screen implements Listener { engine.registerSystem(new CollisionSystem(engine)); engine.registerSystem(new ScreenWrapSystem(engine)); engine.registerSystem(new ModelRenderSystem(engine, graphics)); + engine.registerSystem(new GameDataRenderSystem(engine, graphics, font)); + engine.registerListener(system); engine.registerListener(this.new EventListener(engine)); engine.ready(); + Entity gameData = engine.createEntity(); + gameData.addComponent(new GameDataComponent()); + gameData.activate(); + Entity player = EntityFactory.createPlayer(engine); player.activate(); } @@ -91,7 +105,8 @@ public class GameScreen extends Screen implements Listener { if (Constants.DEBUG) { batch.begin(); - font.draw(batch, String.format("FPS: %d, Entities: %d", Gdx.graphics.getFramesPerSecond(), engine.getEntityCount()), 50, 50); + font.setColor(Color.RED); + font.draw(batch, String.format("FPS: %d, Entities: %d", Gdx.graphics.getFramesPerSecond(), engine.getEntityCount()), 15, 15 + font.getCapHeight()); batch.end(); } } @@ -134,9 +149,10 @@ public class GameScreen extends Screen implements Listener { if (model.contains(bulletPosition)) { // AABBs intersect but let's only consider it a hit if the bullet's position // is actually inside the asteroid + AsteroidComponent asteroid = asteroidMapper.get(event.getAsteroid()); + engine.callEvent(new AsteroidHitEvent(asteroid)); event.getBullet().remove(); - int generation = asteroidMapper.get(event.getAsteroid()).generation; - if (generation < 2) { + if (asteroid.generation < 2) { for (Entity shard : EntityFactory.splitAsteroidIntoChunks(engine, event.getAsteroid(), 2, 2/3f)) { shard.activate(); } @@ -146,7 +162,6 @@ public class GameScreen extends Screen implements Listener { } } event.getAsteroid().remove(); - } } @@ -156,6 +171,7 @@ public class GameScreen extends Screen implements Listener { PolygonModel player = (PolygonModel) modelMapper.get(event.getPlayer()).model; if (asteroid.contains(player.getVertices()) || player.contains(asteroid.getVertices())) { + engine.callEvent(new PlayerDeathEvent(event.getPlayer())); event.getPlayer().deactivate(); for (Entity debris : EntityFactory.createDebris(engine, event.getPlayer())) { debris.activate(); diff --git a/core/src/com/me/asteroids/systems/GameDataRenderSystem.java b/core/src/com/me/asteroids/systems/GameDataRenderSystem.java new file mode 100644 index 0000000..91f028a --- /dev/null +++ b/core/src/com/me/asteroids/systems/GameDataRenderSystem.java @@ -0,0 +1,62 @@ +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.ComponentMapper; +import com.me.common.ecs.Engine; +import com.me.common.ecs.Entity; +import com.me.common.ecs.EntitySystem; + +public class GameDataRenderSystem extends EntitySystem { + + private ComponentMapper gameDataMapper; + + private SpriteBatch batch; + private BitmapFont font; + private GlyphLayout gameOverLayout; + + private GameDataComponent gameData; + + public GameDataRenderSystem(Engine engine, Graphics graphics, BitmapFont font) { + super(engine, GameDataComponent.class); + gameDataMapper = engine.getComponentMapper(GameDataComponent.class); + batch = graphics.getSpriteBatch(); + 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 = gameDataMapper.get(entity); + } + + switch (gameData.gameMode) { + case GAME_OVER: + font.setColor(Color.RED); + font.draw(batch, "GAME OVER", Constants.HALF_WIDTH - (gameOverLayout.width / 2), Constants.HALF_HEIGHT); + break; + default: + font.setColor(Color.CHARTREUSE); + font.draw(batch, "Score: " + gameData.score, 15, Constants.HEIGHT - 15); + font.draw(batch, "Lives: " + gameData.lives, 15, Constants.HEIGHT - 30); + break; + } + } + + @Override + public void postProcess() { + batch.end(); + } + +} diff --git a/core/src/com/me/asteroids/systems/GameDataSystem.java b/core/src/com/me/asteroids/systems/GameDataSystem.java new file mode 100644 index 0000000..7b793cb --- /dev/null +++ b/core/src/com/me/asteroids/systems/GameDataSystem.java @@ -0,0 +1,102 @@ +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.components.VelocityComponent; +import com.me.asteroids.events.AsteroidHitEvent; +import com.me.asteroids.events.PlayerDeathEvent; +import com.me.common.ecs.ComponentMapper; +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; + +public class GameDataSystem extends EntitySystem implements Listener { + + private ComponentMapper gameDataMapper; + + private GameDataComponent gameData; + + public GameDataSystem(Engine engine) { + super(engine, GameDataComponent.class); + gameDataMapper = engine.getComponentMapper(GameDataComponent.class); + } + + @Override + public void processEntity(Entity entity, float dt) { + if(gameData == null) { + gameData = gameDataMapper.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.getEntities().get(1); + PositionComponent position = player.getComponent(PositionComponent.class); + position.rotation = 90; + position.position.set(Constants.HALF_WIDTH, Constants.HALF_HEIGHT); + player.getComponent(VelocityComponent.class).velocity.set(0, 0); + player.activate(); + } + + private void setGameMode(GameMode mode) { + gameData.gameMode = mode; + gameData.gameModeTimer = mode.getTimer(); + } + +}