fix report

This commit is contained in:
Daniel
2024-11-11 02:10:54 +02:00
parent bbbed72703
commit f7ccf4c1fb
35 changed files with 520 additions and 189 deletions

View File

@@ -0,0 +1,132 @@
# Structural Design Patterns
## Author: Schipschi Daniil / FAF-223
----
## Objectives:
* Learn about Structural Design Patterns
* Pick a domain to implement them in
* Implement at least 3 patterns in my code
## Used Design Patterns:
* Facade
* Bridge
* Flyweight
## Implementation
* **Facade**
> I used the ___Facade___ pattern to simplify the management of multiple game systems. The `GameSystemsFacade` class provides a single interface to handle all subsystems, reducing complexity in the main game loop.
>
> The facade manages these systems:
>
> - Projectiles
> - Visual effects
> - Enemies
> - Input
> - Camera
> - Particles
> - Cleanup
> - Interface
> - Player
>
> Here's the implementation:
> ```java
> public class GameSystemsFacade {
> private static GameSystemsFacade instance;
> private final ProjectileHandler projectileHandler;
> private final SpaceVFXHandler spaceVFXHandler;
> // ... other handlers ...
>
> public void update(float delta) {
> step();
> renderClear();
> spaceVFXHandler.render();
> projectileHandler.cycle(delta);
> // ... other updates ...
> }
> }
> ```
> The facade pattern reduces the complexity of system management by providing a single update method instead of requiring multiple system calls in the main game loop.
* **Bridge**
> I implemented the ___Bridge___ pattern in the enemy system to separate enemy types from their behaviors. This separation allows independent modification of both aspects without affecting each other.
>
> Here's the implementation:
> ```java
> public class EnemyEntity extends Entity {
> protected EnemyBehavior behavior; // <--- One Bridged Attribute
> protected EnemyType type; // <--- Second Bridged Attribute
>
> public EnemyEntity(World world, EnemyType type, Behaviors behaviorType,
> Vector2 playerPosition, TripleInt options) {
> super(world, type.getTexturePath(), type.getSize());
> this.type = type;
> this.behavior = behaviorType.createBehavior(options.one(),
> options.two(),
> options.thr());
> initializePosition(playerPosition);
> behavior.init(this);
> }
> }
> ```
>
> The benefits of this pattern include:
> - Independent creation of enemy types and behaviors
> - Easy addition of new enemy types or behaviors
> - Flexible combination of types and behaviors
* **Flyweight**
> I used the ___Flyweight___ pattern to optimize memory usage when handling multiple projectiles. The pattern shares common data between projectile instances instead of duplicating it.
>
> Here's the flyweight implementation:
> ```java
> public class ProjectileFlyweight {
> private static final Map<Boolean, ProjectileFlyweight> flyweights = new HashMap<>();
> private final Sprite sprite;
> private final float size;
> private final float defaultSpeed;
> private final boolean isEnemy;
>
> public static ProjectileFlyweight get(boolean isEnemy) {
> return flyweights.computeIfAbsent(isEnemy, ProjectileFlyweight::new);
> }
> }
> ```
>
> And its usage in projectiles:
> ```java
> public class Projectile extends Entity {
> private final ProjectileFlyweight flyweight;
>
> public Projectile(World world, boolean isEnemy) {
> super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f);
> this.flyweight = ProjectileFlyweight.get(isEnemy);
> }
> // ... other code
>
>
>
> }
> ```
>
> This pattern efficiently manages memory by sharing common resources between multiple projectile instances.
## Conclusions
The implementation of these structural patterns significantly improved the organization and efficiency of my game project:
The Facade pattern simplified system management by providing a unified interface for all game subsystems. This reduced complexity in the main game loop and made the codebase more maintainable.
The Bridge pattern created a flexible enemy system by separating enemy types from their behaviors. This separation allows for independent modification and extension of both aspects, making the system more modular and easier to expand.
The Flyweight pattern optimized memory usage in the projectile system by sharing common resources between instances. This optimization is particularly important when dealing with numerous projectiles simultaneously.
These patterns work together to create a more organized, efficient, and maintainable game architecture. The implementation demonstrates the practical benefits of using structural patterns to solve common game development challenges.

View File

@@ -1,11 +1,12 @@
package org.lumijiez.bugger; package org.lumijiez.bugger;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer; import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.physics.box2d.World;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.handlers.*; import org.lumijiez.bugger.handlers.*;
import org.lumijiez.bugger.util.GameSystemsFacade; import org.lumijiez.bugger.util.GameSystemsFacade;
@@ -20,6 +21,18 @@ public class Bugger {
public static int kills = 0; public static int kills = 0;
private Bugger() { private Bugger() {
spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0,
Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
uiBatch.getProjectionMatrix().setToOrtho2D(0, 0,
Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
shapeRenderer.getProjectionMatrix().setToOrtho2D(0, 1,
Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
Player.getInstance().setPlayer(world, 100, 100); Player.getInstance().setPlayer(world, 100, 100);
this.world.setContactListener(new CollisionHandler()); this.world.setContactListener(new CollisionHandler());
} }
@@ -40,6 +53,12 @@ public class Bugger {
CleanupHandler.getInstance().disposeAll(); CleanupHandler.getInstance().disposeAll();
} }
public void resize(int width, int height) {
spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
uiBatch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
shapeRenderer.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
}
public World getWorld() { public World getWorld() {
return world; return world;
} }

View File

@@ -18,6 +18,7 @@ public class GameScreen implements Screen {
@Override @Override
public void resize(int w, int h) { public void resize(int w, int h) {
bugger.resize(w, h);
} }
@Override @Override

View File

@@ -8,7 +8,7 @@ public class Main extends Game {
@Override @Override
public void create() { public void create() {
setScreen(new GameScreen()); setScreen(new GameScreen());
Gdx.graphics.setResizable(false); Gdx.graphics.setResizable(true);
Gdx.graphics.setTitle("Bugger"); Gdx.graphics.setTitle("Bugger");
} }
} }

View File

@@ -1,20 +0,0 @@
package org.lumijiez.bugger.entities.enemies;
public enum Enemies {
STALKER("Stalker"),
WASP("Wasp"),
ULTRON("Ultron"),
GOLEM("Golem"),
STELLAR("Stellar");
private final String className;
Enemies(String className) {
this.className = className;
}
public String getClassName() {
return className;
}
}

View File

@@ -2,46 +2,45 @@ package org.lumijiez.bugger.entities.enemies;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.physics.box2d.World;
import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Entity; import org.lumijiez.bugger.entities.Entity;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.enemies.behaviors.Behaviors;
import org.lumijiez.bugger.handlers.EnemyProjectileHandler; import org.lumijiez.bugger.entities.enemies.behaviors.EnemyBehavior;
import org.lumijiez.bugger.entities.enemies.types.EnemyType;
import org.lumijiez.bugger.util.data.TripleInt;
import java.util.Random;
public class EnemyEntity extends Entity { public class EnemyEntity extends Entity {
private float shootTimer = 0.0f; private static final Random random = new Random();
protected EnemyBehavior behavior;
protected EnemyType type;
public EnemyEntity(World world, String texturePath, float size) { public EnemyEntity(World world, EnemyType type, Behaviors behaviorType, Vector2 playerPosition, TripleInt options) {
super(world, texturePath, size); super(world, type.getTexturePath(), type.getSize());
this.type = type;
this.behavior = behaviorType.createBehavior(options.one(), options.two(), options.thr());
initializePosition(playerPosition);
behavior.init(this);
}
private void initializePosition(Vector2 playerPosition) {
float angle = random.nextFloat() * 2 * (float) Math.PI;
float spawnX = playerPosition.x + (float) Math.cos(angle) * (type.getSpawnRadius() + type.getSize());
float spawnY = playerPosition.y + (float) Math.sin(angle) * (type.getSpawnRadius() + type.getSize());
this.body = createBody(spawnX, spawnY);
this.body.setUserData(this);
} }
public void update() { public void update() {
Vector2 playerPos = Player.getInstance().getPosition(); if (behavior != null) {
follow(playerPos); behavior.update(this);
}
private void follow(Vector2 playerPos) {
Vector2 direction = playerPos.cpy().sub(body.getPosition()).nor();
float speed = 10f;
body.setLinearVelocity(direction.scl(speed));
float angle = direction.angleDeg() + 270f;
body.setTransform(body.getPosition(), angle * (float) Math.PI / 180f);
shootTimer += Bugger.deltaTime;
float shootCooldown = 2.0f;
if (shootTimer >= shootCooldown) {
EnemyProjectileHandler.getInstance().shootEnemyProjectile(this.body.getPosition(), 50f);
shootTimer = 0.0f;
} }
} }
public void cycle() { public void cycle() {
update(); update();
render(); render(0, 0);
}
public void render() {
super.render(0,0);
} }
} }

View File

@@ -1,20 +0,0 @@
package org.lumijiez.bugger.entities.enemies;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import java.util.Random;
public class Golem extends EnemyEntity {
private static final Random random = new Random();
public Golem(World world, Vector2 playerPosition) {
super(world, "images/golem.png", 30f);
float spawnRadius = 100;
float angle = random.nextFloat() * 2 * (float) Math.PI;
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);
this.body.setUserData(this);
}
}

View File

@@ -1,20 +0,0 @@
package org.lumijiez.bugger.entities.enemies;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import java.util.Random;
public class Stalker extends EnemyEntity {
private static final Random random = new Random();
public Stalker(World world, Vector2 playerPosition) {
super(world, "images/stalker.png", 10f);
float spawnRadius = 100;
float angle = random.nextFloat() * 2 * (float) Math.PI;
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);
this.body.setUserData(this);
}
}

View File

@@ -1,20 +0,0 @@
package org.lumijiez.bugger.entities.enemies;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import java.util.Random;
public class Stellar extends EnemyEntity {
private static final Random random = new Random();
public Stellar(World world, Vector2 playerPosition) {
super(world, "images/stellar.png", 15f);
float spawnRadius = 100;
float angle = random.nextFloat() * 2 * (float) Math.PI;
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);
this.body.setUserData(this);
}
}

View File

@@ -1,20 +0,0 @@
package org.lumijiez.bugger.entities.enemies;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import java.util.Random;
public class Ultron extends EnemyEntity {
private static final Random random = new Random();
public Ultron(World world, Vector2 playerPosition) {
super(world, "images/ultron.png", 10f);
float spawnRadius = 100;
float angle = random.nextFloat() * 2 * (float) Math.PI;
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);
this.body.setUserData(this);
}
}

View File

@@ -1,20 +0,0 @@
package org.lumijiez.bugger.entities.enemies;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*;
import java.util.Random;
public class Wasp extends EnemyEntity {
private static final Random random = new Random();
public Wasp(World world, Vector2 playerPosition) {
super(world, "images/wasp.png", 5f);
float spawnRadius = 100;
float angle = random.nextFloat() * 2 * (float) Math.PI;
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);
this.body.setUserData(this);
}
}

View File

@@ -0,0 +1,21 @@
package org.lumijiez.bugger.entities.enemies.behaviors;
public enum Behaviors {
FOLLOW(FollowBehavior.class),
DEFENSIVE(DefensiveBehavior.class);
private final Class<? extends EnemyBehavior> behaviorClass;
Behaviors(Class<? extends EnemyBehavior> behaviorClass) {
this.behaviorClass = behaviorClass;
}
public EnemyBehavior createBehavior(float param1, float param2, float param3) {
try {
return behaviorClass.getDeclaredConstructor(float.class, float.class, float.class)
.newInstance(param1, param2, param3);
} catch (Exception e) {
throw new RuntimeException("Failed to create behavior: " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,52 @@
package org.lumijiez.bugger.entities.enemies.behaviors;
import com.badlogic.gdx.math.Vector2;
import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.entities.enemies.EnemyEntity;
import org.lumijiez.bugger.handlers.EnemyProjectileHandler;
public class DefensiveBehavior implements EnemyBehavior {
private final float preferredDistance;
private final float moveSpeed;
private final float shootCooldown;
private float shootTimer = 0.0f;
public DefensiveBehavior(float preferredDistance, float moveSpeed, float shootCooldown) {
this.preferredDistance = preferredDistance;
this.moveSpeed = moveSpeed;
this.shootCooldown = shootCooldown;
}
@Override
public void init(EnemyEntity enemy) {
shootTimer = 0.0f;
}
@Override
public void update(EnemyEntity enemy) {
Vector2 playerPos = Player.getInstance().getPosition();
Vector2 enemyPos = enemy.getPosition();
Vector2 direction = playerPos.cpy().sub(enemyPos);
float currentDistance = direction.len();
float distanceDiff = currentDistance - preferredDistance;
direction.nor();
if (Math.abs(distanceDiff) > 1.0f) {
Vector2 movement = direction.scl(Math.min(moveSpeed, Math.abs(distanceDiff) * 0.5f));
enemy.getBody().setLinearVelocity(movement.cpy().scl(Math.signum(distanceDiff)));
} else {
enemy.getBody().setLinearVelocity(Vector2.Zero);
}
float angle = direction.angleDeg() + 270f;
enemy.getBody().setTransform(enemyPos, angle * (float) Math.PI / 180f);
shootTimer += Bugger.deltaTime;
if (shootTimer >= shootCooldown && Math.abs(distanceDiff) < 10f) {
EnemyProjectileHandler.getInstance().shootEnemyProjectile(enemyPos, 40f);
shootTimer = 0.0f;
}
}
}

View File

@@ -0,0 +1,8 @@
package org.lumijiez.bugger.entities.enemies.behaviors;
import org.lumijiez.bugger.entities.enemies.EnemyEntity;
public interface EnemyBehavior {
void update(EnemyEntity enemy);
void init(EnemyEntity enemy);
}

View File

@@ -0,0 +1,42 @@
package org.lumijiez.bugger.entities.enemies.behaviors;
import com.badlogic.gdx.math.Vector2;
import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.entities.enemies.EnemyEntity;
import org.lumijiez.bugger.handlers.EnemyProjectileHandler;
public class FollowBehavior implements EnemyBehavior {
private float shootTimer = 0.0f;
private final float shootCooldown;
private final float moveSpeed;
private final float projectileSpeed;
public FollowBehavior(float shootCooldown, float moveSpeed, float projectileSpeed) {
this.shootCooldown = shootCooldown;
this.moveSpeed = moveSpeed;
this.projectileSpeed = projectileSpeed;
}
@Override
public void init(EnemyEntity enemy) {
shootTimer = 0.0f;
}
@Override
public void update(EnemyEntity enemy) {
Vector2 playerPos = Player.getInstance().getPosition();
Vector2 direction = playerPos.cpy().sub(enemy.getPosition()).nor();
enemy.getBody().setLinearVelocity(direction.scl(moveSpeed));
float angle = direction.angleDeg() + 270f;
enemy.getBody().setTransform(enemy.getPosition(), angle * (float) Math.PI / 180f);
shootTimer += Bugger.deltaTime;
if (shootTimer >= shootCooldown) {
EnemyProjectileHandler.getInstance().shootEnemyProjectile(enemy.getPosition(), projectileSpeed);
shootTimer = 0.0f;
}
}
}

View File

@@ -0,0 +1,7 @@
package org.lumijiez.bugger.entities.enemies.types;
public interface EnemyType {
String getTexturePath();
float getSize();
float getSpawnRadius();
}

View File

@@ -0,0 +1,34 @@
package org.lumijiez.bugger.entities.enemies.types;
public enum EnemyTypes implements EnemyType {
STALKER("images/stalker.png", 10f, 100f),
GOLEM("images/golem.png", 30f, 120f),
STELLAR("images/stellar.png", 15f, 100f),
WASP("images/wasp.png", 8f, 80f),
ULTRON("images/ultron.png", 20f, 150f);
private final String texturePath;
private final float size;
private final float spawnRadius;
EnemyTypes(String texturePath, float size, float spawnRadius) {
this.texturePath = texturePath;
this.size = size;
this.spawnRadius = spawnRadius;
}
@Override
public String getTexturePath() {
return texturePath;
}
@Override
public float getSize() {
return size;
}
@Override
public float getSpawnRadius() {
return spawnRadius;
}
}

View File

@@ -1,4 +1,4 @@
package org.lumijiez.bugger.entities; package org.lumijiez.bugger.entities.player;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.*; import com.badlogic.gdx.physics.box2d.*;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import org.lumijiez.bugger.entities.Entity;
import org.lumijiez.bugger.handlers.CameraHandler; import org.lumijiez.bugger.handlers.CameraHandler;
import static org.lumijiez.bugger.Bugger.shapeRenderer; import static org.lumijiez.bugger.Bugger.shapeRenderer;

View File

@@ -1,27 +1,35 @@
package org.lumijiez.bugger.entities.weapons; package org.lumijiez.bugger.entities.weapons;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*; import com.badlogic.gdx.physics.box2d.*;
import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Entity; import org.lumijiez.bugger.entities.Entity;
import org.lumijiez.bugger.pools.ProjectileFlyweight;
import static org.lumijiez.bugger.pools.ProjectileFlyweight.ENEMY_TEXTURE;
import static org.lumijiez.bugger.pools.ProjectileFlyweight.PLAYER_TEXTURE;
public class Projectile extends Entity { public class Projectile extends Entity {
private static final String ENEMY_TEXTURE = "images/enemyblaze.png";
private static final String PLAYER_TEXTURE = "images/blaze.png";
protected float timeAlive = 0f; protected float timeAlive = 0f;
protected boolean isEnemy; protected boolean isEnemy;
protected float speed = 5000f; protected float speed;
private final ProjectileFlyweight flyweight;
public Projectile(World world, boolean isEnemy) { public Projectile(World world, boolean isEnemy) {
super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f); super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f);
this.flyweight = ProjectileFlyweight.get(isEnemy);
this.isEnemy = isEnemy; this.isEnemy = isEnemy;
this.speed = flyweight.getDefaultSpeed();
this.body = createBody(0, 0); this.body = createBody(0, 0);
} }
public Projectile(World world, Vector2 position, Vector2 direction, boolean isEnemy) { public Projectile(World world, Vector2 position, Vector2 direction, boolean isEnemy) {
super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f); super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f);
this.flyweight = ProjectileFlyweight.get(isEnemy);
Vector2 offsetPosition = position.cpy().add(direction.nor().scl(5f + 1f)); Vector2 offsetPosition = position.cpy().add(direction.nor().scl(5f + 1f));
this.isEnemy = isEnemy; this.isEnemy = isEnemy;
this.speed = flyweight.getDefaultSpeed();
this.body = createBody(offsetPosition.x, offsetPosition.y); this.body = createBody(offsetPosition.x, offsetPosition.y);
this.body.setTransform(offsetPosition, (float) (direction.angleRad() + Math.toRadians(270f))); this.body.setTransform(offsetPosition, (float) (direction.angleRad() + Math.toRadians(270f)));
this.body.setLinearVelocity(direction.nor().scl(speed)); this.body.setLinearVelocity(direction.nor().scl(speed));
@@ -30,6 +38,7 @@ public class Projectile extends Entity {
public Projectile(World world, Vector2 position, Vector2 direction, boolean isEnemy, float speed) { public Projectile(World world, Vector2 position, Vector2 direction, boolean isEnemy, float speed) {
super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f); super(world, isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE, 5f);
this.flyweight = ProjectileFlyweight.get(isEnemy);
Vector2 offsetPosition = position.cpy().add(direction.nor().scl(5f + 1f)); Vector2 offsetPosition = position.cpy().add(direction.nor().scl(5f + 1f));
this.isEnemy = isEnemy; this.isEnemy = isEnemy;
this.speed = speed; this.speed = speed;
@@ -48,7 +57,7 @@ public class Projectile extends Entity {
Body body = world.createBody(bodyDef); Body body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape(); PolygonShape shape = new PolygonShape();
shape.setAsBox(size / 2, size + 5); shape.setAsBox(flyweight.getSize() / 2, flyweight.getSize() + 5);
FixtureDef fixtureDef = new FixtureDef(); FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape; fixtureDef.shape = shape;
@@ -59,6 +68,7 @@ public class Projectile extends Entity {
return body; return body;
} }
public void init(Vector2 position, Vector2 direction, boolean isEnemy) { public void init(Vector2 position, Vector2 direction, boolean isEnemy) {
this.isEnemy = isEnemy; this.isEnemy = isEnemy;
this.body.setTransform(position, (float) (direction.angleRad() + Math.toRadians(270f))); this.body.setTransform(position, (float) (direction.angleRad() + Math.toRadians(270f)));
@@ -87,7 +97,15 @@ public class Projectile extends Entity {
} }
public void render() { public void render() {
super.render(0, 5); Sprite spriteToRender = flyweight.getSprite();
spriteToRender.setOrigin(flyweight.getSize() / 2, flyweight.getSize() / 2);
spriteToRender.setSize(flyweight.getSize(), flyweight.getSize() + 5);
spriteToRender.setPosition(body.getPosition().x - flyweight.getSize() / 2,
body.getPosition().y - flyweight.getSize() / 2);
spriteToRender.setRotation(body.getAngle() * (180f / (float) Math.PI));
Bugger.spriteBatch.begin();
spriteToRender.draw(Bugger.spriteBatch);
Bugger.spriteBatch.end();
} }
@Override @Override

View File

@@ -3,6 +3,9 @@ package org.lumijiez.bugger.factories;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.physics.box2d.World;
import org.lumijiez.bugger.entities.enemies.*; import org.lumijiez.bugger.entities.enemies.*;
import org.lumijiez.bugger.entities.enemies.behaviors.Behaviors;
import org.lumijiez.bugger.entities.enemies.types.EnemyTypes;
import org.lumijiez.bugger.util.data.TripleInt;
import java.util.Random; import java.util.Random;
@@ -10,22 +13,22 @@ public class EnemyFactory {
private static final Random random = new Random(); private static final Random random = new Random();
public static EnemyEntity createRandomEnemy(World world, Vector2 position) { public static EnemyEntity createRandomEnemy(World world, Vector2 position) {
int enemyType = random.nextInt(Enemies.values().length); int enemyType = random.nextInt(EnemyTypes.values().length);
return getEnemyEntity(Enemies.values()[enemyType], world, position); return getEnemyEntity(EnemyTypes.values()[enemyType], world, position);
} }
public static EnemyEntity createEnemy(Enemies enemyType, World world, Vector2 position) { public static EnemyEntity createEnemy(EnemyTypes enemyType, World world, Vector2 position) {
return getEnemyEntity(enemyType, world, position); return getEnemyEntity(enemyType, world, position);
} }
private static EnemyEntity getEnemyEntity(Enemies enemy, World world, Vector2 position) { private static EnemyEntity getEnemyEntity(EnemyTypes enemy, World world, Vector2 position) {
return switch (enemy) { return switch (enemy) {
case STALKER -> new Stalker(world, position); case STALKER -> new EnemyEntity(world, EnemyTypes.STALKER, Behaviors.FOLLOW, position, new TripleInt(10, 10, 10));
case WASP -> new Wasp(world, position); case WASP -> new EnemyEntity(world, EnemyTypes.WASP, Behaviors.FOLLOW, position, new TripleInt(10, 10, 10));
case ULTRON -> new Ultron(world, position); case ULTRON -> new EnemyEntity(world, EnemyTypes.ULTRON, Behaviors.FOLLOW, position, new TripleInt(10, 10, 10));
case GOLEM -> new Golem(world, position); case GOLEM -> new EnemyEntity(world, EnemyTypes.GOLEM, Behaviors.FOLLOW, position, new TripleInt(10, 10, 10));
case STELLAR -> new Stellar(world, position); case STELLAR -> new EnemyEntity(world, EnemyTypes.STELLAR, Behaviors.DEFENSIVE, position, new TripleInt(150, 10, 1));
}; };
} }
} }

View File

@@ -3,7 +3,7 @@ package org.lumijiez.bugger.handlers;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.OrthographicCamera;
import org.lumijiez.bugger.Bugger; import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
public class CameraHandler { public class CameraHandler {
private static CameraHandler instance; private static CameraHandler instance;
@@ -12,11 +12,11 @@ public class CameraHandler {
private final OrthographicCamera uiCam; private final OrthographicCamera uiCam;
private CameraHandler() { private CameraHandler() {
cam = new OrthographicCamera(160, 90); cam = new OrthographicCamera((float) (Gdx.graphics.getWidth() / 3), (float) (Gdx.graphics.getHeight() / 3));
cam.position.set(Player.getInstance().getPosition().x / 2f, Player.getInstance().getPosition().y / 2f, 0); cam.position.set(Player.getInstance().getPosition().x / 2f, Player.getInstance().getPosition().y / 2f, 0);
cam.update(); cam.update();
uiCam = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); uiCam = new OrthographicCamera((float) (Gdx.graphics.getWidth() * 1.5), (float) (Gdx.graphics.getHeight() * 1.5));
uiCam.position.set(uiCam.viewportWidth / 2, uiCam.viewportHeight / 2, 0); uiCam.position.set(uiCam.viewportWidth / 2, uiCam.viewportHeight / 2, 0);
uiCam.update(); uiCam.update();
} }

View File

@@ -1,13 +1,12 @@
package org.lumijiez.bugger.handlers; package org.lumijiez.bugger.handlers;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.physics.box2d.*; import com.badlogic.gdx.physics.box2d.*;
import org.lumijiez.bugger.Bugger; import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.entities.enemies.EnemyEntity; import org.lumijiez.bugger.entities.enemies.EnemyEntity;
import org.lumijiez.bugger.entities.weapons.Projectile; import org.lumijiez.bugger.entities.weapons.Projectile;
import org.lumijiez.bugger.util.CollisionAction; import org.lumijiez.bugger.util.functional.CollisionAction;
import org.lumijiez.bugger.util.CollisionPair; import org.lumijiez.bugger.util.data.CollisionPair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -48,8 +47,11 @@ public class CollisionHandler implements ContactListener {
ParticleHandler instance = ParticleHandler.getInstance(); ParticleHandler instance = ParticleHandler.getInstance();
Player player = Player.getInstance(); Player player = Player.getInstance();
float viewWidth = CameraHandler.getInstance().getUICamera().viewportWidth;
float viewHeight = CameraHandler.getInstance().getUICamera().viewportHeight;
instance.playSmallBoom(player.getPosition().x, player.getPosition().y); instance.playSmallBoom(player.getPosition().x, player.getPosition().y);
instance.playHit(Gdx.graphics.getWidth() - 100, Gdx.graphics.getHeight() - 60); instance.playHit( viewWidth - 100, viewHeight - 60);
} }
private void handleEnemyHit(Projectile ray, EnemyEntity enemy) { private void handleEnemyHit(Projectile ray, EnemyEntity enemy) {

View File

@@ -2,7 +2,7 @@ package org.lumijiez.bugger.handlers;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.entities.weapons.Projectile; import org.lumijiez.bugger.entities.weapons.Projectile;
import org.lumijiez.bugger.pools.ProjectilePool; import org.lumijiez.bugger.pools.ProjectilePool;

View File

@@ -3,7 +3,7 @@ package org.lumijiez.bugger.handlers;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
public class InputHandler { public class InputHandler {
private static InputHandler instance; private static InputHandler instance;
@@ -26,6 +26,10 @@ public class InputHandler {
InterfaceHandler.getInstance().toggleDebug(); InterfaceHandler.getInstance().toggleDebug();
} }
if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) {
Gdx.app.exit();
}
if (Gdx.input.isButtonJustPressed(Input.Buttons.RIGHT)) { if (Gdx.input.isButtonJustPressed(Input.Buttons.RIGHT)) {
float numRays = 8; float numRays = 8;
float radius = 0.5f; float radius = 0.5f;

View File

@@ -5,7 +5,7 @@ import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import org.lumijiez.bugger.Bugger; import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
public class InterfaceHandler { public class InterfaceHandler {
private final BitmapFont bitmapFont; private final BitmapFont bitmapFont;

View File

@@ -7,7 +7,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import org.lumijiez.bugger.Bugger; import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
public class ParticleHandler { public class ParticleHandler {
private static ParticleHandler instance; private static ParticleHandler instance;

View File

@@ -4,7 +4,7 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.entities.weapons.Projectile; import org.lumijiez.bugger.entities.weapons.Projectile;
import org.lumijiez.bugger.pools.ProjectilePool; import org.lumijiez.bugger.pools.ProjectilePool;

View File

@@ -3,14 +3,14 @@ package org.lumijiez.bugger.handlers;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.physics.box2d.World;
import org.lumijiez.bugger.Bugger; import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.entities.enemies.Enemies; import org.lumijiez.bugger.entities.enemies.types.EnemyTypes;
import org.lumijiez.bugger.factories.EnemyFactory; import org.lumijiez.bugger.factories.EnemyFactory;
public class SpawnerHandler { public class SpawnerHandler {
private static SpawnerHandler instance; private static SpawnerHandler instance;
private float enemySpawnTimer = 0f; private float enemySpawnTimer = 0f;
private static final float ENEMY_SPAWN_INTERVAL = 0.5f; private static final float ENEMY_SPAWN_INTERVAL = 1f;
private SpawnerHandler() {} private SpawnerHandler() {}
@@ -31,13 +31,13 @@ public class SpawnerHandler {
} }
} }
public void spawn(Enemies enemy) { public void spawn(EnemyTypes enemy) {
World world = Bugger.getInstance().getWorld(); World world = Bugger.getInstance().getWorld();
Vector2 playerPos = Player.getInstance().getPosition(); Vector2 playerPos = Player.getInstance().getPosition();
EnemyHandler.getInstance().getEnemies().add(EnemyFactory.createEnemy(enemy, world, playerPos)); EnemyHandler.getInstance().getEnemies().add(EnemyFactory.createEnemy(enemy, world, playerPos));
} }
public void spawn(Enemies enemy, World world, Vector2 position) { public void spawn(EnemyTypes enemy, World world, Vector2 position) {
EnemyHandler.getInstance().getEnemies().add(EnemyFactory.createEnemy(enemy, world, position)); EnemyHandler.getInstance().getEnemies().add(EnemyFactory.createEnemy(enemy, world, position));
} }

View File

@@ -0,0 +1,45 @@
package org.lumijiez.bugger.pools;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import java.util.HashMap;
import java.util.Map;
public class ProjectileFlyweight {
private static final Map<Boolean, ProjectileFlyweight> flyweights = new HashMap<>();
private final Sprite sprite;
private final float size;
private final float defaultSpeed;
private final boolean isEnemy;
public static final String ENEMY_TEXTURE = "images/enemyblaze.png";
public static final String PLAYER_TEXTURE = "images/blaze.png";
ProjectileFlyweight(boolean isEnemy) {
this.sprite = new Sprite(new Texture(isEnemy ? ENEMY_TEXTURE : PLAYER_TEXTURE));
this.size = 5f;
this.defaultSpeed = 5000f;
this.isEnemy = isEnemy;
}
public static ProjectileFlyweight get(boolean isEnemy) {
return flyweights.computeIfAbsent(isEnemy, ProjectileFlyweight::new);
}
public Sprite getSprite() {
return sprite;
}
public float getSize() {
return size;
}
public float getDefaultSpeed() {
return defaultSpeed;
}
public boolean isEnemy() {
return isEnemy;
}
}

View File

@@ -3,7 +3,7 @@ package org.lumijiez.bugger.util;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import org.lumijiez.bugger.Bugger; import org.lumijiez.bugger.Bugger;
import org.lumijiez.bugger.entities.Player; import org.lumijiez.bugger.entities.player.Player;
import org.lumijiez.bugger.handlers.*; import org.lumijiez.bugger.handlers.*;
import static org.lumijiez.bugger.Bugger.spriteBatch; import static org.lumijiez.bugger.Bugger.spriteBatch;

View File

@@ -1,4 +1,4 @@
package org.lumijiez.bugger.util; package org.lumijiez.bugger.util.data;
public record CollisionPair(Class<?> typeA, Class<?> typeB) { public record CollisionPair(Class<?> typeA, Class<?> typeB) {
public boolean matches(Object objA, Object objB) { public boolean matches(Object objA, Object objB) {

View File

@@ -0,0 +1,34 @@
package org.lumijiez.bugger.util.data;
public class Triple<A, B, C> {
private final A first;
private final B second;
private final C third;
public Triple(A first, B second, C third) {
this.first = first;
this.second = second;
this.third = third;
}
public A first() {
return first;
}
public B second() {
return second;
}
public C third() {
return third;
}
@Override
public String toString() {
return "Triple{" +
"first=" + first +
", second=" + second +
", third=" + third +
'}';
}
}

View File

@@ -0,0 +1,29 @@
package org.lumijiez.bugger.util.data;
public class TripleInt extends Triple<Integer,Integer,Integer> {
public TripleInt(int first, int second, int third) {
super(first, second, third);
}
public int one() {
return super.first();
}
public int two() {
return super.second();
}
public int thr() {
return super.third();
}
@Override
public String toString() {
return "IntTriple{" +
"first=" + one() +
", second=" + two() +
", third=" + thr() +
'}';
}
}

View File

@@ -1,4 +1,4 @@
package org.lumijiez.bugger.util; package org.lumijiez.bugger.util.functional;
@FunctionalInterface @FunctionalInterface
public interface CollisionAction { public interface CollisionAction {

View File

@@ -1,4 +1,4 @@
package org.lumijiez.bugger.util; package org.lumijiez.bugger.util.functional;
import org.lumijiez.bugger.entities.Entity; import org.lumijiez.bugger.entities.Entity;
import org.lumijiez.bugger.entities.EntityType; import org.lumijiez.bugger.entities.EntityType;