Create animation scaffolding in :engine

This commit is contained in:
2021-03-20 00:03:00 +01:00
parent ef7a6b6ab0
commit 82d2bef569
11 changed files with 239 additions and 24 deletions

View File

@@ -0,0 +1,33 @@
package com.bartlomiejpluta.base.api.game.animation;
import com.bartlomiejpluta.base.api.game.entity.Direction;
import com.bartlomiejpluta.base.api.game.entity.Movement;
import com.bartlomiejpluta.base.api.game.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.internal.logic.Updatable;
import com.bartlomiejpluta.base.api.internal.object.Placeable;
import com.bartlomiejpluta.base.api.internal.render.Renderable;
import org.joml.Vector2ic;
public interface Animation extends Placeable, Renderable, Updatable {
void setStepSize(float x, float y);
Vector2ic getCoordinates();
void setCoordinates(Vector2ic coordinates);
void setCoordinates(int x, int y);
Movement prepareMovement(Direction direction);
Movement getMovement();
void setSpeed(float speed);
void setAnimationSpeed(float speed);
boolean isMoving();
void onAdd(ObjectLayer layer);
void onRemove(ObjectLayer layer);
}

View File

@@ -10,6 +10,7 @@ import com.bartlomiejpluta.base.engine.gui.xml.inflater.Inflater;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.project.serial.ProjectDeserializer;
import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader;
import com.bartlomiejpluta.base.engine.world.animation.manager.AnimationManager;
import com.bartlomiejpluta.base.engine.world.entity.manager.EntityManager;
import com.bartlomiejpluta.base.engine.world.entity.manager.EntitySetManager;
import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager;
@@ -34,6 +35,7 @@ public class DefaultContextManager implements ContextManager {
private final EntitySetManager entitySetManager;
private final FontManager fontManager;
private final EntityManager entityManager;
private final AnimationManager animationManager;
private final ClassLoader classLoader;
private final Inflater inflater;
private final WidgetDefinitionManager widgetDefinitionManager;
@@ -50,6 +52,7 @@ public class DefaultContextManager implements ContextManager {
project.getMapAssets().forEach(mapManager::registerAsset);
project.getImageAssets().forEach(imageManager::registerAsset);
project.getEntitySetAssets().forEach(entitySetManager::registerAsset);
project.getAnimationAssets().forEach(animationManager::registerAsset);
project.getFontAssets().forEach(fontManager::registerAsset);
project.getWidgetDefinitionAssets().forEach(widgetDefinitionManager::registerAsset);

View File

@@ -2,18 +2,19 @@ package com.bartlomiejpluta.base.engine.project.model;
import com.bartlomiejpluta.base.engine.gui.asset.FontAsset;
import com.bartlomiejpluta.base.engine.gui.asset.WidgetDefinitionAsset;
import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset;
import com.bartlomiejpluta.base.engine.world.entity.asset.EntitySetAsset;
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
import com.bartlomiejpluta.base.engine.world.map.asset.GameMapAsset;
import com.bartlomiejpluta.base.engine.world.tileset.asset.TileSetAsset;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.List;
@Getter
@RequiredArgsConstructor
@Builder
public class Project {
@NonNull
@@ -34,6 +35,9 @@ public class Project {
@NonNull
private final List<EntitySetAsset> entitySetAssets;
@NonNull
private final List<AnimationAsset> animationAssets;
@NonNull
private final List<FontAsset> fontAssets;

View File

@@ -3,6 +3,7 @@ package com.bartlomiejpluta.base.engine.project.serial;
import com.bartlomiejpluta.base.engine.gui.asset.FontAsset;
import com.bartlomiejpluta.base.engine.gui.asset.WidgetDefinitionAsset;
import com.bartlomiejpluta.base.engine.project.model.Project;
import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset;
import com.bartlomiejpluta.base.engine.world.entity.asset.EntitySetAsset;
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
import com.bartlomiejpluta.base.engine.world.map.asset.GameMapAsset;
@@ -20,16 +21,18 @@ public class ProtobufProjectDeserializer extends ProjectDeserializer {
@Override
protected Project parse(InputStream input) throws Exception {
var proto = ProjectProto.Project.parseFrom(input);
var name = proto.getName();
var runner = proto.getRunner();
var tileSetAssets = proto.getTileSetsList().stream().map(this::parseTileSetAsset).collect(toList());
var mapAssets = proto.getMapsList().stream().map(this::parseGameMapAsset).collect(toList());
var imageAssets = proto.getImagesList().stream().map(this::parseImageAsset).collect(toList());
var entitySetAssets = proto.getEntitySetsList().stream().map(this::parseEntitySetAsset).collect(toList());
var fontAssets = proto.getFontsList().stream().map(this::parseFontAsset).collect(toList());
var widgetAssets = proto.getWidgetsList().stream().map(this::parseWidgetAsset).collect(toList());
return new Project(name, runner, tileSetAssets, mapAssets, imageAssets, entitySetAssets, fontAssets, widgetAssets);
return Project.builder()
.name(proto.getName())
.runner(proto.getRunner())
.tileSetAssets(proto.getTileSetsList().stream().map(this::parseTileSetAsset).collect(toList()))
.mapAssets(proto.getMapsList().stream().map(this::parseGameMapAsset).collect(toList()))
.imageAssets(proto.getImagesList().stream().map(this::parseImageAsset).collect(toList()))
.entitySetAssets(proto.getEntitySetsList().stream().map(this::parseEntitySetAsset).collect(toList()))
.animationAssets(proto.getAnimationsList().stream().map(this::parseAnimationAsset).collect(toList()))
.fontAssets(proto.getFontsList().stream().map(this::parseFontAsset).collect(toList()))
.widgetDefinitionAssets(proto.getWidgetsList().stream().map(this::parseWidgetAsset).collect(toList()))
.build();
}
private TileSetAsset parseTileSetAsset(ProjectProto.TileSetAsset proto) {
@@ -55,4 +58,8 @@ public class ProtobufProjectDeserializer extends ProjectDeserializer {
private WidgetDefinitionAsset parseWidgetAsset(ProjectProto.WidgetAsset proto) {
return new WidgetDefinitionAsset(proto.getUid(), proto.getSource());
}
private AnimationAsset parseAnimationAsset(ProjectProto.AnimationAsset proto) {
return new AnimationAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns());
}
}

View File

@@ -0,0 +1,23 @@
package com.bartlomiejpluta.base.engine.world.animation.asset;
import com.bartlomiejpluta.base.engine.common.asset.Asset;
import lombok.Getter;
import lombok.NonNull;
@Getter
public class AnimationAsset extends Asset {
private final int rows;
private final int columns;
private final String framesSignature;
public AnimationAsset(@NonNull String uid, @NonNull String source, int rows, int columns) {
super(uid, source);
this.rows = rows;
this.columns = columns;
this.framesSignature = String.format("%dx%d", rows, columns);
}
public String framesSignature() {
return framesSignature;
}
}

View File

@@ -0,0 +1,8 @@
package com.bartlomiejpluta.base.engine.world.animation.manager;
import com.bartlomiejpluta.base.api.game.animation.Animation;
import com.bartlomiejpluta.base.engine.common.manager.AssetManager;
import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset;
public interface AnimationManager extends AssetManager<AnimationAsset, Animation> {
}

View File

@@ -0,0 +1,77 @@
package com.bartlomiejpluta.base.engine.world.animation.manager;
import com.bartlomiejpluta.base.api.game.animation.Animation;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset;
import com.bartlomiejpluta.base.engine.world.animation.model.DefaultAnimation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joml.Vector2f;
import org.joml.Vector2fc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultAnimationManager implements AnimationManager {
private final MeshManager meshManager;
private final TextureManager textureManager;
private final Map<String, AnimationAsset> assets = new HashMap<>();
private final Map<String, Vector2fc[]> frames = new HashMap<>();
private final ProjectConfiguration configuration;
@Override
public void registerAsset(AnimationAsset asset) {
log.info("Registering [{}] animation asset under UID: [{}]", asset.getSource(), asset.getUid());
assets.put(asset.getUid(), asset);
}
@Override
public Animation loadObject(String uid) {
var asset = assets.get(uid);
if (asset == null) {
throw new AppException("The entity set asset with UID: [%s] does not exist", uid);
}
var animationFrames = frames.computeIfAbsent(asset.framesSignature(),
signature -> createFrames(asset.getRows(), asset.getColumns())
);
var source = configuration.projectFile("animations", asset.getSource());
var texture = textureManager.loadTexture(source, asset.getRows(), asset.getColumns());
var material = Material.textured(texture);
var mesh = buildMesh(material, asset.getRows(), asset.getColumns());
return new DefaultAnimation(mesh, material, animationFrames);
}
private Vector2fc[] createFrames(int rows, int columns) {
var frames = new Vector2fc[rows * columns];
for (int row = 0; row < rows; ++row) {
for (int column = 0; column < columns; ++column) {
frames[row * columns + column] = new Vector2f(column, row);
}
}
return frames;
}
private Mesh buildMesh(Material material, int rows, int columns) {
var texture = material.getTexture();
var spriteWidth = texture.getWidth() / columns;
var spriteHeight = texture.getHeight() / rows;
return meshManager.createQuad(spriteWidth, spriteHeight, spriteWidth / 2f, spriteHeight * 0.9f);
}
}

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.engine.world.animation;
package com.bartlomiejpluta.base.engine.world.animation.model;
import com.bartlomiejpluta.base.api.game.camera.Camera;
import com.bartlomiejpluta.base.api.game.screen.Screen;

View File

@@ -0,0 +1,60 @@
package com.bartlomiejpluta.base.engine.world.animation.model;
import com.bartlomiejpluta.base.api.game.animation.Animation;
import com.bartlomiejpluta.base.api.game.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.util.math.MathUtil;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.movement.MovableSprite;
import lombok.NonNull;
import org.joml.Vector2fc;
public class DefaultAnimation extends MovableSprite implements Animation {
private final Vector2fc[] frames;
private int animationSpeed = 100;
public DefaultAnimation(Mesh mesh, Material material, @NonNull Vector2fc[] frames) {
super(mesh, material);
this.frames = frames;
}
@Override
public void setSpeed(float speed) {
framesToCrossOneTile = (int) (1 / MathUtil.clamp(speed, Float.MIN_VALUE, 1.0));
}
@Override
public void setAnimationSpeed(float speed) {
animationSpeed = (int) (1 / MathUtil.clamp(speed, Float.MIN_VALUE, 1.0));
}
@Override
public void onAdd(ObjectLayer layer) {
// do nothing
}
@Override
public void onRemove(ObjectLayer layer) {
// do nothing
}
@Override
public int getAnimationSpeed() {
return animationSpeed;
}
@Override
public boolean shouldAnimate() {
return true;
}
@Override
public Vector2fc[] getSpriteAnimationFramesPositions() {
return frames;
}
@Override
protected void setDefaultAnimationFrame() {
// do nothing
}
}

View File

@@ -27,6 +27,17 @@ public class DefaultEntity extends MovableSprite implements Entity {
@Getter
private Direction faceDirection;
@Getter
@Setter
private boolean blocking;
public DefaultEntity(Mesh mesh, Material material, Map<Direction, Integer> spriteDirectionRows, Map<Direction, Vector2fc> spriteDefaultRows) {
super(mesh, material);
this.spriteDirectionRows = spriteDirectionRows;
this.faceDirection = Direction.DOWN;
this.spriteDefaultRows = spriteDefaultRows;
}
@Override
public int getAnimationSpeed() {
return animationSpeed;
@@ -37,10 +48,6 @@ public class DefaultEntity extends MovableSprite implements Entity {
return isMoving();
}
@Getter
@Setter
private boolean blocking;
@Override
public Vector2fc[] getSpriteAnimationFramesPositions() {
var row = spriteDirectionRows.get(faceDirection);
@@ -109,11 +116,4 @@ public class DefaultEntity extends MovableSprite implements Entity {
public void onRemove(ObjectLayer layer) {
// Do nothing
}
public DefaultEntity(Mesh mesh, Material material, Map<Direction, Integer> spriteDirectionRows, Map<Direction, Vector2fc> spriteDefaultRows) {
super(mesh, material);
this.spriteDirectionRows = spriteDirectionRows;
this.faceDirection = Direction.DOWN;
this.spriteDefaultRows = spriteDefaultRows;
}
}

View File

@@ -5,7 +5,7 @@ import com.bartlomiejpluta.base.api.game.entity.Movement;
import com.bartlomiejpluta.base.api.internal.logic.Updatable;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.animation.AnimatedSprite;
import com.bartlomiejpluta.base.engine.world.animation.model.AnimatedSprite;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.joml.Vector2f;