diff --git a/assets/maps/objects.tsx b/assets/maps/objects.tsx index 7d0532c..2442ce8 100644 --- a/assets/maps/objects.tsx +++ b/assets/maps/objects.tsx @@ -1,11 +1,11 @@ - + - + diff --git a/core/src/main/java/io/github/com/quillraven/component/Move.java b/core/src/main/java/io/github/com/quillraven/component/Move.java new file mode 100644 index 0000000..89d56c8 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/component/Move.java @@ -0,0 +1,25 @@ +package io.github.com.quillraven.component; + +import com.badlogic.ashley.core.Component; +import com.badlogic.ashley.core.ComponentMapper; +import com.badlogic.gdx.math.Vector2; + +public class Move implements Component { + public static final ComponentMapper MAPPER = ComponentMapper.getFor(Move.class); + + private float maxSpeed; + private final Vector2 direction; + + public Move(float maxSpeed) { + this.maxSpeed = maxSpeed; + this.direction = new Vector2(); + } + + public float getMaxSpeed() { + return maxSpeed; + } + + public Vector2 getDirection() { + return direction; + } +} diff --git a/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java b/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java index d59cbbf..a795d2a 100644 --- a/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java +++ b/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java @@ -10,8 +10,9 @@ import com.badlogic.gdx.utils.Disposable; import io.github.com.quillraven.GdxGame; import io.github.com.quillraven.asset.MapAsset; import io.github.com.quillraven.system.AnimationSystem; -import io.github.com.quillraven.system.CleanupSystem; import io.github.com.quillraven.system.PhysicDebugRenderSystem; +import io.github.com.quillraven.system.PhysicMoveSystem; +import io.github.com.quillraven.system.PhysicSystem; import io.github.com.quillraven.system.RenderSystem; import io.github.com.quillraven.system.TestSystem; import io.github.com.quillraven.tiled.TiledAshleySpawner; @@ -33,9 +34,10 @@ public class GameScreen extends ScreenAdapter { this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld); // add ECS systems + this.engine.addSystem(new PhysicMoveSystem()); + this.engine.addSystem(new PhysicSystem(physicWorld, 1 / 60f)); this.engine.addSystem(new AnimationSystem(game.getAssetService())); this.engine.addSystem(new RenderSystem(game.getBatch(), game.getViewport(), game.getCamera())); - this.engine.addSystem(new CleanupSystem()); this.engine.addSystem(new TestSystem(this.tiledService)); this.engine.addSystem(new PhysicDebugRenderSystem(this.physicWorld, game.getCamera())); } diff --git a/core/src/main/java/io/github/com/quillraven/system/CleanupSystem.java b/core/src/main/java/io/github/com/quillraven/system/CleanupSystem.java deleted file mode 100644 index 8cae5e1..0000000 --- a/core/src/main/java/io/github/com/quillraven/system/CleanupSystem.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.com.quillraven.system; - -import com.badlogic.ashley.core.Engine; -import com.badlogic.ashley.core.Entity; -import com.badlogic.ashley.core.EntityListener; -import com.badlogic.ashley.core.EntitySystem; -import com.badlogic.gdx.physics.box2d.Body; -import io.github.com.quillraven.component.Physic; - -public class CleanupSystem extends EntitySystem implements EntityListener { - - @Override - public void addedToEngine(Engine engine) { - super.addedToEngine(engine); - engine.addEntityListener(this); - } - - @Override - public void removedFromEngine(Engine engine) { - super.removedFromEngine(engine); - engine.removeEntityListener(this); - } - - @Override - public void entityAdded(Entity entity) { - } - - @Override - public void entityRemoved(Entity entity) { - // !!! Important !!! - // This does not work if the Physic component gets removed from an entity - // because the component is no longer accessible here. - // This ONLY works when an entity with a Physic component gets removed entirely from the engine. - Physic physic = Physic.MAPPER.get(entity); - if (physic != null) { - Body body = physic.getBody(); - body.getWorld().destroyBody(body); - } - } -} diff --git a/core/src/main/java/io/github/com/quillraven/system/PhysicMoveSystem.java b/core/src/main/java/io/github/com/quillraven/system/PhysicMoveSystem.java new file mode 100644 index 0000000..57b4da4 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/system/PhysicMoveSystem.java @@ -0,0 +1,34 @@ +package io.github.com.quillraven.system; + +import com.badlogic.ashley.core.Entity; +import com.badlogic.ashley.core.Family; +import com.badlogic.ashley.systems.IteratingSystem; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Body; +import io.github.com.quillraven.component.Facing; +import io.github.com.quillraven.component.Move; +import io.github.com.quillraven.component.Physic; + +public class PhysicMoveSystem extends IteratingSystem { + private static final Vector2 TMP_VEC2 = new Vector2(); + + public PhysicMoveSystem() { + super(Family.all(Physic.class, Move.class, Facing.class).get()); + } + + @Override + protected void processEntity(Entity entity, float deltaTime) { + Move move = Move.MAPPER.get(entity); + Physic physic = Physic.MAPPER.get(entity); + Body body = physic.getBody(); + if (move.getDirection().isZero()) { + // no direction given -> stop movement + body.setLinearVelocity(0f, 0f); + return; + } + + float maxSpeed = move.getMaxSpeed(); + TMP_VEC2.set(move.getDirection()).nor(); + body.setLinearVelocity(maxSpeed * TMP_VEC2.x, maxSpeed * TMP_VEC2.y); + } +} diff --git a/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java b/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java new file mode 100644 index 0000000..a749aa9 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/system/PhysicSystem.java @@ -0,0 +1,89 @@ +package io.github.com.quillraven.system; + +import com.badlogic.ashley.core.Engine; +import com.badlogic.ashley.core.Entity; +import com.badlogic.ashley.core.EntityListener; +import com.badlogic.ashley.core.Family; +import com.badlogic.ashley.systems.IteratingSystem; +import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.physics.box2d.Body; +import com.badlogic.gdx.physics.box2d.World; +import io.github.com.quillraven.component.Physic; +import io.github.com.quillraven.component.Transform; + +public class PhysicSystem extends IteratingSystem implements EntityListener { + + private final World world; + private final float interval; + private float accumulator; + + public PhysicSystem(World world, float interval) { + super(Family.all(Physic.class, Transform.class).get()); + this.world = world; + this.interval = interval; + this.accumulator = 0f; + } + + @Override + public void addedToEngine(Engine engine) { + super.addedToEngine(engine); + engine.addEntityListener(getFamily(), this); + } + + @Override + public void removedFromEngine(Engine engine) { + super.removedFromEngine(engine); + engine.removeEntityListener(this); + } + + @Override + public void update(float deltaTime) { + this.accumulator += deltaTime; + + while (this.accumulator >= this.interval) { + this.accumulator -= this.interval; + super.update(interval); + this.world.step(interval, 6, 2); + } + world.clearForces(); + + float alpha = this.accumulator / this.interval; + for (int i = 0; i < getEntities().size(); ++i) { + this.interpolateEntity(getEntities().get(i), alpha); + } + } + + @Override + protected void processEntity(Entity entity, float deltaTime) { + Physic physic = Physic.MAPPER.get(entity); + physic.getPrevPosition().set(physic.getBody().getPosition()); + } + + private void interpolateEntity(Entity entity, float alpha) { + Transform transform = Transform.MAPPER.get(entity); + Physic physic = Physic.MAPPER.get(entity); + + transform.getPosition().set( + MathUtils.lerp(physic.getPrevPosition().x, physic.getBody().getPosition().x, alpha), + MathUtils.lerp(physic.getPrevPosition().y, physic.getBody().getPosition().y, alpha) + ); + } + + @Override + public void entityAdded(Entity entity) { + } + + @Override + public void entityRemoved(Entity entity) { + // !!! Important !!! + // This does not work if the Physic component gets removed from an entity + // because the component is no longer accessible here. + // This ONLY works when an entity with a Physic component gets removed entirely from the engine. + Physic physic = Physic.MAPPER.get(entity); + if (physic != null) { + Body body = physic.getBody(); + body.getWorld().destroyBody(body); + } + } + +} diff --git a/core/src/main/java/io/github/com/quillraven/system/TestSystem.java b/core/src/main/java/io/github/com/quillraven/system/TestSystem.java index 80f7a55..55452fd 100644 --- a/core/src/main/java/io/github/com/quillraven/system/TestSystem.java +++ b/core/src/main/java/io/github/com/quillraven/system/TestSystem.java @@ -6,12 +6,7 @@ import com.badlogic.ashley.core.Family; import com.badlogic.ashley.utils.ImmutableArray; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; -import com.badlogic.gdx.graphics.g2d.Animation; -import com.badlogic.gdx.maps.tiled.TiledMap; -import io.github.com.quillraven.asset.MapAsset; -import io.github.com.quillraven.component.Animation2D; -import io.github.com.quillraven.component.Facing; -import io.github.com.quillraven.component.Facing.FacingDirection; +import io.github.com.quillraven.component.Move; import io.github.com.quillraven.tiled.TiledService; public class TestSystem extends EntitySystem { @@ -24,46 +19,30 @@ public class TestSystem extends EntitySystem { @Override public void update(float deltaTime) { - if (Gdx.input.isKeyJustPressed(Input.Keys.X)) { - Gdx.app.debug("TiledServiceTestSystem", "Setting map to MAIN"); - TiledMap tiledMap = tiledService.loadMap(MapAsset.MAIN); - tiledService.setMap(tiledMap); - } else if (Gdx.input.isKeyJustPressed(Input.Keys.C)) { - Gdx.app.debug("TiledServiceTestSystem", "Setting map to SECOND"); - TiledMap tiledMap = tiledService.loadMap(MapAsset.SECOND); - tiledService.setMap(tiledMap); - } else if (Gdx.input.isKeyJustPressed(Input.Keys.A)) { - ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Facing.class).get()); + if (Gdx.input.isKeyJustPressed(Input.Keys.W)) { + ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Move.class).get()); for (Entity entity : entities) { - Facing.MAPPER.get(entity).setDirection(FacingDirection.LEFT); - } - } else if (Gdx.input.isKeyJustPressed(Input.Keys.D)) { - ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Facing.class).get()); - for (Entity entity : entities) { - Facing.MAPPER.get(entity).setDirection(FacingDirection.RIGHT); + Move.MAPPER.get(entity).getDirection().set(0f, 1f); } } else if (Gdx.input.isKeyJustPressed(Input.Keys.S)) { - ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Animation2D.class).get()); + ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Move.class).get()); for (Entity entity : entities) { - Facing.MAPPER.get(entity).setDirection(FacingDirection.DOWN); - Animation2D.MAPPER.get(entity).setType(Animation2D.AnimationType.IDLE); + Move.MAPPER.get(entity).getDirection().set(0f, -1f); + } + } else if (Gdx.input.isKeyJustPressed(Input.Keys.A)) { + ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Move.class).get()); + for (Entity entity : entities) { + Move.MAPPER.get(entity).getDirection().set(-1f, 0f); + } + } else if (Gdx.input.isKeyJustPressed(Input.Keys.D)) { + ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Move.class).get()); + for (Entity entity : entities) { + Move.MAPPER.get(entity).getDirection().set(1f, 0f); } } else if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) { - ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Animation2D.class).get()); + ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Move.class).get()); for (Entity entity : entities) { - Animation2D animation2D = Animation2D.MAPPER.get(entity); - animation2D.setSpeed(animation2D.getSpeed() * 1.2f); - } - } else if (Gdx.input.isKeyJustPressed(Input.Keys.E)) { - ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Animation2D.class).get()); - for (Entity entity : entities) { - Animation2D animation2D = Animation2D.MAPPER.get(entity); - animation2D.setSpeed(animation2D.getSpeed() / 1.2f); - } - } else if (Gdx.input.isKeyJustPressed(Input.Keys.W)) { - ImmutableArray entities = getEngine().getEntitiesFor(Family.all(Animation2D.class).get()); - for (Entity entity : entities) { - Animation2D.MAPPER.get(entity).setPlayMode(Animation.PlayMode.LOOP_RANDOM); + Move.MAPPER.get(entity).getDirection().setZero(); } } } diff --git a/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java b/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java index 20fd7d6..01fa079 100644 --- a/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java +++ b/core/src/main/java/io/github/com/quillraven/tiled/TiledAshleySpawner.java @@ -29,6 +29,7 @@ import io.github.com.quillraven.component.Animation2D.AnimationType; import io.github.com.quillraven.component.Facing; import io.github.com.quillraven.component.Facing.FacingDirection; import io.github.com.quillraven.component.Graphic; +import io.github.com.quillraven.component.Move; import io.github.com.quillraven.component.Physic; import io.github.com.quillraven.component.Transform; @@ -110,25 +111,34 @@ public class TiledAshleySpawner { private void spawnEntityOf(TiledMapTileMapObject tileMapObject) { Entity entity = this.engine.createEntity(); + TiledMapTile tile = tileMapObject.getTile(); + TextureRegion textureRegion = tile.getTextureRegion(); - TextureRegion textureRegion = tileMapObject.getTile().getTextureRegion(); addEntityTransform( tileMapObject.getX(), tileMapObject.getY(), textureRegion.getRegionWidth(), textureRegion.getRegionHeight(), tileMapObject.getScaleX(), tileMapObject.getScaleY(), entity); addEntityPhysic( - tileMapObject.getTile().getObjects(), + tile.getObjects(), BodyType.DynamicBody, Vector2.Zero, entity); - addEntityAnimation(tileMapObject.getTile(), entity); + addEntityAnimation(tile, entity); + addEntityMove(tile, entity); entity.add(new Facing(FacingDirection.DOWN)); entity.add(new Graphic(textureRegion, Color.WHITE.cpy())); this.engine.addEntity(entity); } + private void addEntityMove(TiledMapTile tile, Entity entity) { + float speed = tile.getProperties().get("speed", 0f, Float.class); + if (speed == 0f) return; + + entity.add(new Move(speed)); + } + private void addEntityAnimation(TiledMapTile tile, Entity entity) { String animationStr = tile.getProperties().get("animation", "", String.class); if (animationStr.isBlank()) { @@ -179,7 +189,7 @@ public class TiledAshleySpawner { Body body = this.physicWorld.createBody(bodyDef); body.setUserData(userData); for (MapObject object : mapObjects) { - FixtureDef fixtureDef = TiledPhysics.fixtureDefOfMapObject(object, scaling, relativeTo); + FixtureDef fixtureDef = TiledPhysics.fixtureDefOf(object, scaling, relativeTo); body.createFixture(fixtureDef); fixtureDef.shape.dispose(); } diff --git a/core/src/main/java/io/github/com/quillraven/tiled/TiledPhysics.java b/core/src/main/java/io/github/com/quillraven/tiled/TiledPhysics.java index f1d477a..40572b7 100644 --- a/core/src/main/java/io/github/com/quillraven/tiled/TiledPhysics.java +++ b/core/src/main/java/io/github/com/quillraven/tiled/TiledPhysics.java @@ -24,7 +24,7 @@ public final class TiledPhysics { // relativeTo is necessary for map objects that are directly placed on a layer (like rectangles for trigger). // Their x/y is equal to the position of the object, but we need it relative to 0,0 like it // is in the collision editor of a tile. - public static FixtureDef fixtureDefOfMapObject(MapObject mapObject, Vector2 scaling, Vector2 relativeTo) { + public static FixtureDef fixtureDefOf(MapObject mapObject, Vector2 scaling, Vector2 relativeTo) { if (mapObject instanceof RectangleMapObject rectMapObj) { return rectangleFixtureDef(rectMapObj, scaling, relativeTo); } else if (mapObject instanceof EllipseMapObject ellipseMapObj) { diff --git a/lwjgl3/src/main/java/io/github/com/quillraven/lwjgl3/Lwjgl3Launcher.java b/lwjgl3/src/main/java/io/github/com/quillraven/lwjgl3/Lwjgl3Launcher.java index 9bcb5aa..94f11c6 100644 --- a/lwjgl3/src/main/java/io/github/com/quillraven/lwjgl3/Lwjgl3Launcher.java +++ b/lwjgl3/src/main/java/io/github/com/quillraven/lwjgl3/Lwjgl3Launcher.java @@ -20,19 +20,19 @@ public class Lwjgl3Launcher { private static Lwjgl3ApplicationConfiguration getDefaultConfiguration() { Lwjgl3ApplicationConfiguration configuration = new Lwjgl3ApplicationConfiguration(); configuration.setTitle("mystictutorial"); - //// Vsync limits the frames per second to what your hardware can display, and helps eliminate - //// screen tearing. This setting doesn't always work on Linux, so the line after is a safeguard. + // Vsync limits the frames per second to what your hardware can display, and helps eliminate + // screen tearing. This setting doesn't always work on Linux, so the line after is a safeguard. configuration.useVsync(true); - //// Limits FPS to the refresh rate of the currently active monitor, plus 1 to try to match fractional - //// refresh rates. The Vsync setting above should limit the actual FPS to match the monitor. + // Limits FPS to the refresh rate of the currently active monitor, plus 1 to try to match fractional + // refresh rates. The Vsync setting above should limit the actual FPS to match the monitor. configuration.setForegroundFPS(Lwjgl3ApplicationConfiguration.getDisplayMode().refreshRate + 1); - //// If you remove the above line and set Vsync to false, you can get unlimited FPS, which can be - //// useful for testing performance, but can also be very stressful to some hardware. - //// You may also need to configure GPU drivers to fully disable Vsync; this can cause screen tearing. + // If you remove the above line and set Vsync to false, you can get unlimited FPS, which can be + // useful for testing performance, but can also be very stressful to some hardware. + // You may also need to configure GPU drivers to fully disable Vsync; this can cause screen tearing. configuration.setWindowedMode((int) (GdxGame.WORLD_WIDTH * 70), (int) (GdxGame.WORLD_HEIGHT * 70)); - //// You can change these files; they are in lwjgl3/src/main/resources/ . - //// They can also be loaded from the root of assets/ . + // You can change these files; they are in lwjgl3/src/main/resources/ . + // They can also be loaded from the root of assets/ . configuration.setWindowIcon("libgdx128.png", "libgdx64.png", "libgdx32.png", "libgdx16.png"); return configuration; }