add animation support

This commit is contained in:
Quillraven
2025-05-27 21:49:25 +02:00
parent 3fe64f2393
commit c820ec7406
16 changed files with 410 additions and 87 deletions

View File

@@ -32,6 +32,10 @@ public class AssetService implements Disposable {
this.assetManager.load(atlasAsset.getPath(), TextureAtlas.class);
}
public TextureAtlas get(AtlasAsset atlasAsset) {
return this.assetManager.get(atlasAsset.getPath(), TextureAtlas.class);
}
public boolean update() {
return this.assetManager.update();
}

View File

@@ -0,0 +1,106 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.ComponentMapper;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Animation.PlayMode;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.github.com.quillraven.asset.AtlasAsset;
import io.github.com.quillraven.component.Facing.FacingDirection;
public class Animation2D implements Component {
public static final ComponentMapper<Animation2D> MAPPER = ComponentMapper.getFor(Animation2D.class);
private final AtlasAsset atlasAsset;
private final String atlasKey;
private AnimationType type;
private FacingDirection direction;
private PlayMode playMode;
private float speed;
private float stateTime;
private Animation<TextureRegion> animation;
private boolean dirty;
public Animation2D(AtlasAsset atlasAsset, String atlasKey, AnimationType type, PlayMode playMode, float speed) {
this.atlasAsset = atlasAsset;
this.atlasKey = atlasKey;
this.type = type;
this.playMode = playMode;
this.speed = speed;
this.stateTime = 0f;
this.animation = null;
this.dirty = true;
}
public void setAnimation(Animation<TextureRegion> animation, FacingDirection direction) {
this.animation = animation;
this.stateTime = 0f;
this.direction = direction;
this.dirty = false;
}
public FacingDirection getDirection() {
return direction;
}
public Animation<TextureRegion> getAnimation() {
return animation;
}
public AtlasAsset getAtlasAsset() {
return atlasAsset;
}
public String getAtlasKey() {
return atlasKey;
}
public void setType(AnimationType type) {
this.type = type;
this.dirty = true;
}
public AnimationType getType() {
return type;
}
public PlayMode getPlayMode() {
return playMode;
}
public float getSpeed() {
return speed;
}
public void setSpeed(float speed) {
this.speed = speed;
}
public void setPlayMode(PlayMode playMode) {
this.playMode = playMode;
}
public boolean isDirty() {
return dirty;
}
public float incAndGetStateTime(float deltaTime) {
this.stateTime += deltaTime * speed;
return this.stateTime;
}
public enum AnimationType {
IDLE,
WALK;
private final String atlasKey;
AnimationType() {
this.atlasKey = this.name().toLowerCase();
}
public String getAtlasKey() {
return atlasKey;
}
}
}

View File

@@ -0,0 +1,36 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.ComponentMapper;
public class Facing implements Component {
public static final ComponentMapper<Facing> MAPPER = ComponentMapper.getFor(Facing.class);
private FacingDirection direction;
public Facing(FacingDirection direction) {
this.direction = direction;
}
public FacingDirection getDirection() {
return direction;
}
public void setDirection(FacingDirection direction) {
this.direction = direction;
}
public enum FacingDirection {
UP, DOWN, LEFT, RIGHT;
private final String atlasKey;
FacingDirection() {
this.atlasKey = this.name().toLowerCase();
}
public String getAtlasKey() {
return atlasKey;
}
}
}

View File

@@ -8,7 +8,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class Graphic implements Component {
public static final ComponentMapper<Graphic> MAPPER = ComponentMapper.getFor(Graphic.class);
private final TextureRegion region;
private TextureRegion region;
private final Color color;
public Graphic(TextureRegion region, Color color) {
@@ -16,6 +16,10 @@ public class Graphic implements Component {
this.color = color;
}
public void setRegion(TextureRegion region) {
this.region = region;
}
public TextureRegion getRegion() {
return region;
}

View File

@@ -9,10 +9,11 @@ import com.badlogic.gdx.physics.box2d.World;
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.RenderSystem;
import io.github.com.quillraven.system.TiledServiceTestSystem;
import io.github.com.quillraven.system.TestSystem;
import io.github.com.quillraven.tiled.TiledAshleySpawner;
import io.github.com.quillraven.tiled.TiledService;
@@ -32,9 +33,10 @@ public class GameScreen extends ScreenAdapter {
this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld);
// add ECS systems
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 TiledServiceTestSystem(this.tiledService));
this.engine.addSystem(new TestSystem(this.tiledService));
this.engine.addSystem(new PhysicDebugRenderSystem(this.physicWorld, game.getCamera()));
}

View File

@@ -0,0 +1,76 @@
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.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import io.github.com.quillraven.asset.AssetService;
import io.github.com.quillraven.asset.AtlasAsset;
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.Graphic;
import java.util.HashMap;
import java.util.Map;
public class AnimationSystem extends IteratingSystem {
private static final float FRAME_DURATION = 1 / 8f;
private final AssetService assetService;
private final Map<CacheKey, Animation<TextureRegion>> animationCache;
public AnimationSystem(AssetService assetService) {
super(Family.all(Animation2D.class, Graphic.class, Facing.class).get());
this.assetService = assetService;
this.animationCache = new HashMap<>();
}
@Override
protected void processEntity(Entity entity, float deltaTime) {
Animation2D animation2D = Animation2D.MAPPER.get(entity);
FacingDirection facingDirection = Facing.MAPPER.get(entity).getDirection();
final float stateTime;
if (animation2D.isDirty() || facingDirection != animation2D.getDirection()) {
updateAnimation(animation2D, facingDirection);
stateTime = 0f;
} else {
stateTime = animation2D.incAndGetStateTime(deltaTime);
}
Animation<TextureRegion> animation = animation2D.getAnimation();
animation.setPlayMode(animation2D.getPlayMode());
TextureRegion keyFrame = animation.getKeyFrame(stateTime);
Graphic.MAPPER.get(entity).setRegion(keyFrame);
}
private void updateAnimation(Animation2D animation2D, FacingDirection direction) {
AtlasAsset atlasAsset = animation2D.getAtlasAsset();
String atlasKey = animation2D.getAtlasKey();
Animation2D.AnimationType type = animation2D.getType();
CacheKey cacheKey = new CacheKey(atlasAsset, atlasKey, type, direction);
Animation<TextureRegion> animation = animationCache.computeIfAbsent(cacheKey, key -> {
TextureAtlas textureAtlas = this.assetService.get(atlasAsset);
String combinedKey = atlasKey + "/" + type.getAtlasKey() + "_" + direction.getAtlasKey();
Array<AtlasRegion> regions = textureAtlas.findRegions(combinedKey);
if (regions.isEmpty()) {
throw new GdxRuntimeException("No regions found for " + key);
}
return new Animation<>(FRAME_DURATION, regions);
});
animation2D.setAnimation(animation, direction);
}
private record CacheKey(
AtlasAsset atlasAsset,
String atlasKey,
Animation2D.AnimationType type,
FacingDirection direction
) {
}
}

View File

@@ -0,0 +1,70 @@
package io.github.com.quillraven.system;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.EntitySystem;
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.tiled.TiledService;
public class TestSystem extends EntitySystem {
private final TiledService tiledService;
public TestSystem(TiledService tiledService) {
super();
this.tiledService = tiledService;
}
@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<Entity> entities = getEngine().getEntitiesFor(Family.all(Facing.class).get());
for (Entity entity : entities) {
Facing.MAPPER.get(entity).setDirection(FacingDirection.LEFT);
}
} else if (Gdx.input.isKeyJustPressed(Input.Keys.D)) {
ImmutableArray<Entity> entities = getEngine().getEntitiesFor(Family.all(Facing.class).get());
for (Entity entity : entities) {
Facing.MAPPER.get(entity).setDirection(FacingDirection.RIGHT);
}
} else if (Gdx.input.isKeyJustPressed(Input.Keys.S)) {
ImmutableArray<Entity> entities = getEngine().getEntitiesFor(Family.all(Animation2D.class).get());
for (Entity entity : entities) {
Facing.MAPPER.get(entity).setDirection(FacingDirection.DOWN);
Animation2D.MAPPER.get(entity).setType(Animation2D.AnimationType.IDLE);
}
} else if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) {
ImmutableArray<Entity> 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.E)) {
ImmutableArray<Entity> 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<Entity> entities = getEngine().getEntitiesFor(Family.all(Animation2D.class).get());
for (Entity entity : entities) {
Animation2D.MAPPER.get(entity).setPlayMode(Animation.PlayMode.LOOP_RANDOM);
}
}
}
}

View File

@@ -1,30 +0,0 @@
package io.github.com.quillraven.system;
import com.badlogic.ashley.core.EntitySystem;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.maps.tiled.TiledMap;
import io.github.com.quillraven.asset.MapAsset;
import io.github.com.quillraven.tiled.TiledService;
public class TiledServiceTestSystem extends EntitySystem {
private final TiledService tiledService;
public TiledServiceTestSystem(TiledService tiledService) {
super();
this.tiledService = tiledService;
}
@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);
}
}
}

View File

@@ -3,12 +3,15 @@ package io.github.com.quillraven.tiled;
import com.badlogic.ashley.core.Engine;
import com.badlogic.ashley.core.Entity;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FileTextureData;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.MapObjects;
import com.badlogic.gdx.maps.objects.RectangleMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.objects.TiledMapTileMapObject;
import com.badlogic.gdx.math.Rectangle;
@@ -19,6 +22,11 @@ import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.GdxRuntimeException;
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.Facing;
import io.github.com.quillraven.component.Facing.FacingDirection;
import io.github.com.quillraven.component.Graphic;
import io.github.com.quillraven.component.Physic;
import io.github.com.quillraven.component.Transform;
@@ -48,6 +56,9 @@ public class TiledAshleySpawner {
}
}
private void loadTileLayer(TiledMapTileLayer tileLayer) {
}
private void loadTriggerLayer(MapLayer triggerLayer) {
for (MapObject mapObject : triggerLayer.getObjects()) {
if (mapObject instanceof RectangleMapObject rectMapObj) {
@@ -70,9 +81,6 @@ public class TiledAshleySpawner {
}
}
private void loadTileLayer(TiledMapTileLayer tileLayer) {
}
private void loadObjectLayer(MapLayer objectLayer) {
for (MapObject mapObject : objectLayer.getObjects()) {
if (mapObject instanceof TiledMapTileMapObject tileMapObject) {
@@ -97,12 +105,29 @@ public class TiledAshleySpawner {
BodyDef.BodyType.DynamicBody,
Vector2.Zero,
entity);
addEntityAnimation(tileMapObject.getTile(), entity);
entity.add(new Facing(FacingDirection.DOWN));
entity.add(new Graphic(textureRegion, Color.WHITE.cpy()));
this.engine.addEntity(entity);
}
private void addEntityPhysic(MapObject mapObject, BodyDef.BodyType bodyType, Vector2 relativeTo, Entity entity) {
private void addEntityAnimation(TiledMapTile tile, Entity entity) {
String animationStr = tile.getProperties().get("animation", "", String.class);
if (animationStr.isBlank()) {
return;
}
AnimationType animationType = AnimationType.valueOf(animationStr);
String atlasAssetStr = tile.getProperties().get("atlasAsset", "", String.class);
AtlasAsset atlasAsset = AtlasAsset.valueOf(atlasAssetStr);
FileTextureData textureData = (FileTextureData) tile.getTextureRegion().getTexture().getTextureData();
String atlasKey = textureData.getFileHandle().nameWithoutExtension();
entity.add(new Animation2D(atlasAsset, atlasKey, animationType, Animation.PlayMode.LOOP, 1f));
}
private void addEntityPhysic(MapObject mapObject, @SuppressWarnings("SameParameterValue") BodyDef.BodyType bodyType, Vector2 relativeTo, Entity entity) {
if (tmpMapObjects.getCount() > 0) tmpMapObjects.remove(0);
tmpMapObjects.add(mapObject);