add attack training dummy (for QueryAABB example)

This commit is contained in:
Quillraven
2025-06-08 20:09:13 +02:00
parent d2e7b83f98
commit 33f986a82d
40 changed files with 654 additions and 126 deletions

View File

@@ -25,342 +25,496 @@ oak_tree/oak_tree
orig: 41, 63
offset: 0, 0
index: -1
player/idle_down
player/attack_down
rotate: false
xy: 86, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/idle_down
player/attack_down
rotate: false
xy: 2, 38
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/idle_down
player/attack_down
rotate: false
xy: 47, 105
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/idle_down
player/attack_down
rotate: false
xy: 86, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/idle_down
player/attack_left
rotate: false
xy: 122, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/idle_down
index: 0
player/attack_left
rotate: false
xy: 2, 2
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/idle_left
index: 1
player/attack_left
rotate: false
xy: 86, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/idle_left
index: 2
player/attack_left
rotate: false
xy: 122, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/idle_left
index: 3
player/attack_right
rotate: false
xy: 158, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/idle_left
index: 0
player/attack_right
rotate: false
xy: 122, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/idle_left
index: 1
player/attack_right
rotate: false
xy: 158, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/idle_left
index: 2
player/attack_right
rotate: false
xy: 194, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/idle_right
index: 3
player/attack_up
rotate: false
xy: 158, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/idle_right
player/attack_up
rotate: false
xy: 194, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/idle_right
player/attack_up
rotate: false
xy: 230, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/idle_right
player/attack_up
rotate: false
xy: 194, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/idle_right
rotate: false
xy: 230, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/idle_right
rotate: false
xy: 266, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/idle_up
rotate: false
xy: 230, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/idle_up
rotate: false
xy: 266, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/idle_up
rotate: false
xy: 302, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/idle_up
player/idle_down
rotate: false
xy: 266, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/idle_up
rotate: false
xy: 302, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/idle_up
index: 0
player/idle_down
rotate: false
xy: 338, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_down
index: 1
player/idle_down
rotate: false
xy: 302, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_down
index: 2
player/idle_down
rotate: false
xy: 338, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/walk_down
index: 3
player/idle_down
rotate: false
xy: 374, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/walk_down
index: 4
player/idle_down
rotate: false
xy: 338, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/walk_down
index: 5
player/idle_left
rotate: false
xy: 374, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/walk_down
index: 0
player/idle_left
rotate: false
xy: 410, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_left
index: 1
player/idle_left
rotate: false
xy: 374, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_left
index: 2
player/idle_left
rotate: false
xy: 410, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/walk_left
index: 3
player/idle_left
rotate: false
xy: 446, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/walk_left
index: 4
player/idle_left
rotate: false
xy: 410, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/walk_left
index: 5
player/idle_right
rotate: false
xy: 446, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/walk_left
index: 0
player/idle_right
rotate: false
xy: 446, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_right
index: 1
player/idle_right
rotate: false
xy: 47, 69
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_right
index: 2
player/idle_right
rotate: false
xy: 38, 33
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/idle_right
rotate: false
xy: 74, 33
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/idle_right
rotate: false
xy: 86, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/idle_up
rotate: false
xy: 122, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/idle_up
rotate: false
xy: 158, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/idle_up
rotate: false
xy: 194, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/idle_up
rotate: false
xy: 230, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/idle_up
rotate: false
xy: 266, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/idle_up
rotate: false
xy: 302, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_down
rotate: false
xy: 338, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_down
rotate: false
xy: 374, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/walk_down
rotate: false
xy: 410, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/walk_down
rotate: false
xy: 446, 113
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/walk_down
rotate: false
xy: 83, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/walk_down
rotate: false
xy: 119, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_left
rotate: false
xy: 155, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_left
rotate: false
xy: 191, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/walk_left
rotate: false
xy: 227, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/walk_left
rotate: false
xy: 263, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/walk_left
rotate: false
xy: 299, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/walk_left
rotate: false
xy: 335, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_right
rotate: false
xy: 371, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_right
rotate: false
xy: 407, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/walk_right
rotate: false
xy: 74, 33
xy: 443, 77
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/walk_right
rotate: false
xy: 86, 113
xy: 110, 41
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/walk_right
rotate: false
xy: 122, 113
xy: 110, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/walk_right
rotate: false
xy: 158, 113
xy: 146, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
player/walk_up
rotate: false
xy: 194, 113
xy: 182, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
player/walk_up
rotate: false
xy: 230, 113
xy: 218, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
player/walk_up
rotate: false
xy: 266, 113
xy: 254, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
player/walk_up
rotate: false
xy: 302, 113
xy: 290, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
player/walk_up
rotate: false
xy: 338, 113
xy: 326, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
player/walk_up
rotate: false
xy: 374, 113
xy: 362, 5
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 5
training_dummy/damaged_down
rotate: false
xy: 230, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
training_dummy/damaged_down
rotate: false
xy: 266, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 1
training_dummy/damaged_down
rotate: false
xy: 230, 149
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 2
training_dummy/damaged_down
rotate: false
xy: 266, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 3
training_dummy/damaged_down
rotate: false
xy: 302, 221
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 4
training_dummy/idle_down
rotate: false
xy: 302, 185
size: 32, 32
orig: 32, 32
offset: 0, 0
index: 0
trap/idle_down
rotate: false
xy: 38, 13
@@ -370,7 +524,7 @@ trap/idle_down
index: 0
trap/idle_down
rotate: false
xy: 410, 129
xy: 482, 217
size: 16, 16
orig: 16, 16
offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,18 +1,18 @@
<?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="17">
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="18" height="16" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="18">
<properties>
<property name="music" value="TOWN"/>
</properties>
<tileset firstgid="1" source="objects.tsx"/>
<tileset firstgid="8" source="tileset.tsx"/>
<tileset firstgid="9" source="tileset.tsx"/>
<layer id="1" name="ground" width="18" height="16">
<data encoding="base64" compression="zlib">
eJylkjsOwjAQRF3z6SI+HVBDzwGIa6BGqYEa+tAHLpCz+G5MJK80Wm2MgeJJibUz6511cM6twdJgA8IXTIyzEhTxew8OiqOh2YKBcV5EjwdoiSeoYs3O0PB/N+sKnKL2Bi7E1ejbeS5iHp48AvlUhi4Xnol9fKZWshSPBtQ92Qp6DzpTpjbqBenH9ZLpPeHJu2vVDngXnjK2emhSWYnPi3x+2ds5zsZ3sN5Nrhdryw/1+n3/yxCMFOOe2tTdpmCmmGf0fwPl7aDo
eJylkjsOwjAQRN3z6SI+HVBDzwGIa6BGqYEa+tAHLpCz+GxMJK80Wm2MgeJJibUz6511cM6twdJgA8IXTIyzEhTxew8OiqOh2YKBcV5EjwdoiSeoYs3O0PB/N+sKnKL2Bi7E1ejbeS5iHp48AvlUhi4Xnol9fKZWshSPBtQ92Qp6DzpTpjbqBenH9ZLpPeHJu2vVDngXnjK2emhSWYnPi3x+2ds5zsZ3sN5Nrhdryw/1+n3/yxCMFOOe2tTdpmCmmGf0fwNwWqII
</data>
</layer>
<layer id="3" name="water" width="18" height="16">
<data encoding="base64" compression="zlib">
eJxjYBgFo2AUjALKgQcQC0DZ7kTqAQAhOACg
eJxjYBgFo2AUjALKgScQC0LZHkTqAQAhxACj
</data>
</layer>
<objectgroup id="6" name="trigger">
@@ -36,5 +36,6 @@
<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"/>
<object id="17" gid="8" x="149" y="132" width="32" height="32"/>
</objectgroup>
</map>

View File

@@ -20,6 +20,17 @@
],
"valuesAsFlags": false
},
{
"id": 4,
"name": "BodyType",
"storageType": "string",
"type": "enum",
"values": [
"StaticBody",
"DynamicBody"
],
"valuesAsFlags": false
},
{
"color": "#ffa0a0a4",
"drawFill": true,
@@ -41,6 +52,22 @@
"type": "string",
"value": "OBJECTS"
},
{
"name": "bodyType",
"propertyType": "BodyType",
"type": "string",
"value": "DynamicBody"
},
{
"name": "damage",
"type": "float",
"value": 0
},
{
"name": "damageDelay",
"type": "float",
"value": 0
},
{
"name": "life",
"type": "int",

View File

@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.0" name="objects" tilewidth="80" tileheight="112" tilecount="5" columns="0">
<tileset version="1.10" tiledversion="1.11.2" name="objects" tilewidth="80" tileheight="112" tilecount="6" 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="damage" type="float" value="7"/>
<property name="damageDelay" type="float" value="0.2"/>
<property name="life" type="int" value="12"/>
<property name="lifeReg" type="float" value="0.25"/>
<property name="speed" type="float" value="3.5"/>
@@ -14,6 +16,26 @@
<object id="1" x="11" y="18" width="9" height="5">
<ellipse/>
</object>
<object id="2" name="attack_sensor_down" x="0" y="17" width="32" height="15">
<properties>
<property name="sensor" type="bool" value="true"/>
</properties>
</object>
<object id="3" name="attack_sensor_up" x="0" y="0" width="32" height="15">
<properties>
<property name="sensor" type="bool" value="true"/>
</properties>
</object>
<object id="4" name="attack_sensor_left" x="0" y="0" width="15" height="32">
<properties>
<property name="sensor" type="bool" value="true"/>
</properties>
</object>
<object id="5" name="attack_sensor_right" x="17" y="0" width="15" height="32">
<properties>
<property name="sensor" type="bool" value="true"/>
</properties>
</object>
</objectgroup>
</tile>
<tile id="2" type="Prop">
@@ -43,4 +65,17 @@
</properties>
<image source="objects/trap.png" width="16" height="16"/>
</tile>
<tile id="7" type="Object">
<properties>
<property name="animation" value="IDLE"/>
<property name="animationSpeed" type="float" value="1"/>
<property name="bodyType" propertytype="BodyType" value="StaticBody"/>
<property name="life" type="int" value="99999"/>
<property name="lifeReg" type="float" value="9999"/>
</properties>
<image source="objects/training_dummy.png" width="32" height="32"/>
<objectgroup draworder="index" id="2">
<object id="1" x="3" y="12" width="26" height="16"/>
</objectgroup>
</tile>
</tileset>

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
@@ -33,6 +34,7 @@ public class GdxGame extends Game {
private OrthographicCamera camera;
private Viewport viewport;
private GLProfiler glProfiler;
private FPSLogger fpsLogger;
private InputMultiplexer inputMultiplexer;
private final Map<Class<? extends Screen>, Screen> screenCache = new HashMap<>();
@@ -51,6 +53,7 @@ public class GdxGame extends Game {
glProfiler = new GLProfiler(Gdx.graphics);
glProfiler.enable();
fpsLogger = new FPSLogger();
addScreen(new LoadingScreen(this));
setScreen(LoadingScreen.class);
@@ -81,9 +84,8 @@ public class GdxGame extends Game {
super.render();
Gdx.graphics.setTitle("Mystic Tutorial " +
"- Draw Calls: " + glProfiler.getDrawCalls() + " " +
"- FPS: " + Gdx.graphics.getFramesPerSecond());
Gdx.graphics.setTitle("Mystic Tutorial - Draw Calls: " + glProfiler.getDrawCalls());
fpsLogger.log();
}
@Override

View File

@@ -5,6 +5,8 @@ import com.badlogic.gdx.ai.fsm.State;
import com.badlogic.gdx.ai.msg.Telegram;
import io.github.com.quillraven.component.Animation2D;
import io.github.com.quillraven.component.Animation2D.AnimationType;
import io.github.com.quillraven.component.Attack;
import io.github.com.quillraven.component.Damaged;
import io.github.com.quillraven.component.Fsm;
import io.github.com.quillraven.component.Move;
@@ -20,6 +22,18 @@ public enum AnimationState implements State<Entity> {
Move move = Move.MAPPER.get(entity);
if (move != null && !move.getDirection().isZero()) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(WALK);
return;
}
Attack attack = Attack.MAPPER.get(entity);
if (attack != null && attack.isAttacking()) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(ATTACK);
return;
}
Damaged damaged = Damaged.MAPPER.get(entity);
if (damaged != null) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(DAMAGED);
}
}
@@ -51,6 +65,54 @@ public enum AnimationState implements State<Entity> {
public void exit(Entity entity) {
}
@Override
public boolean onMessage(Entity entity, Telegram telegram) {
return false;
}
},
ATTACK {
@Override
public void enter(Entity entity) {
Animation2D.MAPPER.get(entity).setType(AnimationType.ATTACK);
}
@Override
public void update(Entity entity) {
Attack attack = Attack.MAPPER.get(entity);
if (attack.canAttack()) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(IDLE);
}
}
@Override
public void exit(Entity entity) {
}
@Override
public boolean onMessage(Entity entity, Telegram telegram) {
return false;
}
},
DAMAGED {
@Override
public void enter(Entity entity) {
Animation2D.MAPPER.get(entity).setType(AnimationType.DAMAGED);
}
@Override
public void update(Entity entity) {
Animation2D animation2D = Animation2D.MAPPER.get(entity);
if (animation2D.isFinished()) {
Fsm.MAPPER.get(entity).getAnimationFsm().changeState(IDLE);
}
}
@Override
public void exit(Entity entity) {
}
@Override
public boolean onMessage(Entity entity, Telegram telegram) {
return false;

View File

@@ -85,9 +85,16 @@ public class Animation2D implements Component {
return this.stateTime;
}
public boolean isFinished() {
return animation.isAnimationFinished(stateTime);
}
public enum AnimationType {
IDLE,
WALK;
WALK,
ATTACK,
DAMAGED,
;
private final String atlasKey;

View File

@@ -0,0 +1,39 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.ComponentMapper;
public class Attack implements Component {
public static final ComponentMapper<Attack> MAPPER = ComponentMapper.getFor(Attack.class);
private float damage;
private float damageDelay;
private float attackTimer;
public Attack(float damage, float damageDelay) {
this.damage = damage;
this.damageDelay = damageDelay;
this.attackTimer = 0f;
}
public boolean canAttack() {
return this.attackTimer == 0f;
}
public boolean isAttacking() {
return this.attackTimer > 0f;
}
public void startAttack() {
this.attackTimer = this.damageDelay;
}
public void decAttackTimer(float deltaTime) {
attackTimer = Math.max(0f, attackTimer - deltaTime);
}
public float getDamage() {
return damage;
}
}

View File

@@ -0,0 +1,22 @@
package io.github.com.quillraven.component;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.ComponentMapper;
public class Damaged implements Component {
public static final ComponentMapper<Damaged> MAPPER = ComponentMapper.getFor(Damaged.class);
private float damage;
public Damaged(float damage) {
this.damage = damage;
}
public void addDamage(float amount) {
this.damage += amount;
}
public float getDamage() {
return damage;
}
}

View File

@@ -19,8 +19,10 @@ import io.github.com.quillraven.audio.AudioService;
import io.github.com.quillraven.input.GameControllerState;
import io.github.com.quillraven.input.KeyboardController;
import io.github.com.quillraven.system.AnimationSystem;
import io.github.com.quillraven.system.AttackSystem;
import io.github.com.quillraven.system.CameraSystem;
import io.github.com.quillraven.system.ControllerSystem;
import io.github.com.quillraven.system.DamagedSystem;
import io.github.com.quillraven.system.FacingSystem;
import io.github.com.quillraven.system.FsmSystem;
import io.github.com.quillraven.system.LifeSystem;
@@ -67,7 +69,13 @@ public class GameScreen extends ScreenAdapter {
this.engine.addSystem(new PhysicMoveSystem());
this.engine.addSystem(new PhysicSystem(physicWorld, 1 / 60f));
this.engine.addSystem(new FacingSystem());
this.engine.addSystem(new AttackSystem(physicWorld));
this.engine.addSystem(new FsmSystem());
// DamagedSystem must run after FsmSystem to correctly
// detect when a damaged animation should be played.
// This is done by checking if an entity has a Damaged component,
// and this component is removed in the DamagedSystem.
this.engine.addSystem(new DamagedSystem());
this.engine.addSystem(new TriggerSystem(audioService));
this.engine.addSystem(new LifeSystem(this.viewModel));
this.engine.addSystem(new AnimationSystem(game.getAssetService()));

View File

@@ -0,0 +1,95 @@
package io.github.com.quillraven.system;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IteratingSystem;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.Shape.Type;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import io.github.com.quillraven.component.Attack;
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.Physic;
public class AttackSystem extends IteratingSystem {
public static final Rectangle attackAABB = new Rectangle();
private final World world;
private final Vector2 tmpVertex;
private Body attackerBody;
private float attackDamage;
public AttackSystem(World world) {
super(Family.all(Attack.class, Facing.class, Physic.class).get());
this.world = world;
this.tmpVertex = new Vector2();
this.attackerBody = null;
this.attackDamage = 0f;
}
@Override
protected void processEntity(Entity entity, float deltaTime) {
Attack attack = Attack.MAPPER.get(entity);
if (attack.canAttack()) return;
attack.decAttackTimer(deltaTime);
if (attack.canAttack()) {
FacingDirection facingDirection = Facing.MAPPER.get(entity).getDirection();
attackerBody = Physic.MAPPER.get(entity).getBody();
PolygonShape attackPolygonShape = getAttackFixture(attackerBody, facingDirection);
updateAttackAABB(attackerBody.getPosition(), attackPolygonShape);
this.attackDamage = attack.getDamage();
world.QueryAABB(this::attackCallback, attackAABB.x, attackAABB.y, attackAABB.width, attackAABB.height);
}
}
private boolean attackCallback(Fixture fixture) {
Body body = fixture.getBody();
if (body.equals(attackerBody)) return true;
if (!(body.getUserData() instanceof Entity entity)) return true;
Life life = Life.MAPPER.get(entity);
if (life == null) {
return true;
}
Damaged damaged = Damaged.MAPPER.get(entity);
if (damaged == null) {
entity.add(new Damaged(this.attackDamage));
} else {
damaged.addDamage(this.attackDamage);
}
return true;
}
private void updateAttackAABB(Vector2 bodyPosition, PolygonShape attackPolygonShape) {
attackPolygonShape.getVertex(0, tmpVertex);
tmpVertex.add(bodyPosition);
attackAABB.setPosition(tmpVertex.x, tmpVertex.y);
attackPolygonShape.getVertex(2, tmpVertex);
tmpVertex.add(bodyPosition);
attackAABB.setSize(tmpVertex.x, tmpVertex.y);
}
private PolygonShape getAttackFixture(Body body, FacingDirection direction) {
Array<Fixture> fixtureList = body.getFixtureList();
String fixtureName = "attack_sensor_" + direction.getAtlasKey();
for (Fixture fixture : fixtureList) {
if (fixtureName.equals(fixture.getUserData()) && Type.Polygon.equals(fixture.getShape().getType())) {
return (PolygonShape) fixture.getShape();
}
}
throw new GdxRuntimeException("Entity has no polygon attack sensor with userData '" + fixtureName + "'");
}
}

View File

@@ -4,6 +4,7 @@ import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IteratingSystem;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.component.Attack;
import io.github.com.quillraven.component.Controller;
import io.github.com.quillraven.component.Move;
import io.github.com.quillraven.input.Command;
@@ -30,6 +31,7 @@ public class ControllerSystem extends IteratingSystem {
case DOWN -> moveEntity(entity, 0f, -1f);
case LEFT -> moveEntity(entity, -1f, 0f);
case RIGHT -> moveEntity(entity, 1f, 0f);
case SELECT -> startEntityAttack(entity);
case CANCEL -> game.setScreen(MenuScreen.class);
}
}
@@ -46,6 +48,13 @@ public class ControllerSystem extends IteratingSystem {
controller.getReleasedCommands().clear();
}
private void startEntityAttack(Entity entity) {
Attack attack = Attack.MAPPER.get(entity);
if (attack != null && attack.canAttack()) {
attack.startAttack();
}
}
private void moveEntity(Entity entity, float dx, float dy) {
Move move = Move.MAPPER.get(entity);
if (move != null) {

View File

@@ -0,0 +1,25 @@
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 io.github.com.quillraven.component.Damaged;
import io.github.com.quillraven.component.Life;
public class DamagedSystem extends IteratingSystem {
public DamagedSystem() {
super(Family.all(Damaged.class).get());
}
@Override
protected void processEntity(Entity entity, float deltaTime) {
Damaged damaged = Damaged.MAPPER.get(entity);
entity.remove(Damaged.class);
Life life = Life.MAPPER.get(entity);
if (life != null) {
life.addLife(-damaged.getDamage());
}
}
}

View File

@@ -2,6 +2,9 @@ package io.github.com.quillraven.system;
import com.badlogic.ashley.core.EntitySystem;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Disposable;
@@ -9,10 +12,12 @@ import com.badlogic.gdx.utils.Disposable;
public class PhysicDebugRenderSystem extends EntitySystem implements Disposable {
private final World physicWorld;
private final Box2DDebugRenderer box2DDebugRenderer;
private final ShapeRenderer shapeRenderer;
private final Camera camera;
public PhysicDebugRenderSystem(World physicWorld, Camera camera) {
this.box2DDebugRenderer = new Box2DDebugRenderer();
this.shapeRenderer = new ShapeRenderer();
this.physicWorld = physicWorld;
this.camera = camera;
setProcessing(false);
@@ -21,10 +26,22 @@ public class PhysicDebugRenderSystem extends EntitySystem implements Disposable
@Override
public void update(float deltaTime) {
this.box2DDebugRenderer.render(physicWorld, camera.combined);
this.shapeRenderer.setProjectionMatrix(camera.combined);
this.shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
this.shapeRenderer.setColor(Color.RED);
Rectangle attackAABB = AttackSystem.attackAABB;
this.shapeRenderer.rect(
attackAABB.x,
attackAABB.y,
attackAABB.width - attackAABB.x,
attackAABB.height - attackAABB.y);
this.shapeRenderer.end();
}
@Override
public void dispose() {
this.box2DDebugRenderer.dispose();
this.shapeRenderer.dispose();
}
}

View File

@@ -10,6 +10,7 @@ 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.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.World;
import io.github.com.quillraven.component.Physic;
@@ -95,26 +96,28 @@ public class PhysicSystem extends IteratingSystem implements EntityListener, Con
@Override
public void beginContact(Contact contact) {
Object userDataA = contact.getFixtureA().getBody().getUserData();
Object userDataB = contact.getFixtureB().getBody().getUserData();
Fixture fixtureA = contact.getFixtureA();
Object userDataA = fixtureA.getBody().getUserData();
Fixture fixtureB = contact.getFixtureB();
Object userDataB = fixtureB.getBody().getUserData();
if (!(userDataA instanceof Entity entityA) || !(userDataB instanceof Entity entityB)) {
return;
}
playerTriggerContact(entityA, entityB);
playerTriggerContact(entityA, fixtureA, entityB, fixtureB);
}
private static void playerTriggerContact(Entity entityA, Entity entityB) {
private static void playerTriggerContact(Entity entityA, Fixture fixtureA, Entity entityB, Fixture fixtureB) {
Trigger trigger = Trigger.MAPPER.get(entityA);
boolean isPlayer = Player.MAPPER.get(entityB) != null;
boolean isPlayer = Player.MAPPER.get(entityB) != null && !fixtureB.isSensor();
if (trigger != null && isPlayer) {
trigger.setTriggeringEntity(entityB);
return;
}
trigger = Trigger.MAPPER.get(entityB);
isPlayer = Player.MAPPER.get(entityA) != null;
isPlayer = Player.MAPPER.get(entityA) != null && !fixtureA.isSensor();
if (trigger != null && isPlayer) {
trigger.setTriggeringEntity(entityA);
}

View File

@@ -17,6 +17,7 @@ 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.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.GdxRuntimeException;
@@ -25,6 +26,7 @@ import io.github.com.quillraven.asset.AssetService;
import io.github.com.quillraven.asset.AtlasAsset;
import io.github.com.quillraven.component.Animation2D;
import io.github.com.quillraven.component.Animation2D.AnimationType;
import io.github.com.quillraven.component.Attack;
import io.github.com.quillraven.component.CameraFollow;
import io.github.com.quillraven.component.Controller;
import io.github.com.quillraven.component.Facing;
@@ -92,7 +94,6 @@ public class TiledAshleyConfigurator {
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);
@@ -103,9 +104,10 @@ public class TiledAshleyConfigurator {
tileMapObject.getScaleX(), tileMapObject.getScaleY(),
sortOffsetY,
entity);
BodyType bodyType = getObjectBodyType(tile);
addEntityPhysic(
tile.getObjects(),
"Prop".equals(classType) ? BodyType.StaticBody : BodyType.DynamicBody,
bodyType,
Vector2.Zero,
entity);
addEntityAnimation(tile, entity);
@@ -114,6 +116,7 @@ public class TiledAshleyConfigurator {
addEntityCameraFollow(tileMapObject, entity);
addEntityLife(tile, entity);
addEntityPlayer(tileMapObject, entity);
addEntityAttack(tile, entity);
entity.add(new Facing(FacingDirection.DOWN));
entity.add(new Fsm(entity));
entity.add(new Graphic(textureRegion, Color.WHITE.cpy()));
@@ -122,6 +125,24 @@ public class TiledAshleyConfigurator {
this.engine.addEntity(entity);
}
private BodyType getObjectBodyType(TiledMapTile tile) {
String classType = tile.getProperties().get("type", "", String.class);
if ("Prop".equals(classType)) {
return BodyType.StaticBody;
}
String bodyTypeStr = tile.getProperties().get("bodyType", "DynamicBody", String.class);
return BodyType.valueOf(bodyTypeStr);
}
private void addEntityAttack(TiledMapTile tile, Entity entity) {
float damage = tile.getProperties().get("damage", 0f, Float.class);
if (damage == 0f) return;
float damageDelay = tile.getProperties().get("damageDelay", 0f, Float.class);
entity.add(new Attack(damage, damageDelay));
}
private void addEntityPlayer(TiledMapTileMapObject tileMapObject, Entity entity) {
if ("Player".equals(tileMapObject.getName())) {
entity.add(new Player());
@@ -227,7 +248,8 @@ public class TiledAshleyConfigurator {
body.setUserData(userData);
for (MapObject object : mapObjects) {
FixtureDef fixtureDef = TiledPhysics.fixtureDefOf(object, scaling, relativeTo);
body.createFixture(fixtureDef);
Fixture fixture = body.createFixture(fixtureDef);
fixture.setUserData(object.getName());
fixtureDef.shape.dispose();
}
return body;