add animated water ripple tile and a trap trigger

This commit is contained in:
Quillraven
2025-06-01 21:46:14 +02:00
parent d5cd88a245
commit 7cf6dbe9e4
28 changed files with 445 additions and 167 deletions

View File

@@ -2,3 +2,4 @@ https://kenmi-art.itch.io/cute-fantasy-rpg
https://yubatake.bandcamp.com/album/jrpg-collection
https://crusenho.itch.io/complete-ui-essential-pack
https://xdeviruchi.itch.io/8-bit-fantasy-adventure-music-pack
https://sfxr.me/

BIN
assets/audio/trap.wav Normal file

Binary file not shown.

View File

@@ -361,3 +361,17 @@ player/walk_up
orig: 32, 32
offset: 0, 0
index: 5
trap/idle_down
rotate: false
xy: 38, 13
size: 16, 16
orig: 16, 16
offset: 0, 0
index: 0
trap/idle_down
rotate: false
xy: 410, 129
size: 16, 16
orig: 16, 16
offset: 0, 0
index: 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,21 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="18" height="16" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="15">
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="18" height="16" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="17">
<properties>
<property name="music" value="TOWN"/>
</properties>
<tileset firstgid="1" source="objects.tsx"/>
<tileset firstgid="7" source="tileset.tsx"/>
<tileset firstgid="8" source="tileset.tsx"/>
<layer id="1" name="ground" width="18" height="16">
<data encoding="base64" compression="zlib">
eJylkjsPwjAMhLPykCoeEjDBxkgzARPqBEywla2w9lfkp3OVYumwTAgwfFIb+c7xOcE5twRzgxUIXzAyzkpQxO8t2Cn2hmYNesZ5ET1uoCXuoIo1G0PD/92sC3CI2gs4EWejb+c5i3l48gjkUxm6XHgm9vGZWslSPBpQOztbQe9BZ8rURr0g/bheMr0mPHl3rXvdAe/CU8ZWD00qK/F5kM8vezvG2fgO1rvJ9WJt+aFev+9/6YOBYvimNnW3MZgophn9n9wdbaQ=
eJylkjsOwjAQRF3z6SI+HVBDzwGIa6BGqYEa+tAHLpCz+G5MJK80Wm2MgeJJibUz6511cM6twdJgA8IXTIyzEhTxew8OiqOh2YKBcV5EjwdoiSeoYs3O0PB/N+sKnKL2Bi7E1ejbeS5iHp48AvlUhi4Xnol9fKZWshSPBtQ92Qp6DzpTpjbqBenH9ZLpPeHJu2vVDngXnjK2emhSWYnPi3x+2ds5zsZ3sN5Nrhdryw/1+n3/yxCMFOOe2tTdpmCmmGf0fwPl7aDo
</data>
</layer>
<layer id="3" name="water" width="18" height="16">
<data encoding="base64" compression="zlib">
eJxjYBgFo2AUjALKgTES24hIPQAWrABm
eJxjYBgFo2AUjALKgQcQC0DZ7kTqAQAhOACg
</data>
</layer>
<objectgroup id="6" name="trigger"/>
<objectgroup id="6" name="trigger">
<object id="16" name="trap_trigger" x="97" y="114" width="14" height="12">
<properties>
<property name="sensor" type="bool" value="true"/>
</properties>
</object>
</objectgroup>
<objectgroup id="5" name="objects">
<object id="5" gid="3" x="197" y="118" width="80" height="112"/>
<object id="9" gid="5" x="32" y="32" width="16" height="16"/>
@@ -29,5 +35,6 @@
<object id="12" gid="6" x="137" y="206" width="41" height="63"/>
<object id="13" gid="6" x="72" y="93" width="41" height="63"/>
<object id="14" gid="6" x="176" y="123" width="41" height="63"/>
<object id="15" gid="7" x="96" y="128" width="16" height="16"/>
</objectgroup>
</map>

View File

@@ -30,6 +30,11 @@
"type": "string",
"value": ""
},
{
"name": "animationSpeed",
"type": "float",
"value": 0
},
{
"name": "atlasAsset",
"propertyType": "AtlasAsset",

View File

@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.0" name="objects" tilewidth="80" tileheight="112" tilecount="4" columns="0">
<tileset version="1.10" tiledversion="1.11.0" name="objects" tilewidth="80" tileheight="112" tilecount="5" columns="0">
<grid orientation="orthogonal" width="1" height="1"/>
<tile id="1" type="Object">
<properties>
<property name="animation" value="IDLE"/>
<property name="animationSpeed" type="float" value="1"/>
<property name="life" type="int" value="12"/>
<property name="lifeReg" type="float" value="0.25"/>
<property name="speed" type="float" value="3.5"/>
@@ -35,4 +36,11 @@
</object>
</objectgroup>
</tile>
<tile id="6" type="Object">
<properties>
<property name="animation" value="IDLE"/>
<property name="z" type="int" value="0"/>
</properties>
<image source="objects/trap.png" width="16" height="16"/>
</tile>
</tileset>

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.0" name="tileset" tilewidth="16" tileheight="16" spacing="16" margin="8" tilecount="128" columns="8">
<image source="tileset.png" width="256" height="512"/>
<tileset version="1.10" tiledversion="1.11.0" name="tileset" tilewidth="16" tileheight="16" spacing="16" margin="8" tilecount="192" columns="12">
<image source="tileset.png" width="384" height="512"/>
<tile id="0">
<objectgroup draworder="index" id="2">
<object id="1" x="8" y="9" width="8" height="7"/>
@@ -32,71 +32,79 @@
</objectgroup>
</tile>
<tile id="8">
<animation>
<frame tileid="8" duration="200"/>
<frame tileid="9" duration="200"/>
<frame tileid="10" duration="200"/>
<frame tileid="11" duration="200"/>
</animation>
</tile>
<tile id="12">
<objectgroup draworder="index" id="2">
<object id="1" x="8" y="0" width="4" height="16"/>
</objectgroup>
</tile>
<tile id="10">
<tile id="14">
<objectgroup draworder="index" id="2">
<object id="1" x="4" y="0" width="5" height="16"/>
</objectgroup>
</tile>
<tile id="11">
<tile id="15">
<objectgroup draworder="index" id="2">
<object id="1" x="7" y="0" width="5" height="16"/>
</objectgroup>
</tile>
<tile id="13">
<tile id="17">
<objectgroup draworder="index" id="2">
<object id="1" x="4" y="0" width="5" height="16"/>
</objectgroup>
</tile>
<tile id="16">
<tile id="24">
<objectgroup draworder="index" id="2">
<object id="1" x="9" y="0" width="7" height="8"/>
</objectgroup>
</tile>
<tile id="17">
<tile id="25">
<objectgroup draworder="index" id="2">
<object id="1" x="0" y="4" width="16" height="4"/>
</objectgroup>
</tile>
<tile id="18">
<tile id="26">
<objectgroup draworder="index" id="2">
<object id="1" x="0" y="0" width="7" height="7"/>
</objectgroup>
</tile>
<tile id="19">
<tile id="27">
<objectgroup draworder="index" id="2">
<object id="1" x="8" y="0" width="8" height="6"/>
</objectgroup>
</tile>
<tile id="20">
<tile id="28">
<objectgroup draworder="index" id="2">
<object id="1" x="0" y="6" width="16" height="4"/>
</objectgroup>
</tile>
<tile id="21">
<tile id="29">
<objectgroup draworder="index" id="2">
<object id="1" x="0" y="0" width="8" height="7"/>
</objectgroup>
</tile>
<tile id="24">
<tile id="36">
<objectgroup draworder="index" id="2">
<object id="1" x="4" y="5" width="12" height="11"/>
</objectgroup>
</tile>
<tile id="25">
<tile id="37">
<objectgroup draworder="index" id="2">
<object id="1" x="0" y="5" width="12" height="11"/>
</objectgroup>
</tile>
<tile id="32">
<tile id="48">
<objectgroup draworder="index" id="2">
<object id="1" x="4" y="0" width="12" height="14"/>
</objectgroup>
</tile>
<tile id="33">
<tile id="49">
<objectgroup draworder="index" id="2">
<object id="1" x="0" y="0" width="12" height="14"/>
</objectgroup>
@@ -105,21 +113,21 @@
<wangset name="Unnamed Set" type="corner" tile="-1">
<wangcolor name="gras" color="#ff0000" tile="-1" probability="1"/>
<wangcolor name="dirt" color="#00ff00" tile="-1" probability="1"/>
<wangtile tileid="9" wangid="0,1,0,1,0,1,0,1"/>
<wangtile tileid="48" wangid="0,1,0,2,0,1,0,1"/>
<wangtile tileid="49" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="50" wangid="0,1,0,1,0,2,0,1"/>
<wangtile tileid="56" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="58" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="64" wangid="0,2,0,1,0,1,0,1"/>
<wangtile tileid="65" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="66" wangid="0,1,0,1,0,1,0,2"/>
<wangtile tileid="72" wangid="0,2,0,1,0,2,0,2"/>
<wangtile tileid="73" wangid="0,2,0,2,0,1,0,2"/>
<wangtile tileid="80" wangid="0,1,0,2,0,2,0,2"/>
<wangtile tileid="81" wangid="0,2,0,2,0,2,0,1"/>
<wangtile tileid="99" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="121" wangid="0,1,0,1,0,1,0,1"/>
<wangtile tileid="13" wangid="0,1,0,1,0,1,0,1"/>
<wangtile tileid="72" wangid="0,1,0,2,0,1,0,1"/>
<wangtile tileid="73" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="74" wangid="0,1,0,1,0,2,0,1"/>
<wangtile tileid="84" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="86" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="96" wangid="0,2,0,1,0,1,0,1"/>
<wangtile tileid="97" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="98" wangid="0,1,0,1,0,1,0,2"/>
<wangtile tileid="108" wangid="0,2,0,1,0,2,0,2"/>
<wangtile tileid="109" wangid="0,2,0,2,0,1,0,2"/>
<wangtile tileid="120" wangid="0,1,0,2,0,2,0,2"/>
<wangtile tileid="121" wangid="0,2,0,2,0,2,0,1"/>
<wangtile tileid="147" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="181" wangid="0,1,0,1,0,1,0,1"/>
</wangset>
</wangsets>
</tileset>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

View File

@@ -3,6 +3,7 @@ package io.github.com.quillraven.asset;
public enum SoundAsset {
SWORD_HIT("sword_hit.wav"),
LIFE_REG("life_reg.wav"),
TRAP("trap.wav"),
;
private final String path;

View File

@@ -68,10 +68,6 @@ public class Animation2D implements Component {
return playMode;
}
public float getSpeed() {
return speed;
}
public void setSpeed(float speed) {
this.speed = speed;
}

View File

@@ -0,0 +1,25 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.ComponentMapper;
import com.badlogic.gdx.maps.MapObject;
public class Tiled implements Component {
public static final ComponentMapper<Tiled> MAPPER = ComponentMapper.getFor(Tiled.class);
private final int id;
private final MapObject mapObjectRef;
public Tiled(MapObject mapObjectRef) {
this.id = mapObjectRef.getProperties().get("id", -1, Integer.class);
this.mapObjectRef = mapObjectRef;
}
public int getId() {
return id;
}
public MapObject getMapObjectRef() {
return mapObjectRef;
}
}

View File

@@ -0,0 +1,29 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.ComponentMapper;
import com.badlogic.ashley.core.Entity;
public class Trigger implements Component {
public static final ComponentMapper<Trigger> MAPPER = ComponentMapper.getFor(Trigger.class);
private final String name;
private Entity triggeringEntity;
public Trigger(String name) {
this.name = name;
this.triggeringEntity = null;
}
public String getName() {
return name;
}
public void setTriggeringEntity(Entity triggeringEntity) {
this.triggeringEntity = triggeringEntity;
}
public Entity getTriggeringEntity() {
return triggeringEntity;
}
}

View File

@@ -4,8 +4,4 @@ public class IdleControllerState implements ControllerState {
@Override
public void keyDown(Command command) {
}
@Override
public void keyUp(Command command) {
}
}

View File

@@ -28,7 +28,8 @@ import io.github.com.quillraven.system.PhysicDebugRenderSystem;
import io.github.com.quillraven.system.PhysicMoveSystem;
import io.github.com.quillraven.system.PhysicSystem;
import io.github.com.quillraven.system.RenderSystem;
import io.github.com.quillraven.tiled.TiledAshleySpawner;
import io.github.com.quillraven.system.TriggerSystem;
import io.github.com.quillraven.tiled.TiledAshleyConfigurator;
import io.github.com.quillraven.tiled.TiledService;
import io.github.com.quillraven.ui.model.GameViewModel;
import io.github.com.quillraven.ui.view.GameView;
@@ -43,7 +44,7 @@ public class GameScreen extends ScreenAdapter {
private final Viewport uiViewport;
private final TiledService tiledService;
private final Engine engine;
private final TiledAshleySpawner tiledAshleySpawner;
private final TiledAshleyConfigurator tiledAshleyConfigurator;
private final World physicWorld;
private final KeyboardController keyboardController;
private final AudioService audioService;
@@ -55,11 +56,11 @@ public class GameScreen extends ScreenAdapter {
this.skin = game.getAssetService().get(SkinAsset.DEFAULT);
this.viewModel = new GameViewModel(game);
this.audioService = game.getAudioService();
this.tiledService = new TiledService(game.getAssetService());
this.physicWorld = new World(Vector2.Zero, true);
this.physicWorld.setAutoClearForces(false);
this.tiledService = new TiledService(game.getAssetService(), this.physicWorld);
this.engine = new Engine();
this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld, this.game.getAssetService());
this.tiledAshleyConfigurator = new TiledAshleyConfigurator(this.engine, this.physicWorld, this.game.getAssetService());
this.keyboardController = new KeyboardController(GameControllerState.class, engine, null);
// add ECS systems
@@ -67,6 +68,7 @@ public class GameScreen extends ScreenAdapter {
this.engine.addSystem(new PhysicSystem(physicWorld, 1 / 60f));
this.engine.addSystem(new FacingSystem());
this.engine.addSystem(new FsmSystem());
this.engine.addSystem(new TriggerSystem(audioService));
this.engine.addSystem(new LifeSystem(this.viewModel));
this.engine.addSystem(new AnimationSystem(game.getAssetService()));
this.engine.addSystem(new CameraSystem(game.getCamera()));
@@ -83,12 +85,12 @@ public class GameScreen extends ScreenAdapter {
this.stage.addActor(new GameView(stage, skin, this.viewModel));
Consumer<TiledMap> renderConsumer = this.engine.getSystem(RenderSystem.class)::setMap;
Consumer<TiledMap> ashleySpawnerConsumer = this.tiledAshleySpawner::loadMapObjects;
Consumer<TiledMap> cameraConsumer = this.engine.getSystem(CameraSystem.class)::setMap;
Consumer<TiledMap> audioConsumer = this.audioService::setMap;
this.tiledService.setMapChangeConsumer(
renderConsumer.andThen(ashleySpawnerConsumer).andThen(cameraConsumer).andThen(audioConsumer)
);
this.tiledService.setMapChangeConsumer(renderConsumer.andThen(cameraConsumer).andThen(audioConsumer));
this.tiledService.setLoadTriggerConsumer(tiledAshleyConfigurator::onLoadTrigger);
this.tiledService.setLoadObjectConsumer(tiledAshleyConfigurator::onLoadObject);
this.tiledService.setLoadTileConsumer(tiledAshleyConfigurator::onLoadTile);
TiledMap startMap = this.tiledService.loadMap(MapAsset.MAIN);
this.tiledService.setMap(startMap);

View File

@@ -7,11 +7,17 @@ import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IteratingSystem;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.World;
import io.github.com.quillraven.component.Physic;
import io.github.com.quillraven.component.Player;
import io.github.com.quillraven.component.Transform;
import io.github.com.quillraven.component.Trigger;
public class PhysicSystem extends IteratingSystem implements EntityListener {
public class PhysicSystem extends IteratingSystem implements EntityListener, ContactListener {
private final World world;
private final float interval;
@@ -22,6 +28,7 @@ public class PhysicSystem extends IteratingSystem implements EntityListener {
this.world = world;
this.interval = interval;
this.accumulator = 0f;
world.setContactListener(this);
}
@Override
@@ -86,4 +93,42 @@ public class PhysicSystem extends IteratingSystem implements EntityListener {
}
}
@Override
public void beginContact(Contact contact) {
Object userDataA = contact.getFixtureA().getBody().getUserData();
Object userDataB = contact.getFixtureB().getBody().getUserData();
if (!(userDataA instanceof Entity entityA) || !(userDataB instanceof Entity entityB)) {
return;
}
playerTriggerContact(entityA, entityB);
}
private static void playerTriggerContact(Entity entityA, Entity entityB) {
Trigger trigger = Trigger.MAPPER.get(entityA);
boolean isPlayer = Player.MAPPER.get(entityB) != null;
if (trigger != null && isPlayer) {
trigger.setTriggeringEntity(entityB);
return;
}
trigger = Trigger.MAPPER.get(entityB);
isPlayer = Player.MAPPER.get(entityA) != null;
if (trigger != null && isPlayer) {
trigger.setTriggeringEntity(entityA);
}
}
@Override
public void endContact(Contact contact) {
}
@Override
public void preSolve(Contact contact, Manifold oldManifold) {
}
@Override
public void postSolve(Contact contact, ContactImpulse impulse) {
}
}

View File

@@ -0,0 +1,75 @@
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.ashley.utils.ImmutableArray;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.Timer;
import io.github.com.quillraven.asset.SoundAsset;
import io.github.com.quillraven.audio.AudioService;
import io.github.com.quillraven.component.Animation2D;
import io.github.com.quillraven.component.Life;
import io.github.com.quillraven.component.Tiled;
import io.github.com.quillraven.component.Trigger;
public class TriggerSystem extends IteratingSystem {
private final AudioService audioService;
public TriggerSystem(AudioService audioService) {
super(Family.all(Trigger.class).get());
this.audioService = audioService;
}
@Override
protected void processEntity(Entity entity, float deltaTime) {
Trigger trigger = Trigger.MAPPER.get(entity);
if (trigger.getTriggeringEntity() == null) return;
fireTrigger(trigger.getName(), trigger.getTriggeringEntity());
trigger.setTriggeringEntity(null);
}
private Entity getByTiledId(int tiledId) {
ImmutableArray<Entity> entities = getEngine().getEntitiesFor(Family.all(Tiled.class).get());
for (Entity entity : entities) {
if (Tiled.MAPPER.get(entity).getId() == tiledId) {
return entity;
}
}
return null;
}
private void fireTrigger(String triggerName, Entity triggeringEntity) {
switch (triggerName) {
case "trap_trigger" -> trapTrigger(triggeringEntity);
default -> throw new GdxRuntimeException("Unsupported trigger: " + triggerName);
}
}
private void trapTrigger(Entity triggeringEntity) {
Entity trapEntity = getByTiledId(15);
if (trapEntity != null) {
// play trap animation
Animation2D animation2D = Animation2D.MAPPER.get(trapEntity);
animation2D.setSpeed(1f);
animation2D.setPlayMode(Animation.PlayMode.NORMAL);
audioService.playSound(SoundAsset.TRAP);
// reset animation
Timer.schedule(new Timer.Task() {
@Override
public void run() {
animation2D.setSpeed(0f);
animation2D.setType(Animation2D.AnimationType.IDLE);
}
}, 2.5f);
// damage player
Life life = Life.MAPPER.get(triggeringEntity);
if (life.getLife() > 2) {
life.addLife(-2f);
}
}
}
}

View File

@@ -7,13 +7,10 @@ import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
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;
import com.badlogic.gdx.math.Vector2;
@@ -21,7 +18,6 @@ import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.GdxRuntimeException;
import io.github.com.quillraven.GdxGame;
@@ -39,10 +35,12 @@ 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.Player;
import io.github.com.quillraven.component.Tiled;
import io.github.com.quillraven.component.Transform;
import io.github.com.quillraven.component.Trigger;
public class TiledAshleySpawner {
private static final Vector2 DEFAULT_SCALING = new Vector2(1f, 1f);
public class TiledAshleyConfigurator {
private static final Vector2 DEFAULT_PHYSIC_SCALING = new Vector2(1f, 1f);
private final Engine engine;
private final World physicWorld;
@@ -50,7 +48,7 @@ public class TiledAshleySpawner {
private final Vector2 tmpVec2;
private final AssetService assetService;
public TiledAshleySpawner(Engine engine, World physicWorld, AssetService assetService) {
public TiledAshleyConfigurator(Engine engine, World physicWorld, AssetService assetService) {
this.engine = engine;
this.physicWorld = physicWorld;
this.tmpMapObjects = new MapObjects();
@@ -58,120 +56,49 @@ public class TiledAshleySpawner {
this.assetService = assetService;
}
public void loadMapObjects(TiledMap tiledMap) {
for (MapLayer layer : tiledMap.getLayers()) {
if (layer instanceof TiledMapTileLayer tileLayer) {
loadTileLayer(tileLayer);
} else if ("objects".equals(layer.getName())) {
loadObjectLayer(layer);
} else if ("trigger".equals(layer.getName())) {
loadTriggerLayer(layer);
}
}
spawnMapBoundary(tiledMap);
public void onLoadTile(TiledMapTile tile, float x, float y) {
createBody(tile.getObjects(),
new Vector2(x, y),
DEFAULT_PHYSIC_SCALING,
BodyDef.BodyType.StaticBody,
Vector2.Zero,
"environment");
}
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);
int tileH = tiledMap.getProperties().get("tileheight", 0, Integer.class);
float mapW = width * tileW * GdxGame.UNIT_SCALE;
float mapH = height * tileH * GdxGame.UNIT_SCALE;
float halfW = mapW * 0.5f;
float halfH = mapH * 0.5f;
float boxThickness = 0.5f;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.StaticBody;
bodyDef.position.setZero();
bodyDef.fixedRotation = true;
Body body = physicWorld.createBody(bodyDef);
// left edge
PolygonShape shape = new PolygonShape();
shape.setAsBox(boxThickness, halfH, new Vector2(-boxThickness, halfH), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
// right edge
shape = new PolygonShape();
shape.setAsBox(boxThickness, halfH, new Vector2(mapW + boxThickness, halfH), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
// bottom edge
shape = new PolygonShape();
shape.setAsBox(halfW, boxThickness, new Vector2(halfW, -boxThickness), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
// top edge
shape = new PolygonShape();
shape.setAsBox(halfW, boxThickness, new Vector2(halfW, mapH + boxThickness), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
}
private void loadTileLayer(TiledMapTileLayer tileLayer) {
for (int y = 0; y < tileLayer.getHeight(); y++) {
for (int x = 0; x < tileLayer.getWidth(); x++) {
TiledMapTileLayer.Cell cell = tileLayer.getCell(x, y);
if (cell == null) continue;
TiledMapTile tile = cell.getTile();
createBody(tile.getObjects(),
new Vector2(x, y),
DEFAULT_SCALING,
BodyType.StaticBody,
Vector2.Zero,
"environment");
}
public void onLoadTrigger(String triggerName, MapObject mapObject) {
if (mapObject instanceof RectangleMapObject rectMapObj) {
Entity entity = this.engine.createEntity();
Rectangle rect = rectMapObj.getRectangle();
addEntityTransform(
rect.getX(), rect.getY(), 0,
rect.getWidth(), rect.getHeight(),
1f, 1f,
0,
entity);
addEntityPhysic(
rectMapObj,
BodyDef.BodyType.StaticBody,
tmpVec2.set(rect.getX(), rect.getY()).scl(GdxGame.UNIT_SCALE),
entity);
entity.add(new Trigger(triggerName));
entity.add(new Tiled(rectMapObj));
this.engine.addEntity(entity);
} else {
throw new GdxRuntimeException("Unsupported map object type for trigger: " + mapObject.getClass().getSimpleName());
}
}
private void loadTriggerLayer(MapLayer triggerLayer) {
for (MapObject mapObject : triggerLayer.getObjects()) {
if (mapObject instanceof RectangleMapObject rectMapObj) {
Entity entity = this.engine.createEntity();
Rectangle rect = rectMapObj.getRectangle();
addEntityTransform(
rect.getX(), rect.getY(),
rect.getWidth(), rect.getHeight(),
1f, 1f,
0,
entity);
addEntityPhysic(
rectMapObj,
BodyType.StaticBody,
tmpVec2.set(rect.getX(), rect.getY()).scl(GdxGame.UNIT_SCALE),
entity);
this.engine.addEntity(entity);
} else {
throw new GdxRuntimeException("Unsupported trigger: " + mapObject.getClass().getSimpleName());
}
}
}
private void loadObjectLayer(MapLayer objectLayer) {
for (MapObject mapObject : objectLayer.getObjects()) {
if (mapObject instanceof TiledMapTileMapObject tileMapObject) {
spawnEntityOf(tileMapObject);
} else {
throw new GdxRuntimeException("Unsupported object: " + mapObject.getClass().getSimpleName());
}
}
}
private void spawnEntityOf(TiledMapTileMapObject tileMapObject) {
public void onLoadObject(TiledMapTileMapObject tileMapObject) {
Entity entity = this.engine.createEntity();
TiledMapTile tile = tileMapObject.getTile();
TextureRegion textureRegion = getTextureRegion(tile);
String classType = tile.getProperties().get("type", "", String.class);
float sortOffsetY = tile.getProperties().get("sortOffsetY", 0, Integer.class);
sortOffsetY *= GdxGame.UNIT_SCALE;
int z = tile.getProperties().get("z", 1, Integer.class);
addEntityTransform(
tileMapObject.getX(), tileMapObject.getY(),
tileMapObject.getX(), tileMapObject.getY(), z,
textureRegion.getRegionWidth(), textureRegion.getRegionHeight(),
tileMapObject.getScaleX(), tileMapObject.getScaleY(),
sortOffsetY,
@@ -190,6 +117,7 @@ public class TiledAshleySpawner {
entity.add(new Facing(FacingDirection.DOWN));
entity.add(new Fsm(entity));
entity.add(new Graphic(textureRegion, Color.WHITE.cpy()));
entity.add(new Tiled(tileMapObject));
this.engine.addEntity(entity);
}
@@ -258,8 +186,9 @@ public class TiledAshleySpawner {
AtlasAsset atlasAsset = AtlasAsset.valueOf(atlasAssetStr);
FileTextureData textureData = (FileTextureData) tile.getTextureRegion().getTexture().getTextureData();
String atlasKey = textureData.getFileHandle().nameWithoutExtension();
float speed = tile.getProperties().get("animationSpeed", 0f, Float.class);
entity.add(new Animation2D(atlasAsset, atlasKey, animationType, Animation.PlayMode.LOOP, 1f));
entity.add(new Animation2D(atlasAsset, atlasKey, animationType, Animation.PlayMode.LOOP, speed));
}
private void addEntityPhysic(MapObject mapObject, @SuppressWarnings("SameParameterValue") BodyType bodyType, Vector2 relativeTo, Entity entity) {
@@ -305,7 +234,7 @@ public class TiledAshleySpawner {
}
private static void addEntityTransform(
float x, float y,
float x, float y, int z,
float w, float h,
float scaleX, float scaleY,
float sortOffsetY,
@@ -318,7 +247,7 @@ public class TiledAshleySpawner {
position.scl(GdxGame.UNIT_SCALE);
size.scl(GdxGame.UNIT_SCALE);
entity.add(new Transform(position, 0, size, scaling, 0f, sortOffsetY));
entity.add(new Transform(position, z, size, scaling, 0f, sortOffsetY));
}
}

View File

@@ -1,19 +1,43 @@
package io.github.com.quillraven.tiled;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
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.Vector2;
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.GdxRuntimeException;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.asset.AssetService;
import io.github.com.quillraven.asset.MapAsset;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class TiledService {
private final AssetService assetService;
private Consumer<TiledMap> mapChangeConsumer;
private final World physicWorld;
private TiledMap currentMap;
public TiledService(AssetService assetService) {
private Consumer<TiledMap> mapChangeConsumer;
private BiConsumer<String, MapObject> loadTriggerConsumer;
private Consumer<TiledMapTileMapObject> loadObjectConsumer;
private LoadTileConsumer loadTileConsumer;
public TiledService(AssetService assetService, World physicWorld) {
this.assetService = assetService;
this.physicWorld = physicWorld;
this.mapChangeConsumer = null;
this.loadTriggerConsumer = null;
this.loadObjectConsumer = null;
this.loadTileConsumer = null;
this.currentMap = null;
}
@@ -29,6 +53,7 @@ public class TiledService {
}
this.currentMap = tiledMap;
loadMapObjects(tiledMap);
if (this.mapChangeConsumer != null) {
this.mapChangeConsumer.accept(tiledMap);
}
@@ -37,4 +62,116 @@ public class TiledService {
public void setMapChangeConsumer(Consumer<TiledMap> mapChangeConsumer) {
this.mapChangeConsumer = mapChangeConsumer;
}
public void setLoadObjectConsumer(Consumer<TiledMapTileMapObject> loadObjectConsumer) {
this.loadObjectConsumer = loadObjectConsumer;
}
public void setLoadTriggerConsumer(BiConsumer<String, MapObject> loadTriggerConsumer) {
this.loadTriggerConsumer = loadTriggerConsumer;
}
public void setLoadTileConsumer(LoadTileConsumer loadTileConsumer) {
this.loadTileConsumer = loadTileConsumer;
}
public void loadMapObjects(TiledMap tiledMap) {
for (MapLayer layer : tiledMap.getLayers()) {
if (layer instanceof TiledMapTileLayer tileLayer) {
loadTileLayer(tileLayer);
} else if ("objects".equals(layer.getName())) {
loadObjectLayer(layer);
} else if ("trigger".equals(layer.getName())) {
loadTriggerLayer(layer);
}
}
spawnMapBoundary(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 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);
float mapW = width * tileW * GdxGame.UNIT_SCALE;
float mapH = height * tileH * GdxGame.UNIT_SCALE;
float halfW = mapW * 0.5f;
float halfH = mapH * 0.5f;
float boxThickness = 0.5f;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.StaticBody;
bodyDef.position.setZero();
bodyDef.fixedRotation = true;
Body body = physicWorld.createBody(bodyDef);
// left edge
PolygonShape shape = new PolygonShape();
shape.setAsBox(boxThickness, halfH, new Vector2(-boxThickness, halfH), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
// right edge
shape = new PolygonShape();
shape.setAsBox(boxThickness, halfH, new Vector2(mapW + boxThickness, halfH), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
// bottom edge
shape = new PolygonShape();
shape.setAsBox(halfW, boxThickness, new Vector2(halfW, -boxThickness), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
// top edge
shape = new PolygonShape();
shape.setAsBox(halfW, boxThickness, new Vector2(halfW, mapH + boxThickness), 0f);
body.createFixture(shape, 0f).setFriction(0f);
shape.dispose();
}
private void loadTileLayer(TiledMapTileLayer tileLayer) {
if (loadTileConsumer == null) return;
for (int y = 0; y < tileLayer.getHeight(); y++) {
for (int x = 0; x < tileLayer.getWidth(); x++) {
TiledMapTileLayer.Cell cell = tileLayer.getCell(x, y);
if (cell == null) continue;
loadTileConsumer.accept(cell.getTile(), x, y);
}
}
}
private void loadTriggerLayer(MapLayer triggerLayer) {
if (loadTriggerConsumer == null) return;
for (MapObject mapObject : triggerLayer.getObjects()) {
if (mapObject.getName() == null || mapObject.getName().isBlank()) {
throw new GdxRuntimeException("Trigger must have a name: " + mapObject);
}
if (mapObject instanceof RectangleMapObject rectMapObj) {
loadTriggerConsumer.accept(mapObject.getName(), rectMapObj);
} else {
throw new GdxRuntimeException("Unsupported trigger: " + mapObject.getClass().getSimpleName());
}
}
}
private void loadObjectLayer(MapLayer objectLayer) {
if (loadObjectConsumer == null) return;
for (MapObject mapObject : objectLayer.getObjects()) {
if (mapObject instanceof TiledMapTileMapObject tileMapObject) {
loadObjectConsumer.accept(tileMapObject);
} else {
throw new GdxRuntimeException("Unsupported object: " + mapObject.getClass().getSimpleName());
}
}
}
@FunctionalInterface
public interface LoadTileConsumer {
void accept(TiledMapTile tile, float x, float y);
}
}

View File

@@ -33,7 +33,7 @@ public class GameViewModel extends ViewModel {
public void setLifePoints(int lifePoints) {
if (this.lifePoints != lifePoints) {
this.propertyChangeSupport.firePropertyChange(LIFE_POINTS, this.lifePoints, lifePoints);
if (this.lifePoints < lifePoints) {
if (this.lifePoints != 0 && this.lifePoints < lifePoints) {
audioService.playSound(SoundAsset.LIFE_REG);
}
}