From db3ee943d8f96e29fbdc785989deeb917a4c51f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delorme?= Date: Thu, 18 Nov 2021 22:30:32 +0100 Subject: [PATCH] feat(LifeTime): Add life duration to GameObject MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add lifetime and active attributes to GameObject, and manage lifetime computation, - Modify Render to manage active attribute on all GameObject (Simple a relative to camera), - Modify PhysicEngine to manage the GameObject update process. - Extract debug grid from Render system and create a DebugViewportGrid and a DebugViewportGridRenderHelper. - prepare next release (1.0.1-SNAPSHOT) Signed-off-by: Frédéric Delorme --- pom.xml | 2 +- .../core/entity/DebugViewportGrid.java | 37 ++++++++++++ .../core/entity/GameObject.java | 26 ++++++-- .../fromclasstogame/core/gfx/Render.java | 59 +++++++++---------- .../gfx/renderer/AbstractRenderHelper.java | 3 + .../DebugViewportGridRenderHelper.java | 34 +++++++++++ .../gfx/renderer/GameObjectRenderHelper.java | 6 +- .../core/gfx/renderer/TextRenderHelper.java | 11 ++-- .../core/physic/PhysicEngine.java | 15 ++--- .../core/physic/collision/BoundingBox.java | 14 ++--- .../demo/scenes/DemoScene.java | 23 +++++++- 11 files changed, 166 insertions(+), 64 deletions(-) create mode 100644 src/main/java/fr/snapgames/fromclasstogame/core/entity/DebugViewportGrid.java create mode 100644 src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/DebugViewportGridRenderHelper.java diff --git a/pom.xml b/pom.xml index d96267a0..81a750da 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 core fromclasstogame - 1.0.0 + 1.0.1-SNAPSHOT FromClassToGame Journey from a simple class to a Game, with the Java language 2021 diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/entity/DebugViewportGrid.java b/src/main/java/fr/snapgames/fromclasstogame/core/entity/DebugViewportGrid.java new file mode 100644 index 00000000..4c5525cc --- /dev/null +++ b/src/main/java/fr/snapgames/fromclasstogame/core/entity/DebugViewportGrid.java @@ -0,0 +1,37 @@ +package fr.snapgames.fromclasstogame.core.entity; + +import fr.snapgames.fromclasstogame.core.physic.World; + +import java.util.List; + +public class DebugViewportGrid extends GameObject { + public int gridX; + public int gridY; + private World w; + + + public DebugViewportGrid(String objectName, World w) { + super(objectName); + this.w = w; + this.gridX = 16; + this.gridY = 16; + } + + public DebugViewportGrid(String objectName, World w, int gridX, int gridY) { + super(objectName); + this.w = w; + this.gridX = gridX; + this.gridY = gridY; + } + + @Override + public List getDebugInfo() { + List info = super.getDebugInfo(); + info.add(String.format("world: %fx%f", w.width, w.height)); + return info; + } + + public World getWorld() { + return w; + } +} diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/entity/GameObject.java b/src/main/java/fr/snapgames/fromclasstogame/core/entity/GameObject.java index 17c5ae6e..daa7e51a 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/entity/GameObject.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/entity/GameObject.java @@ -12,12 +12,16 @@ import java.util.List; import java.util.Map; + public class GameObject implements Entity { private static int index = 0; public int id = ++index; public String name = "noname_" + id; + + public boolean active = true; + public Vector2d position = new Vector2d(); public Vector2d velocity = new Vector2d(); public Vector2d acceleration = new Vector2d(); @@ -33,13 +37,10 @@ public class GameObject implements Entity { public double gravity = 0; public Material material; public double mass = 1; - + public int life = -1; public List> behaviors = new ArrayList<>(); - protected Map attributes = new HashMap<>(); - private List debugData = new ArrayList<>(); - private int debug; public GameObject(String objectName) { @@ -66,6 +67,14 @@ public static int getIndex() { } public void update(long dt) { + if (life > -1) { + if (life - dt >= 0) { + life -= dt; + } else { + active = false; + life = -1; + } + } } public List getDebugInfo() { @@ -75,8 +84,8 @@ public List getDebugInfo() { debugInfo.add("vel:" + velocity.toString()); debugInfo.add("acc:" + acceleration.toString()); debugInfo.add("friction:" + material.dynFriction); - debugInfo.add("contact:" + getAttribute("touching",false)); - debugInfo.add("jumping:" + getAttribute("jumping",false)); + debugInfo.add("contact:" + getAttribute("touching", false)); + debugInfo.add("jumping:" + getAttribute("jumping", false)); return debugInfo; } @@ -181,6 +190,11 @@ public Map getAttributes() { return attributes; } + public GameObject setDuration(int ms) { + this.life = ms; + return this; + } + public enum GOType { POINT, RECTANGLE, CIRCLE, IMAGE, OTHER } diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/Render.java b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/Render.java index b8d54faa..92b2f000 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/Render.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/Render.java @@ -4,6 +4,7 @@ import fr.snapgames.fromclasstogame.core.config.Configuration; import fr.snapgames.fromclasstogame.core.entity.Camera; import fr.snapgames.fromclasstogame.core.entity.GameObject; +import fr.snapgames.fromclasstogame.core.gfx.renderer.DebugViewportGridRenderHelper; import fr.snapgames.fromclasstogame.core.gfx.renderer.GameObjectRenderHelper; import fr.snapgames.fromclasstogame.core.gfx.renderer.RenderHelper; import fr.snapgames.fromclasstogame.core.gfx.renderer.TextRenderHelper; @@ -28,10 +29,8 @@ public class Render extends System { private static final Logger logger = LoggerFactory.getLogger(Render.class); - - private BufferedImage buffer; private static int screenShotIndex = 0; - + private BufferedImage buffer; private Camera camera; private List objectsRelativeToCamera = new CopyOnWriteArrayList<>(); @@ -50,51 +49,47 @@ public int initialize(Configuration config) { setViewport(config.width, config.height); addRenderHelper(new GameObjectRenderHelper()); addRenderHelper(new TextRenderHelper()); + addRenderHelper(new DebugViewportGridRenderHelper()); return 0; } public void render() { Graphics2D g = this.buffer.createGraphics(); g.clearRect(0, 0, this.buffer.getWidth(), this.buffer.getHeight()); - g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + setRenderingHintsList(g); - if (camera != null) { - g.translate(-camera.position.x, -camera.position.y); - } - drawGrid(g, world); - for (GameObject go : objects) { - draw(g, go); - } - - if (camera != null) { - g.translate(camera.position.x, camera.position.y); - } - for (GameObject go : objectsRelativeToCamera) { - draw(g, go); - } + setCamera(g, -camera.position.x, -camera.position.y); + drawObjectList(g, objects); + setCamera(g, camera.position.x, camera.position.y); + drawObjectList(g, objectsRelativeToCamera); g.dispose(); if (renderScreenshot) { saveScreenshot(); renderScreenshot = false; } + + objects.stream().filter(o -> !o.active).forEach(o -> objects.remove(o)); + objectsRelativeToCamera.stream().filter(o -> !o.active).forEach(o -> objectsRelativeToCamera.remove(o)); } - private void drawGrid(Graphics2D g, World w) { - if (w != null && debug > 0) { - g.setColor(Color.BLUE); - for (int x = 0; x < w.width; x += 16) { - g.drawRect(x, 0, (int) 16, (int) w.height); - } - for (int y = 0; y < w.height; y += 16) { - if (y + 16 < w.height) { - g.drawRect(0, y, (int) w.width, 16); - } + private void setRenderingHintsList(Graphics2D g) { + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } + + private void setCamera(Graphics2D g, double v, double v2) { + if (camera != null) { + g.translate(v, v2); + } + } + + private void drawObjectList(Graphics2D g, List objects) { + for (GameObject go : objects) { + if (go.active) { + draw(g, go); } - g.setColor(debugColor); - g.drawRect(0, 0, (int) w.width, (int) w.height); } } diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/AbstractRenderHelper.java b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/AbstractRenderHelper.java index 7de464ed..9f60a7ec 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/AbstractRenderHelper.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/AbstractRenderHelper.java @@ -13,6 +13,9 @@ * @since 1.0.0 */ public class AbstractRenderHelper { + protected Color debugBackgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.7f); + protected Color debugFrontColor = Color.ORANGE; + protected Color debugBoxColor = Color.YELLOW; /** * Simple set active Font diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/DebugViewportGridRenderHelper.java b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/DebugViewportGridRenderHelper.java new file mode 100644 index 00000000..ebb8b09c --- /dev/null +++ b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/DebugViewportGridRenderHelper.java @@ -0,0 +1,34 @@ +package fr.snapgames.fromclasstogame.core.gfx.renderer; + +import fr.snapgames.fromclasstogame.core.entity.DebugViewportGrid; +import fr.snapgames.fromclasstogame.core.physic.World; + +import java.awt.*; + +public class DebugViewportGridRenderHelper extends AbstractRenderHelper implements RenderHelper { + + private Color gridColor = Color.BLUE; + + @Override + public String getType() { + return DebugViewportGrid.class.getName(); + } + + @Override + public void draw(Graphics2D g, DebugViewportGrid dvg) { + if (dvg.getWorld() != null && dvg.getDebug() > 0) { + World w = dvg.getWorld(); + g.setColor(gridColor); + for (int x = 0; x < w.width; x += dvg.gridX) { + g.drawRect(x, 0, dvg.gridX, (int) w.height); + } + for (int y = 0; y < w.height; y += dvg.gridY) { + if (y + dvg.gridY < w.height) { + g.drawRect(0, y, (int) w.width, dvg.gridY); + } + } + g.setColor(debugBoxColor); + g.drawRect(0, 0, (int) w.width, (int) w.height); + } + } +} diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/GameObjectRenderHelper.java b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/GameObjectRenderHelper.java index bbc41cd6..d1fa30e0 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/GameObjectRenderHelper.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/GameObjectRenderHelper.java @@ -9,9 +9,7 @@ public class GameObjectRenderHelper extends AbstractRenderHelper implements RenderHelper { private static final Logger logger = LoggerFactory.getLogger(GameObjectRenderHelper.class); - private Color debugBackgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.7f); - private Color debugFrontColor = Color.ORANGE; - private Color debugBoxColor = Color.YELLOW; + @Override public void draw(Graphics2D g, GameObject go) { @@ -51,7 +49,7 @@ private void drawDebugInfo(Graphics2D g, GameObject go) { setFontSize(g, 9); double offsetY = -40; double offsetX = go.width + 8; - int height = (int)((go.getDebugInfo().size()+2)*9); + int height = ((go.getDebugInfo().size()+2)*9); fillRect(g, go.position, 100, height, go.width + 1, offsetY - 12, debugBackgroundColor); setColor(g, debugFrontColor); int i = 0; diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/TextRenderHelper.java b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/TextRenderHelper.java index 5db3963d..ad38300a 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/TextRenderHelper.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/gfx/renderer/TextRenderHelper.java @@ -1,16 +1,17 @@ package fr.snapgames.fromclasstogame.core.gfx.renderer; -import java.awt.Graphics2D; +import java.awt.*; import fr.snapgames.fromclasstogame.core.entity.TextObject; -public class TextRenderHelper implements RenderHelper { +public class TextRenderHelper extends AbstractRenderHelper implements RenderHelper { + private Color shadowColor = new Color(0.2f,0.2f,0.2f,0.6f); @Override public void draw(Graphics2D g, TextObject to) { - g.setFont(to.font); - g.setColor(to.color); - g.drawString(to.text, (int) (to.position.x), (int) (to.position.y)); + drawTextBorder(g,2,to); + setColor(g, to.color); + drawText(g,to.text,to.position); } @Override diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/physic/PhysicEngine.java b/src/main/java/fr/snapgames/fromclasstogame/core/physic/PhysicEngine.java index 7d79444c..ca3ba7bc 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/physic/PhysicEngine.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/physic/PhysicEngine.java @@ -58,11 +58,11 @@ private void update(GameObject go, long dt) { // limit acceleration with GameObject threshold `maxHorizontalAcceleration` and `maxVerticalAcceleration` if (go.getAttributes().containsKey("maxHorizontalAcceleration")) { - double ax = (Double) go.getAttribute("maxHorizontalAcceleration",0); + double ax = (Double) go.getAttribute("maxHorizontalAcceleration", 0); go.acceleration.x = Math.abs(go.acceleration.x) > ax ? Math.signum(go.acceleration.x) * ax : go.acceleration.x; } if (go.getAttributes().containsKey("maxVerticalAcceleration")) { - double ay = (Double) go.getAttribute("maxVerticalAcceleration",0); + double ay = (Double) go.getAttribute("maxVerticalAcceleration", 0); go.acceleration.y = Math.abs(go.acceleration.y) > ay ? Math.signum(go.acceleration.y) * ay : go.acceleration.y; } @@ -71,25 +71,24 @@ private void update(GameObject go, long dt) { go.velocity = go.velocity.add(go.acceleration.multiply(dtCorrected)).multiply(friction); if (touching && Math.abs(go.acceleration.x) < 0.5 && Math.abs(go.acceleration.y) < 0.5) { - double dynFriction = dynFriction = go.material != null ? go.material.dynFriction : 1; + double dynFriction = go.material != null ? go.material.dynFriction : 1; go.velocity = go.velocity.multiply(dynFriction); } // limit velocity with GameObject threshold `maxHorizontalVelocity` and `maxVerticalVelocity` if (go.getAttributes().containsKey("maxHorizontalVelocity")) { - double dx = (Double) go.getAttribute("maxHorizontalVelocity",0); + double dx = (Double) go.getAttribute("maxHorizontalVelocity", 0); go.velocity.x = Math.abs(go.velocity.x) > dx ? Math.signum(go.velocity.x) * dx : go.velocity.x; } if (go.getAttributes().containsKey("maxVerticalVelocity")) { - double dy = (Double) go.getAttribute("maxVerticalVelocity",0); + double dy = (Double) go.getAttribute("maxVerticalVelocity", 0); go.velocity.y = Math.abs(go.velocity.y) > dy ? Math.signum(go.velocity.y) * dy : go.velocity.y; } // Compute position go.position.x += ceilMinMaxValue(go.velocity.x * dtCorrected, 0.1, world.maxVelocity); go.position.y += ceilMinMaxValue(go.velocity.y * dtCorrected, 0.1, world.maxVelocity); - // Update the Object itself - go.update(dt); + // test World space constrained verifyGameConstraint(go); // update Bounding box for this GameObject. @@ -97,6 +96,8 @@ private void update(GameObject go, long dt) { go.bbox.update(go); } } + // Update the Object itself + go.update(dt); } private double ceilValue(double x, double ceil) { diff --git a/src/main/java/fr/snapgames/fromclasstogame/core/physic/collision/BoundingBox.java b/src/main/java/fr/snapgames/fromclasstogame/core/physic/collision/BoundingBox.java index 9ab724d0..ed47617f 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/core/physic/collision/BoundingBox.java +++ b/src/main/java/fr/snapgames/fromclasstogame/core/physic/collision/BoundingBox.java @@ -6,12 +6,12 @@ public class BoundingBox { - BoundingBoxType type; - Vector2d position; - Box shape; - double diam1; - double diam2; - Vector2d[] points; + public BoundingBoxType type; + public Vector2d position; + public Box shape; + public double diam1; + public double diam2; + public Vector2d[] points; public void update(GameObject go) { position.x = go.position.x; @@ -41,7 +41,7 @@ public enum BoundingBoxType { POINT, RECTANGLE, CIRCLE, - ELLIPSE; + ELLIPSE } } diff --git a/src/main/java/fr/snapgames/fromclasstogame/demo/scenes/DemoScene.java b/src/main/java/fr/snapgames/fromclasstogame/demo/scenes/DemoScene.java index 8a195890..b5c16dcb 100644 --- a/src/main/java/fr/snapgames/fromclasstogame/demo/scenes/DemoScene.java +++ b/src/main/java/fr/snapgames/fromclasstogame/demo/scenes/DemoScene.java @@ -3,7 +3,9 @@ import fr.snapgames.fromclasstogame.core.Game; import fr.snapgames.fromclasstogame.core.behaviors.BasicParticleBehavior; import fr.snapgames.fromclasstogame.core.entity.Camera; +import fr.snapgames.fromclasstogame.core.entity.DebugViewportGrid; import fr.snapgames.fromclasstogame.core.entity.GameObject; +import fr.snapgames.fromclasstogame.core.entity.TextObject; import fr.snapgames.fromclasstogame.core.entity.particles.ParticleSystem; import fr.snapgames.fromclasstogame.core.exceptions.io.UnknownResource; import fr.snapgames.fromclasstogame.core.gfx.renderer.InventoryRenderHelper; @@ -81,7 +83,17 @@ public void initialize(Game g) { @Override public void create(Game g) throws UnknownResource { - g.setWorld(new World(800, 600)); + // Declare World playground + World world = new World(800, 600); + g.setWorld(world); + + // add Viewport Grid debug view + DebugViewportGrid dvg = new DebugViewportGrid("vpgrid", world, 32, 32); + dvg.setDebug(1); + dvg.setLayer(11); + dvg.setPriority(2); + add(dvg); + // add main character (player) Material m = DefaultMaterial.newMaterial("player", 0.25, 0.3, 0.80, 0.98); GameObject player = new GameObject("player", new Vector2d(160, 100)) @@ -158,6 +170,13 @@ public void create(Game g) throws UnknownResource { // shuffle `enemy_*`'s object's position and acceleration randomizeFilteredGameObject("enemy_"); + TextObject welcome = new TextObject("welcomeMsg", new Vector2d(40, 100)) + .setText("Welcome on Board"); + welcome.setDuration(2000).setLayer(0).setPriority(1).relativeToCamera(true); + + add(welcome); + + // Add the Debug switcher capability to this scene addBehavior(new DebugSwitcherBehavior()); @@ -172,7 +191,7 @@ private void generateEnemies(int nbEnemies) throws UnknownResource { .setColor(Color.ORANGE).setImage(ResourceManager.getImage("images/tiles01.png:orangeBall")) .setMaterial(DefaultMaterial.RUBBER.getMaterial()).setMass(Utils.rand(-8, 13)).setLayer(10) .setPriority(3) - .setSize(8,8); + .setSize(8, 8); randomizePosAndAccGameObject(e); add(e);