Compare commits

...

10 Commits

Author SHA1 Message Date
Quillraven
f7a9ba229a add missing forest ui credits 2025-08-08 10:44:08 +02:00
Quillraven
6766187e71 add tutorial link 2025-08-08 10:41:49 +02:00
Quillraven
aff6bd7756 add missing stage.dispose call 2025-08-07 07:24:03 +02:00
Quillraven
f9d1b55b74 cleanup environment bodies 2025-07-31 21:26:19 +02:00
Quillraven
c064fc2338 minor improvements 2025-07-31 21:26:07 +02:00
Quillraven
bdf59e983b remove unnecessary facing component 2025-07-24 21:50:32 +02:00
Quillraven
95e1091e16 make damage label stationary and not move with game camera 2025-06-24 21:35:05 +02:00
Quillraven
ee39eab8eb update to LibGDX 1.13.6-SNAPSHOT 2025-06-24 20:20:02 +02:00
Quillraven
e331582663 add some minimalistic documentation to "more complex" methods 2025-06-23 21:21:05 +02:00
Quillraven
ff3048ca3a #4 improve attack behavior (animation) 2025-06-23 21:02:49 +02:00
23 changed files with 180 additions and 52 deletions

View File

@@ -4,7 +4,7 @@
![Ashley](https://img.shields.io/badge/Ashley-1.7.4-blue) ![Ashley](https://img.shields.io/badge/Ashley-1.7.4-blue)
![Tiled](https://img.shields.io/badge/Tiled-1.11-red) ![Tiled](https://img.shields.io/badge/Tiled-1.11-red)
This is the source code for the LibGDX Java tutorial series on my This is the source code for the LibGDX Java [tutorial](https://www.youtube.com/playlist?list=PLTKHCDn5RKK8us8DL7OGqgp4rQQByiX0C) series on my
[YouTube](https://www.youtube.com/Quillraven) channel. [YouTube](https://www.youtube.com/Quillraven) channel.
The game itself contains: The game itself contains:
@@ -27,6 +27,7 @@ The game itself contains:
- [Cute Fantasy assets](https://kenmi-art.itch.io/cute-fantasy-rpg) - [Cute Fantasy assets](https://kenmi-art.itch.io/cute-fantasy-rpg)
- [JRPG Music](https://yubatake.bandcamp.com/album/jrpg-collection) - [JRPG Music](https://yubatake.bandcamp.com/album/jrpg-collection)
- [UI essentials](https://crusenho.itch.io/complete-ui-essential-pack) - [UI essentials](https://crusenho.itch.io/complete-ui-essential-pack)
- [UI pack forest](https://toffeecraft.itch.io/ui-user-interface-forest)
- [8-bit-fantasy Music](https://xdeviruchi.itch.io/8-bit-fantasy-adventure-music-pack) - [8-bit-fantasy Music](https://xdeviruchi.itch.io/8-bit-fantasy-adventure-music-pack)
- [SFX generator](https://sfxr.me/) - [SFX generator](https://sfxr.me/)
- [Training dummy](https://elthen.itch.io/2d-pixel-art-training-dummy) - [Training dummy](https://elthen.itch.io/2d-pixel-art-training-dummy)

View File

@@ -7,6 +7,7 @@ buildscript {
google() google()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://central.sonatype.com/repository/maven-snapshots/' }
} }
dependencies { dependencies {
@@ -67,6 +68,7 @@ subprojects {
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
maven { url 'https://central.sonatype.com/repository/maven-snapshots/' }
} }
} }

View File

@@ -75,6 +75,9 @@ public class GdxGame extends Game {
screenCache.remove(screen.getClass()); screenCache.remove(screen.getClass());
} }
/**
* Renders the current screen with performance monitoring.
*/
@Override @Override
public void render() { public void render() {
glProfiler.reset(); glProfiler.reset();
@@ -94,6 +97,9 @@ public class GdxGame extends Game {
super.resize(width, height); super.resize(width, height);
} }
/**
* Cleans up all game resources and services.
*/
@Override @Override
public void dispose() { public void dispose() {
for (Screen screen : screenCache.values()) { for (Screen screen : screenCache.values()) {

View File

@@ -20,7 +20,7 @@ public enum AnimationState implements State<Entity> {
@Override @Override
public void update(Entity entity) { public void update(Entity entity) {
Move move = Move.MAPPER.get(entity); Move move = Move.MAPPER.get(entity);
if (move != null && !move.getDirection().isZero()) { if (move != null && !move.isRooted() && !move.getDirection().isZero()) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(WALK); Fsm.MAPPER.get(entity).getAnimationFsm().changeState(WALK);
return; return;
} }
@@ -56,7 +56,7 @@ public enum AnimationState implements State<Entity> {
@Override @Override
public void update(Entity entity) { public void update(Entity entity) {
Move move = Move.MAPPER.get(entity); Move move = Move.MAPPER.get(entity);
if (move.getDirection().isZero()) { if (move.getDirection().isZero() || move.isRooted()) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(IDLE); Fsm.MAPPER.get(entity).getAnimationFsm().changeState(IDLE);
} }
} }

View File

@@ -1,7 +1,9 @@
package io.github.com.quillraven.asset; package io.github.com.quillraven.asset;
import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.maps.tiled.BaseTiledMapLoader;
import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
public enum MapAsset implements Asset<TiledMap> { public enum MapAsset implements Asset<TiledMap> {
MAIN("mainmap.tmx"); MAIN("mainmap.tmx");
@@ -9,7 +11,9 @@ public enum MapAsset implements Asset<TiledMap> {
private final AssetDescriptor<TiledMap> descriptor; private final AssetDescriptor<TiledMap> descriptor;
MapAsset(String mapName) { MapAsset(String mapName) {
this.descriptor = new AssetDescriptor<>("maps/" + mapName, TiledMap.class); BaseTiledMapLoader.Parameters parameters = new TmxMapLoader.Parameters();
parameters.projectFilePath = "maps/mystic.tiled-project";
this.descriptor = new AssetDescriptor<>("maps/" + mapName, TiledMap.class, parameters);
} }
@Override @Override

View File

@@ -9,6 +9,7 @@ public class Move implements Component {
private float maxSpeed; private float maxSpeed;
private final Vector2 direction; private final Vector2 direction;
private boolean isRooted;
public Move(float maxSpeed) { public Move(float maxSpeed) {
this.maxSpeed = maxSpeed; this.maxSpeed = maxSpeed;
@@ -22,4 +23,12 @@ public class Move implements Component {
public Vector2 getDirection() { public Vector2 getDirection() {
return direction; return direction;
} }
public void setRooted(boolean rooted) {
this.isRooted = rooted;
}
public boolean isRooted() {
return isRooted;
}
} }

View File

@@ -136,5 +136,6 @@ public class GameScreen extends ScreenAdapter {
} }
} }
this.physicWorld.dispose(); this.physicWorld.dispose();
this.stage.dispose();
} }
} }

View File

@@ -18,6 +18,9 @@ public class LoadingScreen extends ScreenAdapter {
this.assetService = game.getAssetService(); this.assetService = game.getAssetService();
} }
/**
* Queues all required assets for loading.
*/
@Override @Override
public void show() { public void show() {
for (AtlasAsset atlasAsset : AtlasAsset.values()) { for (AtlasAsset atlasAsset : AtlasAsset.values()) {
@@ -29,6 +32,9 @@ public class LoadingScreen extends ScreenAdapter {
} }
} }
/**
* Updates asset loading progress and transitions to menu when complete.
*/
@Override @Override
public void render(float delta) { public void render(float delta) {
if (assetService.update()) { if (assetService.update()) {

View File

@@ -31,6 +31,9 @@ public class AnimationSystem extends IteratingSystem {
this.animationCache = new HashMap<>(); this.animationCache = new HashMap<>();
} }
/**
* Updates animation state and sets graphic's component region.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Animation2D animation2D = Animation2D.MAPPER.get(entity); Animation2D animation2D = Animation2D.MAPPER.get(entity);
@@ -49,6 +52,9 @@ public class AnimationSystem extends IteratingSystem {
Graphic.MAPPER.get(entity).setRegion(keyFrame); Graphic.MAPPER.get(entity).setRegion(keyFrame);
} }
/**
* Updates animation based on direction and type, using cached animations.
*/
private void updateAnimation(Animation2D animation2D, FacingDirection direction) { private void updateAnimation(Animation2D animation2D, FacingDirection direction) {
AtlasAsset atlasAsset = animation2D.getAtlasAsset(); AtlasAsset atlasAsset = animation2D.getAtlasAsset();
String atlasKey = animation2D.getAtlasKey(); String atlasKey = animation2D.getAtlasKey();

View File

@@ -18,6 +18,7 @@ import io.github.com.quillraven.component.Damaged;
import io.github.com.quillraven.component.Facing; import io.github.com.quillraven.component.Facing;
import io.github.com.quillraven.component.Facing.FacingDirection; import io.github.com.quillraven.component.Facing.FacingDirection;
import io.github.com.quillraven.component.Life; import io.github.com.quillraven.component.Life;
import io.github.com.quillraven.component.Move;
import io.github.com.quillraven.component.Physic; import io.github.com.quillraven.component.Physic;
public class AttackSystem extends IteratingSystem { public class AttackSystem extends IteratingSystem {
@@ -38,6 +39,9 @@ public class AttackSystem extends IteratingSystem {
this.attackDamage = 0f; this.attackDamage = 0f;
} }
/**
* Processes attack logic including sound effects and damage detection.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Attack attack = Attack.MAPPER.get(entity); Attack attack = Attack.MAPPER.get(entity);
@@ -46,6 +50,10 @@ public class AttackSystem extends IteratingSystem {
if (attack.hasAttackStarted() && attack.getSfx() != null) { if (attack.hasAttackStarted() && attack.getSfx() != null) {
audioService.playSound(attack.getSfx()); audioService.playSound(attack.getSfx());
Move move = Move.MAPPER.get(entity);
if (move != null) {
move.setRooted(true);
}
} }
attack.decAttackTimer(deltaTime); attack.decAttackTimer(deltaTime);
@@ -57,6 +65,11 @@ public class AttackSystem extends IteratingSystem {
this.attackDamage = attack.getDamage(); this.attackDamage = attack.getDamage();
world.QueryAABB(this::attackCallback, attackAABB.x, attackAABB.y, attackAABB.width, attackAABB.height); world.QueryAABB(this::attackCallback, attackAABB.x, attackAABB.y, attackAABB.width, attackAABB.height);
Move move = Move.MAPPER.get(entity);
if (move != null) {
move.setRooted(false);
}
} }
} }

View File

@@ -27,6 +27,9 @@ public class CameraSystem extends IteratingSystem {
this.targetPosition = new Vector2(); this.targetPosition = new Vector2();
} }
/**
* Updates camera position with smooth following and boundary constraints.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Transform transform = Transform.MAPPER.get(entity); Transform transform = Transform.MAPPER.get(entity);
@@ -39,25 +42,32 @@ public class CameraSystem extends IteratingSystem {
camera.position.set(smoothedX, smoothedY, camera.position.z); camera.position.set(smoothedX, smoothedY, camera.position.z);
} }
/**
* Calculates target camera position within map boundaries.
*/
private void calcTargetPosition(Vector2 entityPosition) { private void calcTargetPosition(Vector2 entityPosition) {
// Keep the target position within map boundaries
float targetX = entityPosition.x; float targetX = entityPosition.x;
float targetY = entityPosition.y + CAM_OFFSET_Y;
float camHalfW = camera.viewportWidth * 0.5f; float camHalfW = camera.viewportWidth * 0.5f;
if (mapW > camHalfW) { if (mapW > camHalfW) {
float min = Math.min(camHalfW, mapW - camHalfW); float min = Math.min(camHalfW, mapW - camHalfW);
float max = Math.max(camHalfW, mapW - camHalfW); float max = Math.max(camHalfW, mapW - camHalfW);
targetX = MathUtils.clamp(targetX, min, max); targetX = MathUtils.clamp(targetX, min, max);
} }
float targetY = entityPosition.y + CAM_OFFSET_Y;
float camHalfH = camera.viewportHeight * 0.5f; float camHalfH = camera.viewportHeight * 0.5f;
if (mapH > camHalfH) { if (mapH > camHalfH) {
float min = Math.min(camHalfH, mapH - camHalfH); float min = Math.min(camHalfH, mapH - camHalfH);
float max = Math.max(camHalfH, mapH - camHalfH); float max = Math.max(camHalfH, mapH - camHalfH);
targetY = MathUtils.clamp(targetY, min, max); targetY = MathUtils.clamp(targetY, min, max);
} }
this.targetPosition.set(targetX, targetY); this.targetPosition.set(targetX, targetY);
} }
/**
* Sets up camera for a new map and positions it at the target entity.
*/
public void setMap(TiledMap tiledMap) { public void setMap(TiledMap tiledMap) {
int width = tiledMap.getProperties().get("width", 0, Integer.class); int width = tiledMap.getProperties().get("width", 0, Integer.class);
int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class); int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class);
@@ -71,8 +81,6 @@ public class CameraSystem extends IteratingSystem {
return; return;
} }
Transform transform = Transform.MAPPER.get(camEntity); processEntity(camEntity, 0f);
calcTargetPosition(transform.getPosition());
camera.position.set(this.targetPosition.x, this.targetPosition.y, camera.position.z);
} }
} }

View File

@@ -18,6 +18,9 @@ public class ControllerSystem extends IteratingSystem {
this.game = game; this.game = game;
} }
/**
* Processes input commands for the entity, handling movement and actions.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Controller controller = Controller.MAPPER.get(entity); Controller controller = Controller.MAPPER.get(entity);

View File

@@ -5,7 +5,6 @@ import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IteratingSystem; import com.badlogic.ashley.systems.IteratingSystem;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body; 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.Move;
import io.github.com.quillraven.component.Physic; import io.github.com.quillraven.component.Physic;
@@ -13,7 +12,7 @@ public class PhysicMoveSystem extends IteratingSystem {
private static final Vector2 TMP_VEC2 = new Vector2(); private static final Vector2 TMP_VEC2 = new Vector2();
public PhysicMoveSystem() { public PhysicMoveSystem() {
super(Family.all(Physic.class, Move.class, Facing.class).get()); super(Family.all(Physic.class, Move.class).get());
} }
@Override @Override
@@ -21,8 +20,8 @@ public class PhysicMoveSystem extends IteratingSystem {
Move move = Move.MAPPER.get(entity); Move move = Move.MAPPER.get(entity);
Physic physic = Physic.MAPPER.get(entity); Physic physic = Physic.MAPPER.get(entity);
Body body = physic.getBody(); Body body = physic.getBody();
if (move.getDirection().isZero()) { if (move.isRooted() || move.getDirection().isZero()) {
// no direction given -> stop movement // no direction given or rooted -> stop movement
body.setLinearVelocity(0f, 0f); body.setLinearVelocity(0f, 0f);
return; return;
} }

View File

@@ -44,6 +44,9 @@ public class PhysicSystem extends IteratingSystem implements EntityListener, Con
engine.removeEntityListener(this); engine.removeEntityListener(this);
} }
/**
* Updates physics simulation with fixed timestep and interpolation.
*/
@Override @Override
public void update(float deltaTime) { public void update(float deltaTime) {
this.accumulator += deltaTime; this.accumulator += deltaTime;
@@ -61,12 +64,18 @@ public class PhysicSystem extends IteratingSystem implements EntityListener, Con
} }
} }
/**
* Stores previous position for interpolation.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Physic physic = Physic.MAPPER.get(entity); Physic physic = Physic.MAPPER.get(entity);
physic.getPrevPosition().set(physic.getBody().getPosition()); physic.getPrevPosition().set(physic.getBody().getPosition());
} }
/**
* Interpolates entity position between physics steps.
*/
private void interpolateEntity(Entity entity, float alpha) { private void interpolateEntity(Entity entity, float alpha) {
Transform transform = Transform.MAPPER.get(entity); Transform transform = Transform.MAPPER.get(entity);
Physic physic = Physic.MAPPER.get(entity); Physic physic = Physic.MAPPER.get(entity);
@@ -94,6 +103,9 @@ public class PhysicSystem extends IteratingSystem implements EntityListener, Con
} }
} }
/**
* Handles collision detection between entities and triggers.
*/
@Override @Override
public void beginContact(Contact contact) { public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(); Fixture fixtureA = contact.getFixtureA();

View File

@@ -8,13 +8,15 @@ import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.maps.MapLayer; import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.renderers.BatchTiledMapRenderer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile; import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.viewport.Viewport; import com.badlogic.gdx.utils.viewport.Viewport;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.component.Graphic; import io.github.com.quillraven.component.Graphic;
import io.github.com.quillraven.component.Transform; import io.github.com.quillraven.component.Transform;
import io.github.com.quillraven.tiled.TiledRenderer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
@@ -25,7 +27,7 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
private final OrthographicCamera camera; private final OrthographicCamera camera;
private final Viewport viewport; private final Viewport viewport;
private final TiledRenderer tiledRenderer; private final BatchTiledMapRenderer tiledRenderer;
private final List<MapLayer> fgdLayers; private final List<MapLayer> fgdLayers;
private final List<MapLayer> bgdLayers; private final List<MapLayer> bgdLayers;
@@ -38,11 +40,14 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
this.batch = batch; this.batch = batch;
this.viewport = viewport; this.viewport = viewport;
this.camera = camera; this.camera = camera;
this.tiledRenderer = new TiledRenderer(batch); this.tiledRenderer = new OrthogonalTiledMapRenderer(null, GdxGame.UNIT_SCALE, batch);
this.fgdLayers = new ArrayList<>(); this.fgdLayers = new ArrayList<>();
this.bgdLayers = new ArrayList<>(); this.bgdLayers = new ArrayList<>();
} }
/**
* Renders the scene with background, entities, and foreground layers.
*/
@Override @Override
public void update(float deltaTime) { public void update(float deltaTime) {
AnimatedTiledMapTile.updateAnimationBaseTime(); AnimatedTiledMapTile.updateAnimationBaseTime();
@@ -51,16 +56,19 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
batch.begin(); batch.begin();
batch.setColor(Color.WHITE); batch.setColor(Color.WHITE);
this.tiledRenderer.setView(camera); this.tiledRenderer.setView(camera);
this.tiledRenderer.renderLayers(bgdLayers); bgdLayers.forEach(tiledRenderer::renderMapLayer);
forceSort(); forceSort();
super.update(deltaTime); super.update(deltaTime);
batch.setColor(Color.WHITE); batch.setColor(Color.WHITE);
this.tiledRenderer.renderLayers(fgdLayers); fgdLayers.forEach(tiledRenderer::renderMapLayer);
batch.end(); batch.end();
} }
/**
* Renders a single entity with its transform and graphic components.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Transform transform = Transform.MAPPER.get(entity); Transform transform = Transform.MAPPER.get(entity);
@@ -84,6 +92,9 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
); );
} }
/**
* Sets up the map and organizes layers into background and foreground.
*/
public void setMap(TiledMap tiledMap) { public void setMap(TiledMap tiledMap) {
this.tiledRenderer.setMap(tiledMap); this.tiledRenderer.setMap(tiledMap);

View File

@@ -22,6 +22,9 @@ public class TriggerSystem extends IteratingSystem {
this.audioService = audioService; this.audioService = audioService;
} }
/**
* Processes triggered entities and fires appropriate trigger effects.
*/
@Override @Override
protected void processEntity(Entity entity, float deltaTime) { protected void processEntity(Entity entity, float deltaTime) {
Trigger trigger = Trigger.MAPPER.get(entity); Trigger trigger = Trigger.MAPPER.get(entity);
@@ -41,6 +44,9 @@ public class TriggerSystem extends IteratingSystem {
return null; return null;
} }
/**
* Routes trigger events to appropriate handlers based on trigger name.
*/
private void fireTrigger(String triggerName, Entity triggeringEntity) { private void fireTrigger(String triggerName, Entity triggeringEntity) {
switch (triggerName) { switch (triggerName) {
case "trap_trigger" -> trapTrigger(triggeringEntity); case "trap_trigger" -> trapTrigger(triggeringEntity);
@@ -48,6 +54,9 @@ public class TriggerSystem extends IteratingSystem {
} }
} }
/**
* Handles trap trigger effects including animation and damage.
*/
private void trapTrigger(Entity triggeringEntity) { private void trapTrigger(Entity triggeringEntity) {
Entity trapEntity = getByTiledId(15); Entity trapEntity = getByTiledId(15);
if (trapEntity != null) { if (trapEntity != null) {

View File

@@ -91,6 +91,9 @@ public class TiledAshleyConfigurator {
} }
} }
/**
* Creates and configures an entity from a Tiled map object with all necessary components.
*/
public void onLoadObject(TiledMapTileMapObject tileMapObject) { public void onLoadObject(TiledMapTileMapObject tileMapObject) {
Entity entity = this.engine.createEntity(); Entity entity = this.engine.createEntity();
TiledMapTile tile = tileMapObject.getTile(); TiledMapTile tile = tileMapObject.getTile();
@@ -228,7 +231,7 @@ public class TiledAshleyConfigurator {
private void addEntityPhysic(MapObjects mapObjects, BodyType bodyType, Vector2 relativeTo, Entity entity) { private void addEntityPhysic(MapObjects mapObjects, BodyType bodyType, Vector2 relativeTo, Entity entity) {
if (mapObjects.getCount() == 0) return; if (mapObjects.getCount() == 0) return;
Transform transform = entity.getComponent(Transform.class); Transform transform = Transform.MAPPER.get(entity);
Body body = createBody(mapObjects, Body body = createBody(mapObjects,
transform.getPosition(), transform.getPosition(),
transform.getScaling(), transform.getScaling(),

View File

@@ -1,20 +0,0 @@
package io.github.com.quillraven.tiled;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import io.github.com.quillraven.GdxGame;
import java.util.List;
public class TiledRenderer extends OrthogonalTiledMapRenderer {
public TiledRenderer(Batch batch) {
super(null, GdxGame.UNIT_SCALE, batch);
}
public void renderLayers(List<MapLayer> layers) {
for (MapLayer layer : layers) {
this.renderMapLayer(layer);
}
}
}

View File

@@ -12,6 +12,7 @@ import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef; import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.PolygonShape; import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.GdxRuntimeException;
import io.github.com.quillraven.GdxGame; import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.asset.AssetService; import io.github.com.quillraven.asset.AssetService;
@@ -50,6 +51,15 @@ public class TiledService {
public void setMap(TiledMap tiledMap) { public void setMap(TiledMap tiledMap) {
if (this.currentMap != null) { if (this.currentMap != null) {
this.assetService.unload(this.currentMap.getProperties().get("mapAsset", MapAsset.class)); this.assetService.unload(this.currentMap.getProperties().get("mapAsset", MapAsset.class));
// quick and dirt environment body cleanup (=map boundary and static tile collision bodies)
Array<Body> bodies = new Array<>();
physicWorld.getBodies(bodies);
for (Body body : bodies) {
if ("environment".equals(body.getUserData())) {
physicWorld.destroyBody(body);
}
}
} }
this.currentMap = tiledMap; this.currentMap = tiledMap;
@@ -75,6 +85,9 @@ public class TiledService {
this.loadTileConsumer = loadTileConsumer; this.loadTileConsumer = loadTileConsumer;
} }
/**
* Loads all map objects from different layers and creates map collision boundaries.
*/
public void loadMapObjects(TiledMap tiledMap) { public void loadMapObjects(TiledMap tiledMap) {
for (MapLayer layer : tiledMap.getLayers()) { for (MapLayer layer : tiledMap.getLayers()) {
if (layer instanceof TiledMapTileLayer tileLayer) { if (layer instanceof TiledMapTileLayer tileLayer) {
@@ -89,8 +102,10 @@ public class TiledService {
spawnMapBoundary(tiledMap); spawnMapBoundary(tiledMap);
} }
/**
* Creates physics boundaries around the map edges.
*/
private void spawnMapBoundary(TiledMap tiledMap) { private void spawnMapBoundary(TiledMap tiledMap) {
// create four boxes for the map boundary (left, right, bottom and top edge)
int width = tiledMap.getProperties().get("width", 0, Integer.class); int width = tiledMap.getProperties().get("width", 0, Integer.class);
int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class); int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class);
int height = tiledMap.getProperties().get("height", 0, Integer.class); int height = tiledMap.getProperties().get("height", 0, Integer.class);
@@ -106,6 +121,7 @@ public class TiledService {
bodyDef.position.setZero(); bodyDef.position.setZero();
bodyDef.fixedRotation = true; bodyDef.fixedRotation = true;
Body body = physicWorld.createBody(bodyDef); Body body = physicWorld.createBody(bodyDef);
body.setUserData("environment");
// left edge // left edge
PolygonShape shape = new PolygonShape(); PolygonShape shape = new PolygonShape();

View File

@@ -15,16 +15,16 @@ public class GameViewModel extends ViewModel {
private final AudioService audioService; private final AudioService audioService;
private int lifePoints; private int lifePoints;
private int maxLife; private int maxLife;
private final Vector2 tmpVec2;
private Map.Entry<Vector2, Integer> playerDamage; private Map.Entry<Vector2, Integer> playerDamage;
private final Vector2 tmpVec2;
public GameViewModel(GdxGame game) { public GameViewModel(GdxGame game) {
super(game); super(game);
this.audioService = game.getAudioService(); this.audioService = game.getAudioService();
this.lifePoints = 0; this.lifePoints = 0;
this.maxLife = 0; this.maxLife = 0;
this.tmpVec2 = new Vector2();
this.playerDamage = null; this.playerDamage = null;
this.tmpVec2 = new Vector2();
} }
public void setMaxLife(int maxLife) { public void setMaxLife(int maxLife) {
@@ -58,10 +58,15 @@ public class GameViewModel extends ViewModel {
} }
public void playerDamage(int amount, float x, float y) { public void playerDamage(int amount, float x, float y) {
tmpVec2.set(x, y); Vector2 position = new Vector2(x, y);
game.getViewport().project(tmpVec2); this.playerDamage = Map.entry(position, amount);
this.playerDamage = Map.entry(tmpVec2, amount);
this.propertyChangeSupport.firePropertyChange(PLAYER_DAMAGE, null, this.playerDamage); this.propertyChangeSupport.firePropertyChange(PLAYER_DAMAGE, null, this.playerDamage);
} }
public Vector2 toScreenCoords(Vector2 position) {
tmpVec2.set(position);
game.getViewport().project(tmpVec2);
return tmpVec2;
}
} }

View File

@@ -43,6 +43,9 @@ public class GameView extends View<GameViewModel> {
add(horizontalGroup); add(horizontalGroup);
} }
/**
* Updates the life display with appropriate heart icons.
*/
private void updateLife(int lifePoints) { private void updateLife(int lifePoints) {
lifeGroup.clear(); lifeGroup.clear();
@@ -57,15 +60,31 @@ public class GameView extends View<GameViewModel> {
} }
} }
private Vector2 toStageCoords(Vector2 gamePosition) {
Vector2 resultPosition = viewModel.toScreenCoords(gamePosition);
stage.getViewport().unproject(resultPosition);
resultPosition.y = stage.getViewport().getWorldHeight() - resultPosition.y;
return resultPosition;
}
/**
* Shows animated damage text at the specified position.
*/
private void showDamage(Map.Entry<Vector2, Integer> damAndPos) { private void showDamage(Map.Entry<Vector2, Integer> damAndPos) {
Vector2 position = damAndPos.getKey(); final Vector2 position = damAndPos.getKey();
int damage = damAndPos.getValue(); int damage = damAndPos.getValue();
stage.getViewport().unproject(position);
position.y = stage.getViewport().getWorldHeight() - position.y;
TextraLabel textraLabel = new TypingLabel("[%75]{JUMP=2.0;0.5;0.9}{RAINBOW}" + damage, skin, "small"); TextraLabel textraLabel = new TypingLabel("[%75]{JUMP=2.0;0.5;0.9}{RAINBOW}" + damage, skin, "small");
textraLabel.setPosition(position.x, position.y);
stage.addActor(textraLabel); stage.addActor(textraLabel);
textraLabel.addAction(Actions.sequence(Actions.delay(1.25f), Actions.removeActor()));
textraLabel.addAction(
Actions.parallel(
Actions.sequence(Actions.delay(1.25f), Actions.removeActor()),
Actions.forever(Actions.run(() -> {
Vector2 stageCoords = toStageCoords(position);
textraLabel.setPosition(stageCoords.x, stageCoords.y);
}))
)
);
} }
} }

View File

@@ -28,6 +28,9 @@ public class MenuView extends View<MenuViewModel> {
selectMenuItem(this.selectedItem); selectMenuItem(this.selectedItem);
} }
/**
* Selects a menu item and animates the selection indicator.
*/
private void selectMenuItem(Group menuItem) { private void selectMenuItem(Group menuItem) {
if (selectionImg.getParent() != null) { if (selectionImg.getParent() != null) {
selectionImg.getParent().removeActor(selectionImg); selectionImg.getParent().removeActor(selectionImg);
@@ -54,6 +57,9 @@ public class MenuView extends View<MenuViewModel> {
))); )));
} }
/**
* Sets up the main UI layout with banner and menu content.
*/
@Override @Override
protected void setupUI() { protected void setupUI() {
setFillParent(true); setFillParent(true);
@@ -68,6 +74,9 @@ public class MenuView extends View<MenuViewModel> {
add(label).padRight(5.0f).padBottom(5f).expand().align(Align.bottomRight); add(label).padRight(5.0f).padBottom(5f).expand().align(Align.bottomRight);
} }
/**
* Creates the menu content with buttons and volume sliders.
*/
private void setupMenuContent() { private void setupMenuContent() {
Table contentTable = new Table(); Table contentTable = new Table();
contentTable.setBackground(skin.getDrawable("frame")); contentTable.setBackground(skin.getDrawable("frame"));
@@ -114,6 +123,9 @@ public class MenuView extends View<MenuViewModel> {
return slider; return slider;
} }
/**
* Moves selection to the next menu item.
*/
@Override @Override
public void onDown() { public void onDown() {
Group menuContentTable = this.selectedItem.getParent(); Group menuContentTable = this.selectedItem.getParent();
@@ -127,6 +139,9 @@ public class MenuView extends View<MenuViewModel> {
selectMenuItem((Group) menuContentTable.getChild(currentIdx)); selectMenuItem((Group) menuContentTable.getChild(currentIdx));
} }
/**
* Moves selection to the previous menu item.
*/
@Override @Override
public void onUp() { public void onUp() {
Group menuContentTable = this.selectedItem.getParent(); Group menuContentTable = this.selectedItem.getParent();

View File

@@ -6,7 +6,7 @@ ashleyVersion=1.7.4
box2dlightsVersion=1.5 box2dlightsVersion=1.5
enableGraalNative=false enableGraalNative=false
graalHelperVersion=2.0.1 graalHelperVersion=2.0.1
gdxVersion=1.13.5 gdxVersion=1.13.6-SNAPSHOT
projectVersion=1.0.0 projectVersion=1.0.0
stripeVersion=2.0.0 stripeVersion=2.0.0
tenPatchVersion=5.2.3 tenPatchVersion=5.2.3