add camera system and optimize y sorting for rendering

This commit is contained in:
Quillraven
2025-05-30 00:18:12 +02:00
parent dc905f9c07
commit bcbac0fa90
14 changed files with 272 additions and 93 deletions

View File

@@ -20,8 +20,8 @@ import io.github.com.quillraven.screen.LoadingScreen;
import java.util.HashMap;
public class GdxGame extends Game {
public static final float WORLD_HEIGHT = 11f;
public static final float WORLD_WIDTH = 6f;
public static final float WORLD_HEIGHT = 9f;
public static final float WORLD_WIDTH = 16f;
public static final float UNIT_SCALE = 1f / 16f;
private Batch batch;

View File

@@ -1,8 +1,7 @@
package io.github.com.quillraven.asset;
public enum MapAsset {
MAIN("mainmap.tmx"),
SECOND("secondmap.tmx");
MAIN("mainmap.tmx");
private final String path;

View File

@@ -0,0 +1,6 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
public class CameraFollow implements Component {
}

View File

@@ -11,20 +11,23 @@ public class Transform implements Component, Comparable<Transform> {
private final int z;
private final Vector2 size;
private final Vector2 scaling;
private final float rotationDeg;
private float rotationDeg;
private float sortOffsetY;
public Transform(
Vector2 position,
int z,
Vector2 size,
Vector2 scaling,
float rotationDeg
float rotationDeg,
float sortOffsetY
) {
this.position = position;
this.z = z;
this.size = size;
this.scaling = scaling;
this.rotationDeg = rotationDeg;
this.sortOffsetY = sortOffsetY;
}
@Override
@@ -32,8 +35,8 @@ public class Transform implements Component, Comparable<Transform> {
if (this.z != other.z) {
return Float.compare(this.z, other.z);
}
if (this.position.y != other.position.y) {
return Float.compare(other.position.y, this.position.y);
if (this.position.y + this.sortOffsetY != other.position.y + other.sortOffsetY) {
return Float.compare(other.position.y + other.sortOffsetY, this.position.y + this.sortOffsetY);
}
return Float.compare(this.position.x, other.position.x);
}

View File

@@ -16,6 +16,7 @@ import io.github.com.quillraven.component.Controller;
import io.github.com.quillraven.input.GameControllerState;
import io.github.com.quillraven.input.KeyboardController;
import io.github.com.quillraven.system.AnimationSystem;
import io.github.com.quillraven.system.CameraSystem;
import io.github.com.quillraven.system.FacingSystem;
import io.github.com.quillraven.system.FsmSystem;
import io.github.com.quillraven.system.PhysicDebugRenderSystem;
@@ -48,6 +49,7 @@ public class GameScreen extends ScreenAdapter {
this.engine.addSystem(new FacingSystem());
this.engine.addSystem(new FsmSystem());
this.engine.addSystem(new AnimationSystem(game.getAssetService()));
this.engine.addSystem(new CameraSystem(game.getCamera()));
this.engine.addSystem(new RenderSystem(game.getBatch(), game.getViewport(), game.getCamera()));
this.engine.addSystem(new PhysicDebugRenderSystem(this.physicWorld, game.getCamera()));
}
@@ -61,7 +63,8 @@ public class GameScreen extends ScreenAdapter {
Consumer<TiledMap> renderConsumer = this.engine.getSystem(RenderSystem.class)::setMap;
Consumer<TiledMap> ashleySpawnerConsumer = this.tiledAshleySpawner::loadMapObjects;
this.tiledService.setMapChangeConsumer(renderConsumer.andThen(ashleySpawnerConsumer));
Consumer<TiledMap> cameraConsumer = this.engine.getSystem(CameraSystem.class)::setMap;
this.tiledService.setMapChangeConsumer(renderConsumer.andThen(ashleySpawnerConsumer).andThen(cameraConsumer));
TiledMap startMap = this.tiledService.loadMap(MapAsset.MAIN);
this.tiledService.setMap(startMap);

View File

@@ -0,0 +1,78 @@
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.graphics.Camera;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.component.CameraFollow;
import io.github.com.quillraven.component.Transform;
public class CameraSystem extends IteratingSystem {
private static final float CAM_OFFSET_Y = 1f; // make the camera look up 'X' additional tiles
private final Camera camera;
private final float smoothingFactor;
private final Vector2 targetPosition;
private float mapW;
private float mapH;
public CameraSystem(Camera camera) {
super(Family.all(CameraFollow.class, Transform.class).get());
this.camera = camera;
this.smoothingFactor = 4f; // lower value = slower camera follow
this.targetPosition = new Vector2();
}
@Override
protected void processEntity(Entity entity, float deltaTime) {
Transform transform = Transform.MAPPER.get(entity);
calcTargetPosition(transform.getPosition());
// Apply smoothing using linear interpolation (LERP)
float progress = smoothingFactor * deltaTime;
float smoothedX = MathUtils.lerp(camera.position.x, this.targetPosition.x, progress);
float smoothedY = MathUtils.lerp(camera.position.y, this.targetPosition.y, progress);
camera.position.set(smoothedX, smoothedY, camera.position.z);
}
private void calcTargetPosition(Vector2 entityPosition) {
// Keep the target position within map boundaries
float targetX = entityPosition.x;
float targetY = entityPosition.y + CAM_OFFSET_Y;
float camHalfW = camera.viewportWidth * 0.5f;
if (mapW > camHalfW) {
float min = Math.min(camHalfW, mapW - camHalfW);
float max = Math.max(camHalfW, mapW - camHalfW);
targetX = MathUtils.clamp(targetX, min, max);
}
float camHalfH = camera.viewportHeight * 0.5f;
if (mapH > camHalfH) {
float min = Math.min(camHalfH, mapH - camHalfH);
float max = Math.max(camHalfH, mapH - camHalfH);
targetY = MathUtils.clamp(targetY, min, max);
}
this.targetPosition.set(targetX, targetY);
}
public void setMap(TiledMap tiledMap) {
int width = tiledMap.getProperties().get("width", 0, Integer.class);
int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class);
int height = tiledMap.getProperties().get("height", 0, Integer.class);
int tileH = tiledMap.getProperties().get("tileheight", 0, Integer.class);
mapW = width * tileW * GdxGame.UNIT_SCALE;
mapH = height * tileH * GdxGame.UNIT_SCALE;
Entity camEntity = getEntities().first();
if (camEntity == null) {
return;
}
Transform transform = Transform.MAPPER.get(camEntity);
calcTargetPosition(transform.getPosition());
camera.position.set(this.targetPosition.x, this.targetPosition.y, camera.position.z);
}
}

View File

@@ -26,6 +26,7 @@ import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.asset.AtlasAsset;
import io.github.com.quillraven.component.Animation2D;
import io.github.com.quillraven.component.Animation2D.AnimationType;
import io.github.com.quillraven.component.CameraFollow;
import io.github.com.quillraven.component.Controller;
import io.github.com.quillraven.component.Facing;
import io.github.com.quillraven.component.Facing.FacingDirection;
@@ -88,6 +89,7 @@ public class TiledAshleySpawner {
rect.getX(), rect.getY(),
rect.getWidth(), rect.getHeight(),
1f, 1f,
0,
entity);
addEntityPhysic(
rectMapObj,
@@ -116,11 +118,14 @@ public class TiledAshleySpawner {
TiledMapTile tile = tileMapObject.getTile();
TextureRegion textureRegion = tile.getTextureRegion();
String classType = tile.getProperties().get("type", "", String.class);
float sortOffsetY = tile.getProperties().get("sortOffsetY", 0, Integer.class);
sortOffsetY *= GdxGame.UNIT_SCALE;
addEntityTransform(
tileMapObject.getX(), tileMapObject.getY(),
textureRegion.getRegionWidth(), textureRegion.getRegionHeight(),
tileMapObject.getScaleX(), tileMapObject.getScaleY(),
sortOffsetY,
entity);
addEntityPhysic(
tile.getObjects(),
@@ -130,6 +135,7 @@ public class TiledAshleySpawner {
addEntityAnimation(tile, entity);
addEntityMove(tile, entity);
addEntityController(tileMapObject, entity);
addEntityCameraFollow(tileMapObject, entity);
entity.add(new Facing(FacingDirection.DOWN));
entity.add(new Fsm(entity));
entity.add(new Graphic(textureRegion, Color.WHITE.cpy()));
@@ -137,6 +143,13 @@ public class TiledAshleySpawner {
this.engine.addEntity(entity);
}
private void addEntityCameraFollow(TiledMapTileMapObject tileMapObject, Entity entity) {
boolean cameraFollow = tileMapObject.getProperties().get("camFollow", false, Boolean.class);
if (!cameraFollow) return;
entity.add(new CameraFollow());
}
private void addEntityController(TiledMapTileMapObject tileMapObject, Entity entity) {
boolean controller = tileMapObject.getProperties().get("controller", false, Boolean.class);
if (!controller) return;
@@ -212,6 +225,7 @@ public class TiledAshleySpawner {
float x, float y,
float w, float h,
float scaleX, float scaleY,
float sortOffsetY,
Entity entity
) {
Vector2 position = new Vector2(x, y);
@@ -221,7 +235,7 @@ public class TiledAshleySpawner {
position.scl(GdxGame.UNIT_SCALE);
size.scl(GdxGame.UNIT_SCALE);
entity.add(new Transform(position, 0, size, scaling, 0f));
entity.add(new Transform(position, 0, size, scaling, 0f, sortOffsetY));
}
}