From fa809148a7f45c9adea6d410face7b433837c263 Mon Sep 17 00:00:00 2001 From: Matt Low Date: Thu, 30 Jan 2020 23:56:00 +0400 Subject: [PATCH] Add GameDataComponent which holds current game's data (score, lives, etc) Now more like an actual game. Score, lives, and when you die, you get respawned. If you die rnough times, you Game Over, and your lives and score are reset. Each time you get 10000 points, you gain a life! Add GameMode enum Add GameDataSystem which manipulates the current game data based on events. Add GameDataRenderSystem which renders the current game data. Add AsteroidHitEvent and PlayerDeathEvent which are are fired when an asteroid is destroyed or the player is killed, respectively. Add SpriteBatch to Graphics --- core/src/com/me/asteroids/Constants.java | 2 + core/src/com/me/asteroids/GameMode.java | 24 +++++ core/src/com/me/asteroids/Graphics.java | 7 ++ .../components/GameDataComponent.java | 28 +++++ .../me/asteroids/events/AsteroidHitEvent.java | 14 +++ .../me/asteroids/events/PlayerDeathEvent.java | 14 +++ .../com/me/asteroids/screens/GameScreen.java | 26 ++++- .../systems/GameDataRenderSystem.java | 62 +++++++++++ .../me/asteroids/systems/GameDataSystem.java | 102 ++++++++++++++++++ 9 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 core/src/com/me/asteroids/GameMode.java create mode 100644 core/src/com/me/asteroids/components/GameDataComponent.java create mode 100644 core/src/com/me/asteroids/events/AsteroidHitEvent.java create mode 100644 core/src/com/me/asteroids/events/PlayerDeathEvent.java create mode 100644 core/src/com/me/asteroids/systems/GameDataRenderSystem.java create mode 100644 core/src/com/me/asteroids/systems/GameDataSystem.java 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(); + } + +}