Compare commits
10 Commits
083453edb0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7a9ba229a | ||
|
|
6766187e71 | ||
|
|
aff6bd7756 | ||
|
|
f9d1b55b74 | ||
|
|
c064fc2338 | ||
|
|
bdf59e983b | ||
|
|
95e1091e16 | ||
|
|
ee39eab8eb | ||
|
|
e331582663 | ||
|
|
ff3048ca3a |
@@ -4,7 +4,7 @@
|
||||

|
||||

|
||||
|
||||
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.
|
||||
|
||||
The game itself contains:
|
||||
@@ -27,6 +27,7 @@ The game itself contains:
|
||||
- [Cute Fantasy assets](https://kenmi-art.itch.io/cute-fantasy-rpg)
|
||||
- [JRPG Music](https://yubatake.bandcamp.com/album/jrpg-collection)
|
||||
- [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)
|
||||
- [SFX generator](https://sfxr.me/)
|
||||
- [Training dummy](https://elthen.itch.io/2d-pixel-art-training-dummy)
|
||||
|
||||
@@ -7,6 +7,7 @@ buildscript {
|
||||
google()
|
||||
maven { url 'https://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 {
|
||||
|
||||
@@ -67,6 +68,7 @@ subprojects {
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url 'https://central.sonatype.com/repository/maven-snapshots/' }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@ public class GdxGame extends Game {
|
||||
screenCache.remove(screen.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the current screen with performance monitoring.
|
||||
*/
|
||||
@Override
|
||||
public void render() {
|
||||
glProfiler.reset();
|
||||
@@ -94,6 +97,9 @@ public class GdxGame extends Game {
|
||||
super.resize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up all game resources and services.
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
for (Screen screen : screenCache.values()) {
|
||||
|
||||
@@ -20,7 +20,7 @@ public enum AnimationState implements State<Entity> {
|
||||
@Override
|
||||
public void update(Entity 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);
|
||||
return;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public enum AnimationState implements State<Entity> {
|
||||
@Override
|
||||
public void update(Entity entity) {
|
||||
Move move = Move.MAPPER.get(entity);
|
||||
if (move.getDirection().isZero()) {
|
||||
if (move.getDirection().isZero() || move.isRooted()) {
|
||||
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package io.github.com.quillraven.asset;
|
||||
|
||||
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.TmxMapLoader;
|
||||
|
||||
public enum MapAsset implements Asset<TiledMap> {
|
||||
MAIN("mainmap.tmx");
|
||||
@@ -9,7 +11,9 @@ public enum MapAsset implements Asset<TiledMap> {
|
||||
private final AssetDescriptor<TiledMap> descriptor;
|
||||
|
||||
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
|
||||
|
||||
@@ -9,6 +9,7 @@ public class Move implements Component {
|
||||
|
||||
private float maxSpeed;
|
||||
private final Vector2 direction;
|
||||
private boolean isRooted;
|
||||
|
||||
public Move(float maxSpeed) {
|
||||
this.maxSpeed = maxSpeed;
|
||||
@@ -22,4 +23,12 @@ public class Move implements Component {
|
||||
public Vector2 getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setRooted(boolean rooted) {
|
||||
this.isRooted = rooted;
|
||||
}
|
||||
|
||||
public boolean isRooted() {
|
||||
return isRooted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,5 +136,6 @@ public class GameScreen extends ScreenAdapter {
|
||||
}
|
||||
}
|
||||
this.physicWorld.dispose();
|
||||
this.stage.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ public class LoadingScreen extends ScreenAdapter {
|
||||
this.assetService = game.getAssetService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues all required assets for loading.
|
||||
*/
|
||||
@Override
|
||||
public void show() {
|
||||
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
|
||||
public void render(float delta) {
|
||||
if (assetService.update()) {
|
||||
|
||||
@@ -31,6 +31,9 @@ public class AnimationSystem extends IteratingSystem {
|
||||
this.animationCache = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates animation state and sets graphic's component region.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
Animation2D animation2D = Animation2D.MAPPER.get(entity);
|
||||
@@ -49,6 +52,9 @@ public class AnimationSystem extends IteratingSystem {
|
||||
Graphic.MAPPER.get(entity).setRegion(keyFrame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates animation based on direction and type, using cached animations.
|
||||
*/
|
||||
private void updateAnimation(Animation2D animation2D, FacingDirection direction) {
|
||||
AtlasAsset atlasAsset = animation2D.getAtlasAsset();
|
||||
String atlasKey = animation2D.getAtlasKey();
|
||||
|
||||
@@ -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.FacingDirection;
|
||||
import io.github.com.quillraven.component.Life;
|
||||
import io.github.com.quillraven.component.Move;
|
||||
import io.github.com.quillraven.component.Physic;
|
||||
|
||||
public class AttackSystem extends IteratingSystem {
|
||||
@@ -38,6 +39,9 @@ public class AttackSystem extends IteratingSystem {
|
||||
this.attackDamage = 0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes attack logic including sound effects and damage detection.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
Attack attack = Attack.MAPPER.get(entity);
|
||||
@@ -46,6 +50,10 @@ public class AttackSystem extends IteratingSystem {
|
||||
|
||||
if (attack.hasAttackStarted() && attack.getSfx() != null) {
|
||||
audioService.playSound(attack.getSfx());
|
||||
Move move = Move.MAPPER.get(entity);
|
||||
if (move != null) {
|
||||
move.setRooted(true);
|
||||
}
|
||||
}
|
||||
|
||||
attack.decAttackTimer(deltaTime);
|
||||
@@ -57,6 +65,11 @@ public class AttackSystem extends IteratingSystem {
|
||||
|
||||
this.attackDamage = attack.getDamage();
|
||||
world.QueryAABB(this::attackCallback, attackAABB.x, attackAABB.y, attackAABB.width, attackAABB.height);
|
||||
|
||||
Move move = Move.MAPPER.get(entity);
|
||||
if (move != null) {
|
||||
move.setRooted(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ public class CameraSystem extends IteratingSystem {
|
||||
this.targetPosition = new Vector2();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates camera position with smooth following and boundary constraints.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
Transform transform = Transform.MAPPER.get(entity);
|
||||
@@ -39,25 +42,32 @@ public class CameraSystem extends IteratingSystem {
|
||||
camera.position.set(smoothedX, smoothedY, camera.position.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates target camera position within map boundaries.
|
||||
*/
|
||||
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 targetY = entityPosition.y + CAM_OFFSET_Y;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up camera for a new map and positions it at the target entity.
|
||||
*/
|
||||
public void setMap(TiledMap tiledMap) {
|
||||
int width = tiledMap.getProperties().get("width", 0, Integer.class);
|
||||
int tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class);
|
||||
@@ -71,8 +81,6 @@ public class CameraSystem extends IteratingSystem {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform transform = Transform.MAPPER.get(camEntity);
|
||||
calcTargetPosition(transform.getPosition());
|
||||
camera.position.set(this.targetPosition.x, this.targetPosition.y, camera.position.z);
|
||||
processEntity(camEntity, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ public class ControllerSystem extends IteratingSystem {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes input commands for the entity, handling movement and actions.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
Controller controller = Controller.MAPPER.get(entity);
|
||||
|
||||
@@ -5,7 +5,6 @@ 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;
|
||||
|
||||
@@ -13,7 +12,7 @@ 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());
|
||||
super(Family.all(Physic.class, Move.class).get());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -21,8 +20,8 @@ public class PhysicMoveSystem extends IteratingSystem {
|
||||
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
|
||||
if (move.isRooted() || move.getDirection().isZero()) {
|
||||
// no direction given or rooted -> stop movement
|
||||
body.setLinearVelocity(0f, 0f);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ public class PhysicSystem extends IteratingSystem implements EntityListener, Con
|
||||
engine.removeEntityListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates physics simulation with fixed timestep and interpolation.
|
||||
*/
|
||||
@Override
|
||||
public void update(float deltaTime) {
|
||||
this.accumulator += deltaTime;
|
||||
@@ -61,12 +64,18 @@ public class PhysicSystem extends IteratingSystem implements EntityListener, Con
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores previous position for interpolation.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
Physic physic = Physic.MAPPER.get(entity);
|
||||
physic.getPrevPosition().set(physic.getBody().getPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates entity position between physics steps.
|
||||
*/
|
||||
private void interpolateEntity(Entity entity, float alpha) {
|
||||
Transform transform = Transform.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
|
||||
public void beginContact(Contact contact) {
|
||||
Fixture fixtureA = contact.getFixtureA();
|
||||
|
||||
@@ -8,13 +8,15 @@ import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.maps.MapLayer;
|
||||
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.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
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.Transform;
|
||||
import io.github.com.quillraven.tiled.TiledRenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@@ -25,7 +27,7 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
|
||||
private final OrthographicCamera camera;
|
||||
private final Viewport viewport;
|
||||
|
||||
private final TiledRenderer tiledRenderer;
|
||||
private final BatchTiledMapRenderer tiledRenderer;
|
||||
private final List<MapLayer> fgdLayers;
|
||||
private final List<MapLayer> bgdLayers;
|
||||
|
||||
@@ -38,11 +40,14 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
|
||||
this.batch = batch;
|
||||
this.viewport = viewport;
|
||||
this.camera = camera;
|
||||
this.tiledRenderer = new TiledRenderer(batch);
|
||||
this.tiledRenderer = new OrthogonalTiledMapRenderer(null, GdxGame.UNIT_SCALE, batch);
|
||||
this.fgdLayers = new ArrayList<>();
|
||||
this.bgdLayers = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the scene with background, entities, and foreground layers.
|
||||
*/
|
||||
@Override
|
||||
public void update(float deltaTime) {
|
||||
AnimatedTiledMapTile.updateAnimationBaseTime();
|
||||
@@ -51,16 +56,19 @@ public class RenderSystem extends SortedIteratingSystem implements Disposable {
|
||||
batch.begin();
|
||||
batch.setColor(Color.WHITE);
|
||||
this.tiledRenderer.setView(camera);
|
||||
this.tiledRenderer.renderLayers(bgdLayers);
|
||||
bgdLayers.forEach(tiledRenderer::renderMapLayer);
|
||||
|
||||
forceSort();
|
||||
super.update(deltaTime);
|
||||
|
||||
batch.setColor(Color.WHITE);
|
||||
this.tiledRenderer.renderLayers(fgdLayers);
|
||||
fgdLayers.forEach(tiledRenderer::renderMapLayer);
|
||||
batch.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a single entity with its transform and graphic components.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
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) {
|
||||
this.tiledRenderer.setMap(tiledMap);
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ public class TriggerSystem extends IteratingSystem {
|
||||
this.audioService = audioService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes triggered entities and fires appropriate trigger effects.
|
||||
*/
|
||||
@Override
|
||||
protected void processEntity(Entity entity, float deltaTime) {
|
||||
Trigger trigger = Trigger.MAPPER.get(entity);
|
||||
@@ -41,6 +44,9 @@ public class TriggerSystem extends IteratingSystem {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes trigger events to appropriate handlers based on trigger name.
|
||||
*/
|
||||
private void fireTrigger(String triggerName, Entity triggeringEntity) {
|
||||
switch (triggerName) {
|
||||
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) {
|
||||
Entity trapEntity = getByTiledId(15);
|
||||
if (trapEntity != null) {
|
||||
|
||||
@@ -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) {
|
||||
Entity entity = this.engine.createEntity();
|
||||
TiledMapTile tile = tileMapObject.getTile();
|
||||
@@ -228,7 +231,7 @@ public class TiledAshleyConfigurator {
|
||||
private void addEntityPhysic(MapObjects mapObjects, BodyType bodyType, Vector2 relativeTo, Entity entity) {
|
||||
if (mapObjects.getCount() == 0) return;
|
||||
|
||||
Transform transform = entity.getComponent(Transform.class);
|
||||
Transform transform = Transform.MAPPER.get(entity);
|
||||
Body body = createBody(mapObjects,
|
||||
transform.getPosition(),
|
||||
transform.getScaling(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.badlogic.gdx.physics.box2d.Body;
|
||||
import com.badlogic.gdx.physics.box2d.BodyDef;
|
||||
import com.badlogic.gdx.physics.box2d.PolygonShape;
|
||||
import com.badlogic.gdx.physics.box2d.World;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException;
|
||||
import io.github.com.quillraven.GdxGame;
|
||||
import io.github.com.quillraven.asset.AssetService;
|
||||
@@ -50,6 +51,15 @@ public class TiledService {
|
||||
public void setMap(TiledMap tiledMap) {
|
||||
if (this.currentMap != null) {
|
||||
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;
|
||||
@@ -75,6 +85,9 @@ public class TiledService {
|
||||
this.loadTileConsumer = loadTileConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all map objects from different layers and creates map collision boundaries.
|
||||
*/
|
||||
public void loadMapObjects(TiledMap tiledMap) {
|
||||
for (MapLayer layer : tiledMap.getLayers()) {
|
||||
if (layer instanceof TiledMapTileLayer tileLayer) {
|
||||
@@ -89,8 +102,10 @@ public class TiledService {
|
||||
spawnMapBoundary(tiledMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates physics boundaries around the map edges.
|
||||
*/
|
||||
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 tileW = tiledMap.getProperties().get("tilewidth", 0, Integer.class);
|
||||
int height = tiledMap.getProperties().get("height", 0, Integer.class);
|
||||
@@ -106,6 +121,7 @@ public class TiledService {
|
||||
bodyDef.position.setZero();
|
||||
bodyDef.fixedRotation = true;
|
||||
Body body = physicWorld.createBody(bodyDef);
|
||||
body.setUserData("environment");
|
||||
|
||||
// left edge
|
||||
PolygonShape shape = new PolygonShape();
|
||||
|
||||
@@ -15,16 +15,16 @@ public class GameViewModel extends ViewModel {
|
||||
private final AudioService audioService;
|
||||
private int lifePoints;
|
||||
private int maxLife;
|
||||
private final Vector2 tmpVec2;
|
||||
private Map.Entry<Vector2, Integer> playerDamage;
|
||||
private final Vector2 tmpVec2;
|
||||
|
||||
public GameViewModel(GdxGame game) {
|
||||
super(game);
|
||||
this.audioService = game.getAudioService();
|
||||
this.lifePoints = 0;
|
||||
this.maxLife = 0;
|
||||
this.tmpVec2 = new Vector2();
|
||||
this.playerDamage = null;
|
||||
this.tmpVec2 = new Vector2();
|
||||
}
|
||||
|
||||
public void setMaxLife(int maxLife) {
|
||||
@@ -58,10 +58,15 @@ public class GameViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public void playerDamage(int amount, float x, float y) {
|
||||
tmpVec2.set(x, y);
|
||||
game.getViewport().project(tmpVec2);
|
||||
|
||||
this.playerDamage = Map.entry(tmpVec2, amount);
|
||||
Vector2 position = new Vector2(x, y);
|
||||
this.playerDamage = Map.entry(position, amount);
|
||||
this.propertyChangeSupport.firePropertyChange(PLAYER_DAMAGE, null, this.playerDamage);
|
||||
}
|
||||
|
||||
public Vector2 toScreenCoords(Vector2 position) {
|
||||
tmpVec2.set(position);
|
||||
game.getViewport().project(tmpVec2);
|
||||
return tmpVec2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ public class GameView extends View<GameViewModel> {
|
||||
add(horizontalGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the life display with appropriate heart icons.
|
||||
*/
|
||||
private void updateLife(int lifePoints) {
|
||||
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) {
|
||||
Vector2 position = damAndPos.getKey();
|
||||
final Vector2 position = damAndPos.getKey();
|
||||
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.setPosition(position.x, position.y);
|
||||
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);
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ public class MenuView extends View<MenuViewModel> {
|
||||
selectMenuItem(this.selectedItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a menu item and animates the selection indicator.
|
||||
*/
|
||||
private void selectMenuItem(Group menuItem) {
|
||||
if (selectionImg.getParent() != null) {
|
||||
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
|
||||
protected void setupUI() {
|
||||
setFillParent(true);
|
||||
@@ -68,6 +74,9 @@ public class MenuView extends View<MenuViewModel> {
|
||||
add(label).padRight(5.0f).padBottom(5f).expand().align(Align.bottomRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the menu content with buttons and volume sliders.
|
||||
*/
|
||||
private void setupMenuContent() {
|
||||
Table contentTable = new Table();
|
||||
contentTable.setBackground(skin.getDrawable("frame"));
|
||||
@@ -114,6 +123,9 @@ public class MenuView extends View<MenuViewModel> {
|
||||
return slider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves selection to the next menu item.
|
||||
*/
|
||||
@Override
|
||||
public void onDown() {
|
||||
Group menuContentTable = this.selectedItem.getParent();
|
||||
@@ -127,6 +139,9 @@ public class MenuView extends View<MenuViewModel> {
|
||||
selectMenuItem((Group) menuContentTable.getChild(currentIdx));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves selection to the previous menu item.
|
||||
*/
|
||||
@Override
|
||||
public void onUp() {
|
||||
Group menuContentTable = this.selectedItem.getParent();
|
||||
|
||||
@@ -6,7 +6,7 @@ ashleyVersion=1.7.4
|
||||
box2dlightsVersion=1.5
|
||||
enableGraalNative=false
|
||||
graalHelperVersion=2.0.1
|
||||
gdxVersion=1.13.5
|
||||
gdxVersion=1.13.6-SNAPSHOT
|
||||
projectVersion=1.0.0
|
||||
stripeVersion=2.0.0
|
||||
tenPatchVersion=5.2.3
|
||||
|
||||
Reference in New Issue
Block a user