diff --git a/assets/particles/boom.p b/assets/particles/boom.p new file mode 100644 index 0000000..dd6a31d --- /dev/null +++ b/assets/particles/boom.p @@ -0,0 +1,155 @@ +Sparks +- Delay - +active: false +- Duration - +lowMin: 0.0 +lowMax: 300.0 +- Count - +min: 200 +max: 200 +- Emission - +lowMin: 0.0 +lowMax: 0.0 +highMin: 90.0 +highMax: 90.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +- Life - +lowMin: 0.0 +lowMax: 0.0 +highMin: 500.0 +highMax: 500.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +independent: false +- Life Offset - +active: false +independent: false +- X Offset - +active: false +- Y Offset - +active: false +- Spawn Shape - +shape: point +- Spawn Width - +lowMin: 0.0 +lowMax: 0.0 +highMin: 0.0 +highMax: 0.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +- Spawn Height - +lowMin: 0.0 +lowMax: 0.0 +highMin: 0.0 +highMax: 0.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +- X Scale - +lowMin: 0.0 +lowMax: 0.0 +highMin: 30.0 +highMax: 30.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +- Y Scale - +active: false +- Velocity - +active: true +lowMin: 0.0 +lowMax: 0.0 +highMin: 30.0 +highMax: 285.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +- Angle - +active: true +lowMin: 0.0 +lowMax: 0.0 +highMin: 0.0 +highMax: 360.0 +relative: false +scalingCount: 1 +scaling0: 1.0 +timelineCount: 1 +timeline0: 0.0 +- Rotation - +active: true +lowMin: 0.0 +lowMax: 360.0 +highMin: -360.0 +highMax: 360.0 +relative: true +scalingCount: 2 +scaling0: 0.0 +scaling1: 1.0 +timelineCount: 2 +timeline0: 0.0 +timeline1: 1.0 +- Wind - +active: false +- Gravity - +active: true +lowMin: 0.0 +lowMax: 0.0 +highMin: 0.0 +highMax: 0.0 +relative: true +scalingCount: 2 +scaling0: 0.0 +scaling1: 1.0 +timelineCount: 2 +timeline0: 0.0 +timeline1: 1.0 +- Tint - +colorsCount: 3 +colors0: 0.93333334 +colors1: 1.0 +colors2: 0.3764706 +timelineCount: 1 +timeline0: 0.0 +- Transparency - +lowMin: 0.0 +lowMax: 0.0 +highMin: 1.0 +highMax: 1.0 +relative: false +scalingCount: 4 +scaling0: 0.0 +scaling1: 1.0 +scaling2: 0.75 +scaling3: 0.0 +timelineCount: 4 +timeline0: 0.0 +timeline1: 0.2 +timeline2: 0.8 +timeline3: 1.0 +- Options - +attached: false +continuous: false +aligned: false +additive: true +behind: false +premultipliedAlpha: false +spriteMode: single +- Image Paths - +particle-star.png + diff --git a/assets/particles/particle-star.png b/assets/particles/particle-star.png new file mode 100644 index 0000000..7da2529 Binary files /dev/null and b/assets/particles/particle-star.png differ diff --git a/core/src/main/java/org/lumijiez/bugger/GameContactListener.java b/core/src/main/java/org/lumijiez/bugger/GameContactListener.java new file mode 100644 index 0000000..be20e03 --- /dev/null +++ b/core/src/main/java/org/lumijiez/bugger/GameContactListener.java @@ -0,0 +1,57 @@ +package org.lumijiez.bugger; + +import com.badlogic.gdx.physics.box2d.*; +import org.lumijiez.bugger.entities.Entity; +import org.lumijiez.bugger.entities.enemies.EnemyEntity; +import org.lumijiez.bugger.entities.weapons.Arrow; + +public class GameContactListener implements ContactListener { + @Override + public void beginContact(Contact contact) { + Fixture fixtureA = contact.getFixtureA(); + Fixture fixtureB = contact.getFixtureB(); + + if (isArrow(fixtureA) && isArrow(fixtureB)) { + return; + } + + if (isArrow(fixtureA) && isEntity(fixtureB)) { + Arrow arrow = (Arrow) fixtureA.getBody().getUserData(); + EnemyEntity enemy = (EnemyEntity) fixtureB.getBody().getUserData(); + if (arrow != null) { + arrow.destroy(); + enemy.destroy(); + } + } + + if (isArrow(fixtureB) && isEntity(fixtureA)) { + Arrow arrow = (Arrow) fixtureB.getBody().getUserData(); + EnemyEntity enemy = (EnemyEntity) fixtureA.getBody().getUserData(); + if (arrow != null) { + arrow.destroy(); + enemy.destroy(); + } + } + } + + @Override + public void endContact(Contact contact) { + } + + @Override + public void preSolve(Contact contact, Manifold manifold) { + + } + + @Override + public void postSolve(Contact contact, ContactImpulse impulse) { + } + + private boolean isArrow(Fixture fixture) { + return fixture.getBody().getUserData() instanceof Arrow; + } + + private boolean isEntity(Fixture fixture) { + return fixture.getBody().getUserData() instanceof EnemyEntity; + } +} diff --git a/core/src/main/java/org/lumijiez/bugger/GameScreen.java b/core/src/main/java/org/lumijiez/bugger/GameScreen.java index d9260be..427cac0 100644 --- a/core/src/main/java/org/lumijiez/bugger/GameScreen.java +++ b/core/src/main/java/org/lumijiez/bugger/GameScreen.java @@ -1,13 +1,18 @@ package org.lumijiez.bugger; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; import com.badlogic.gdx.Screen; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.*; +import com.badlogic.gdx.utils.Array; +import org.lumijiez.bugger.entities.Entity; import org.lumijiez.bugger.entities.Player; +import org.lumijiez.bugger.entities.enemies.EnemyEntity; import org.lumijiez.bugger.entities.enemies.Wasp; +import org.lumijiez.bugger.entities.weapons.Arrow; import java.util.ArrayList; import java.util.List; @@ -15,10 +20,12 @@ import java.util.List; public class GameScreen implements Screen { private final World world; private final Player player; - public static final SpriteBatch spriteBatch = new SpriteBatch();; - private List enemies; - private float enemySpawnTimer = 0f; // Timer to manage enemy spawning - private static final float ENEMY_SPAWN_INTERVAL = 2f; + public static final SpriteBatch spriteBatch = new SpriteBatch(); + private final List enemies; + private Array arrows; + private Array entitiesToDestroy; + private float enemySpawnTimer = 0f; + private static final float ENEMY_SPAWN_INTERVAL = 0.5f; private final Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer(); public GameScreen() { @@ -26,6 +33,9 @@ public class GameScreen implements Screen { player = Player.getInstance(); player.setPlayer(world, 100, 100); enemies = new ArrayList<>(); + arrows = new Array<>(); + entitiesToDestroy = new Array<>(); + world.setContactListener(new GameContactListener()); } @Override @@ -38,24 +48,55 @@ public class GameScreen implements Screen { Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - world.step(1 / 2f, 6, 2); + world.step(1f, 6, 2); + + if (Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)) { + Arrow arrow = player.shootArrow(); + arrows.add(arrow); + } + + for (Arrow arrow : arrows) { + if (!arrow.isMarkedToDestroy()) { + arrow.update(delta); + arrow.render(); + } else { + entitiesToDestroy.add(arrow); + } + } + + for (EnemyEntity enemy : enemies) { + if (enemy.isMarkedToDestroy()) { + entitiesToDestroy.add(enemy); + } + } + + for (Entity entity : entitiesToDestroy) { + world.destroyBody(entity.getBody()); + if (entity instanceof Arrow) arrows.removeValue((Arrow) entity, true); + if (entity instanceof EnemyEntity) { + Gdx.app.log("EFFECT", "PLAYED"); + ParticleManager.getInstance().playEffect(entity.getBody().getPosition().x, entity.getBody().getPosition().y); + enemies.remove(entity); + } + } + entitiesToDestroy.clear(); + + ParticleManager.getInstance().update(delta); player.render(); + + + ParticleManager.getInstance().render(spriteBatch); + enemySpawnTimer += delta; if (enemySpawnTimer >= ENEMY_SPAWN_INTERVAL) { enemies.add(new Wasp(world, Player.getInstance().getPosition())); - enemySpawnTimer = 0; // Reset the timer + enemySpawnTimer = 0; } - // Move enemies towards the player - for (Wasp enemy : enemies) { + for (EnemyEntity enemy : enemies) { enemy.moveTowards(player.getPosition()); - } - - // Render player and enemies - player.render(); // Call the player's render method - for (Wasp enemy : enemies) { - enemy.render(); // Render each enemy + enemy.render(); } debugRenderer.render(world, spriteBatch.getProjectionMatrix()); diff --git a/core/src/main/java/org/lumijiez/bugger/ParticleManager.java b/core/src/main/java/org/lumijiez/bugger/ParticleManager.java new file mode 100644 index 0000000..719a7a2 --- /dev/null +++ b/core/src/main/java/org/lumijiez/bugger/ParticleManager.java @@ -0,0 +1,61 @@ +package org.lumijiez.bugger; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.g2d.ParticleEffect; +import com.badlogic.gdx.graphics.g2d.ParticleEffectPool; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.Array; + +public class ParticleManager { + private static ParticleManager instance; + private final ParticleEffectPool particleEffectPool; + private final Array activeEffects; + + private ParticleManager() { + ParticleEffect effect = new ParticleEffect(); + effect.load(Gdx.files.internal("particles/boom.p"), Gdx.files.internal("particles")); + effect.scaleEffect(0.2f); + particleEffectPool = new ParticleEffectPool(effect, 1, 20); + activeEffects = new Array<>(); + } + + public static ParticleManager getInstance() { + if (instance == null) { + instance = new ParticleManager(); + } + return instance; + } + + public void playEffect(float x, float y) { + ParticleEffectPool.PooledEffect newEffect = particleEffectPool.obtain(); + newEffect.setPosition(x, y); + newEffect.start(); + activeEffects.add(newEffect); + } + + public void update(float delta) { + for (int i = activeEffects.size - 1; i >= 0; i--) { + ParticleEffectPool.PooledEffect effect = activeEffects.get(i); + effect.update(delta); + if (effect.isComplete()) { + effect.free(); + activeEffects.removeIndex(i); + } + } + } + + public void render(SpriteBatch spriteBatch) { + spriteBatch.begin(); + for (ParticleEffectPool.PooledEffect effect : activeEffects) { + effect.draw(spriteBatch); + } + spriteBatch.end(); + } + + public void dispose() { + for (ParticleEffectPool.PooledEffect effect : activeEffects) { + effect.free(); + } + activeEffects.clear(); + } +} diff --git a/core/src/main/java/org/lumijiez/bugger/entities/Entity.java b/core/src/main/java/org/lumijiez/bugger/entities/Entity.java index b65870a..13c5407 100644 --- a/core/src/main/java/org/lumijiez/bugger/entities/Entity.java +++ b/core/src/main/java/org/lumijiez/bugger/entities/Entity.java @@ -12,6 +12,7 @@ public abstract class Entity { protected Sprite sprite; protected final float size; protected World world; + protected boolean markedToDestroy = false; public Entity(World world, String texturePath, float size) { this.world = world; @@ -49,4 +50,18 @@ public abstract class Entity { public Vector2 getPosition() { return body.getPosition(); } + + public void destroy() { + if (!markedToDestroy) { + markedToDestroy = true; + } + } + + public boolean isMarkedToDestroy() { + return markedToDestroy; + } + + public Body getBody() { + return body; + } } diff --git a/core/src/main/java/org/lumijiez/bugger/entities/Player.java b/core/src/main/java/org/lumijiez/bugger/entities/Player.java index 0aea905..3121d97 100644 --- a/core/src/main/java/org/lumijiez/bugger/entities/Player.java +++ b/core/src/main/java/org/lumijiez/bugger/entities/Player.java @@ -2,21 +2,16 @@ package org.lumijiez.bugger.entities; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Sprite; -import com.badlogic.gdx.physics.box2d.Body; -import com.badlogic.gdx.physics.box2d.BodyDef; -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.math.Vector2; +import org.lumijiez.bugger.entities.weapons.Arrow; public class Player extends Entity { private static Player instance; private final float speed = 5f; private Player() { - super(null, "images/wasp.png", 50f); // World will be set later + super(null, "images/wasp.png", 50f); } public static Player getInstance() { @@ -62,4 +57,16 @@ public class Player extends Entity { body.setTransform(body.getPosition(), angle * (float) Math.PI / 180f); sprite.setRotation(body.getAngle() * (180f / (float) Math.PI)); } + + public Arrow shootArrow() { + Vector2 direction = new Vector2(); + float mouseX = Gdx.input.getX(); + float mouseY = Gdx.input.getY(); + Vector2 mousePosition = new Vector2(mouseX, Gdx.graphics.getHeight() - mouseY); + direction.set(mousePosition).sub(getPosition()).nor(); + + Arrow arrow = new Arrow(world, getPosition(), direction); + arrow.body.setUserData(arrow); + return arrow; + } } diff --git a/core/src/main/java/org/lumijiez/bugger/entities/enemies/EnemyEntity.java b/core/src/main/java/org/lumijiez/bugger/entities/enemies/EnemyEntity.java new file mode 100644 index 0000000..e1748cb --- /dev/null +++ b/core/src/main/java/org/lumijiez/bugger/entities/enemies/EnemyEntity.java @@ -0,0 +1,25 @@ +package org.lumijiez.bugger.entities.enemies; + +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.World; +import org.lumijiez.bugger.entities.Entity; + +public class EnemyEntity extends Entity { + private final float speed = 50f; + + public EnemyEntity(World world, String texturePath, float size) { + super(world, texturePath, size); + } + + public void moveTowards(Vector2 target) { + Vector2 direction = target.cpy().sub(body.getPosition()).nor(); + body.setLinearVelocity(direction.scl(speed / 100f)); + + float angle = direction.angleDeg() + 270f; + body.setTransform(body.getPosition(), angle * (float) Math.PI / 180f); + } + + public void render() { + super.render(); + } +} diff --git a/core/src/main/java/org/lumijiez/bugger/entities/enemies/Wasp.java b/core/src/main/java/org/lumijiez/bugger/entities/enemies/Wasp.java index 50ae501..68b68b7 100644 --- a/core/src/main/java/org/lumijiez/bugger/entities/enemies/Wasp.java +++ b/core/src/main/java/org/lumijiez/bugger/entities/enemies/Wasp.java @@ -2,12 +2,12 @@ package org.lumijiez.bugger.entities.enemies; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.*; +import org.lumijiez.bugger.ParticleManager; import org.lumijiez.bugger.entities.Entity; import java.util.Random; -public class Wasp extends Entity { - private final float speed = 50f; +public class Wasp extends EnemyEntity { private static final Random random = new Random(); public Wasp(World world, Vector2 playerPosition) { @@ -17,17 +17,6 @@ public class Wasp extends Entity { float spawnX = playerPosition.x + (float) Math.cos(angle) * (spawnRadius + size); float spawnY = playerPosition.y + (float) Math.sin(angle) * (spawnRadius + size); this.body = createBody(spawnX, spawnY); - } - - public void moveTowards(Vector2 target) { - Vector2 direction = target.cpy().sub(body.getPosition()).nor(); - body.setLinearVelocity(direction.scl(speed / 100f)); - - float angle = direction.angleDeg() + 270f; - body.setTransform(body.getPosition(), angle * (float) Math.PI / 180f); - } - - public void render() { - super.render(); + this.body.setUserData(this); } } diff --git a/core/src/main/java/org/lumijiez/bugger/entities/weapons/Arrow.java b/core/src/main/java/org/lumijiez/bugger/entities/weapons/Arrow.java new file mode 100644 index 0000000..21bbbd8 --- /dev/null +++ b/core/src/main/java/org/lumijiez/bugger/entities/weapons/Arrow.java @@ -0,0 +1,49 @@ +package org.lumijiez.bugger.entities.weapons; + +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.*; +import org.lumijiez.bugger.entities.Entity; + +public class Arrow extends Entity { + private final float speed = 5000f; + private final float lifetime = 3f; + private float timeAlive = 0f; + + public Arrow(World world, Vector2 position, Vector2 direction) { + super(world, "images/wasp.png", 10f); + Vector2 offsetPosition = position.cpy().add(direction.nor().scl(size + 15f)); + + this.body = createBody(offsetPosition.x, offsetPosition.y); + this.body.setLinearVelocity(direction.nor().scl(speed)); + } + + protected Body createBody(float x, float y) { + BodyDef bodyDef = new BodyDef(); + bodyDef.position.set(x, y); + bodyDef.type = BodyDef.BodyType.DynamicBody; + bodyDef.gravityScale = 0; + + Body body = world.createBody(bodyDef); + PolygonShape shape = new PolygonShape(); + shape.setAsBox(size / 2, size / 2); + + FixtureDef fixtureDef = new FixtureDef(); + fixtureDef.shape = shape; + fixtureDef.isSensor = true; + body.createFixture(fixtureDef); + + shape.dispose(); + return body; + } + + public void update(float delta) { + timeAlive += delta; + if (timeAlive >= lifetime || isMarkedToDestroy()) { + destroy(); + } + } + + public void render() { + super.render(); + } +}