diff --git a/Projekte/battleship/client/build.gradle b/Projekte/battleship/client/build.gradle index 8c3e5c2..b330d95 100644 --- a/Projekte/battleship/client/build.gradle +++ b/Projekte/battleship/client/build.gradle @@ -9,6 +9,7 @@ dependencies { implementation project(":battleship:model") implementation libs.jme3.desktop + implementation libs.jme3.effects implementation project(path: ':battleship:server') implementation project(path: ':battleship:server') diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java new file mode 100644 index 0000000..bd84e85 --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java @@ -0,0 +1,243 @@ +package pp.battleship.client.gui; + +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh.Type; +import com.jme3.effect.shapes.EmitterSphereShape; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import pp.battleship.client.BattleshipApp; + +/** + * Factory class responsible for creating particle effects used in the game. + * This centralizes the creation of various types of particle emitters. + */ +public class ParticleEffectFactory { + private static final int COUNT_FACTOR = 1; + private static final float COUNT_FACTOR_F = 1f; + private static final boolean POINT_SPRITE = true; + private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle; + + private final BattleshipApp app; + + ParticleEffectFactory(BattleshipApp app) { + this.app = app; + } + + /** + * Creates a flame particle emitter. + * + * @return a configured flame particle emitter + */ + ParticleEmitter createFlame() { + ParticleEmitter flame = new ParticleEmitter("Flame", EMITTER_TYPE, 32 * COUNT_FACTOR); + flame.setSelectRandomImage(true); + flame.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (1f / COUNT_FACTOR_F))); + flame.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f)); + flame.setStartSize(0.1f); + flame.setEndSize(0.5f); + flame.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f)); + flame.setParticlesPerSec(0); + flame.setGravity(0, -5, 0); + flame.setLowLife(.4f); + flame.setHighLife(.5f); + flame.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 7, 0)); + flame.getParticleInfluencer().setVelocityVariation(1f); + flame.setImagesX(2); + flame.setImagesY(2); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png")); + mat.setBoolean("PointSprite", POINT_SPRITE); + flame.setMaterial(mat); + + return flame; + } + + /** + * Creates a flash particle emitter. + * + * @return a configured flash particle emitter + */ + ParticleEmitter createFlash() { + ParticleEmitter flash = new ParticleEmitter("Flash", EMITTER_TYPE, 24 * COUNT_FACTOR); + flash.setSelectRandomImage(true); + flash.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1f / COUNT_FACTOR_F)); + flash.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f)); + flash.setStartSize(.1f); + flash.setEndSize(0.5f); + flash.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f)); + flash.setParticlesPerSec(0); + flash.setGravity(0, 0, 0); + flash.setLowLife(.2f); + flash.setHighLife(.2f); + flash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 5f, 0)); + flash.getParticleInfluencer().setVelocityVariation(1); + flash.setImagesX(2); + flash.setImagesY(2); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flash.png")); + mat.setBoolean("PointSprite", POINT_SPRITE); + flash.setMaterial(mat); + + return flash; + } + + /** + * Creates a round spark particle emitter. + * + * @return a configured round spark particle emitter + */ + ParticleEmitter createRoundSpark() { + ParticleEmitter roundSpark = new ParticleEmitter("RoundSpark", EMITTER_TYPE, 20 * COUNT_FACTOR); + roundSpark.setStartColor(new ColorRGBA(1f, 0.29f, 0.34f, (float) (1.0 / COUNT_FACTOR_F))); + roundSpark.setEndColor(new ColorRGBA(0, 0, 0, 0.5f / COUNT_FACTOR_F)); + roundSpark.setStartSize(0.2f); + roundSpark.setEndSize(0.8f); + roundSpark.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f)); + roundSpark.setParticlesPerSec(0); + roundSpark.setGravity(0, -.5f, 0); + roundSpark.setLowLife(1.8f); + roundSpark.setHighLife(2f); + roundSpark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0)); + roundSpark.getParticleInfluencer().setVelocityVariation(.5f); + roundSpark.setImagesX(1); + roundSpark.setImagesY(1); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/roundspark.png")); + mat.setBoolean("PointSprite", POINT_SPRITE); + roundSpark.setMaterial(mat); + + return roundSpark; + } + + /** + * Creates a spark particle emitter. + * + * @return a configured spark particle emitter + */ + ParticleEmitter createSpark() { + ParticleEmitter spark = new ParticleEmitter("Spark", Type.Triangle, 30 * COUNT_FACTOR); + spark.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F)); + spark.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f)); + spark.setStartSize(.5f); + spark.setEndSize(.5f); + spark.setFacingVelocity(true); + spark.setParticlesPerSec(0); + spark.setGravity(0, 5, 0); + spark.setLowLife(1.1f); + spark.setHighLife(1.5f); + spark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0)); + spark.getParticleInfluencer().setVelocityVariation(1); + spark.setImagesX(1); + spark.setImagesY(1); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/spark.png")); + spark.setMaterial(mat); + + return spark; + } + + /** + * Creates a smoke trail particle emitter. + * + * @return a configured smoke trail particle emitter + */ + ParticleEmitter createSmokeTrail() { + ParticleEmitter smokeTrail = new ParticleEmitter("SmokeTrail", Type.Triangle, 22 * COUNT_FACTOR); + smokeTrail.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F)); + smokeTrail.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f)); + smokeTrail.setStartSize(.2f); + smokeTrail.setEndSize(1f); + smokeTrail.setFacingVelocity(true); + smokeTrail.setParticlesPerSec(0); + smokeTrail.setGravity(0, 1, 0); + smokeTrail.setLowLife(.4f); + smokeTrail.setHighLife(.5f); + smokeTrail.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 12, 0)); + smokeTrail.getParticleInfluencer().setVelocityVariation(1); + smokeTrail.setImagesX(1); + smokeTrail.setImagesY(3); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/smoketrail.png")); + smokeTrail.setMaterial(mat); + + return smokeTrail; + } + + /** + * Creates a debris particle emitter. + * + * @return a configured debris particle emitter + */ + ParticleEmitter createDebris() { + ParticleEmitter debris = new ParticleEmitter("Debris", Type.Triangle, 15 * COUNT_FACTOR); + debris.setSelectRandomImage(true); + debris.setRandomAngle(true); + debris.setRotateSpeed(FastMath.TWO_PI * 4); + debris.setStartColor(new ColorRGBA(1f, 0.59f, 0.28f, 1.0f / COUNT_FACTOR_F)); + debris.setEndColor(new ColorRGBA(.5f, 0.5f, 0.5f, 0f)); + debris.setStartSize(.10f); + debris.setEndSize(.15f); + debris.setParticlesPerSec(0); + debris.setGravity(0, 12f, 0); + debris.setLowLife(1.4f); + debris.setHighLife(1.5f); + debris.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 15, 0)); + debris.getParticleInfluencer().setVelocityVariation(.60f); + debris.setImagesX(3); + debris.setImagesY(3); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png")); + debris.setMaterial(mat); + + return debris; + } + + /** + * Creates a shockwave particle emitter. + * + * @return a configured shockwave particle emitter + */ + ParticleEmitter createShockwave() { + ParticleEmitter shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR); + shockwave.setFaceNormal(Vector3f.UNIT_Y); + shockwave.setStartColor(new ColorRGBA(.48f, 0.17f, 0.01f, .8f / COUNT_FACTOR_F)); + shockwave.setEndColor(new ColorRGBA(.48f, 0.17f, 0.01f, 0f)); + shockwave.setStartSize(0f); + shockwave.setEndSize(3f); + shockwave.setParticlesPerSec(0); + shockwave.setGravity(0, 0, 0); + shockwave.setLowLife(0.5f); + shockwave.setHighLife(0.5f); + shockwave.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0)); + shockwave.getParticleInfluencer().setVelocityVariation(0f); + shockwave.setImagesX(1); + shockwave.setImagesY(1); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/shockwave.png")); + shockwave.setMaterial(mat); + + return shockwave; + } + + /** + * Creates a moving smoke emitter. + * + * @return a configured smoke emitter + */ + ParticleEmitter createMovingSmokeEmitter() { + ParticleEmitter smokeEmitter = new ParticleEmitter("SmokeEmitter", Type.Triangle, 300); + smokeEmitter.setGravity(0, 0, 0); + smokeEmitter.getParticleInfluencer().setVelocityVariation(1); + smokeEmitter.setLowLife(1); + smokeEmitter.setHighLife(1); + smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, .5f, 0)); + smokeEmitter.setImagesX(15); // Assuming the smoke texture is a sprite sheet with 15 frames + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png")); + smokeEmitter.setMaterial(mat); + + return smokeEmitter; + } +} diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java index 24bfb42..cd56309 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java @@ -7,6 +7,7 @@ package pp.battleship.client.gui; +import com.jme3.effect.ParticleEmitter; import com.jme3.material.Material; import com.jme3.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; @@ -21,7 +22,6 @@ import pp.battleship.model.Battleship; import pp.battleship.model.Rotation; import pp.battleship.model.ShipMap; import pp.battleship.model.Shot; - import static java.util.Objects.requireNonNull; import static pp.util.FloatMath.HALF_PI; import static pp.util.FloatMath.PI; @@ -34,14 +34,13 @@ import static pp.util.FloatMath.PI; */ class SeaSynchronizer extends ShipMapSynchronizer { private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS - - private static final String UX23 = "Models/UX23/UX23.j3o"; private static final String BOJE = "Models/Boje/Boje.j3o"; private static final String ALIENSHIP = "Models/Alienship/Alienship.j3o"; private static final String MARLOW66 = "Models/Marlow66/Marlow66.j3o"; - private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; //NON-NLS + private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; + private static final String COLOR = "Color"; //NON-NLS private static final String SHIP = "ship"; //NON-NLS private static final String SHOT = "shot"; //NON-NLS @@ -51,6 +50,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { private final ShipMap map; private final BattleshipApp app; + private final ParticleEffectFactory particleFactory; /** * Constructs a {@code SeaSynchronizer} object with the specified application, root node, and ship map. @@ -63,6 +63,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { super(app.getGameLogic().getOwnMap(), root); this.app = app; this.map = map; + this.particleFactory = new ParticleEffectFactory(app); addExisting(); } @@ -79,7 +80,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { return shot.isHit() ? handleHit(shot) : createCylinder(shot); } - /** + /** * Handles a hit by attaching its representation to the node that * contains the ship model as a child so that it moves with the ship. * @@ -91,10 +92,48 @@ class SeaSynchronizer extends ShipMapSynchronizer { final Battleship ship = requireNonNull(map.findShipAt(shot), "Missing ship"); final Node shipNode = requireNonNull((Node) getSpatial(ship), "Missing ship node"); - final Geometry representation = createCylinder(shot); - representation.getLocalTranslation().subtractLocal(shipNode.getLocalTranslation()); - shipNode.attachChild(representation); + + // Create a new node specifically for the hit effects + Node hitEffectNode = new Node("HitEffectNode"); + + // Create particle effects + ParticleEmitter flame = particleFactory.createFlame(); + ParticleEmitter flash = particleFactory.createFlash(); + ParticleEmitter spark = particleFactory.createSpark(); + ParticleEmitter roundSpark = particleFactory.createRoundSpark(); + ParticleEmitter smokeTrail = particleFactory.createSmokeTrail(); + ParticleEmitter debris = particleFactory.createDebris(); + ParticleEmitter shockwave = particleFactory.createShockwave(); + ParticleEmitter movingSmoke = particleFactory.createMovingSmokeEmitter(); + + // Attach all effects to the hitEffectNode + hitEffectNode.attachChild(flame); + hitEffectNode.attachChild(flash); + hitEffectNode.attachChild(spark); + hitEffectNode.attachChild(roundSpark); + hitEffectNode.attachChild(smokeTrail); + hitEffectNode.attachChild(debris); + hitEffectNode.attachChild(shockwave); + hitEffectNode.attachChild(movingSmoke); + + // Set the local translation for the hit effect to the point of impact + hitEffectNode.setLocalTranslation(shot.getY() + 0.5f - shipNode.getLocalTranslation().x, + 0.5f, // Adjust as needed for height above the ship + shot.getX() + 0.5f - shipNode.getLocalTranslation().z); + + // Attach the hitEffectNode to the shipNode so it moves with the ship + shipNode.attachChild(hitEffectNode); + + // Emit particles when the hit happens + flash.emitAllParticles(); + spark.emitAllParticles(); + smokeTrail.emitAllParticles(); + debris.emitAllParticles(); + shockwave.emitAllParticles(); + flame.emitAllParticles(); + roundSpark.emitAllParticles(); + return null; } @@ -149,72 +188,20 @@ class SeaSynchronizer extends ShipMapSynchronizer { */ private Spatial createShip(Battleship ship) { switch (ship.getLength()) { - - - - case 2: - return createUX23(ship); - - case 3: - return createMarlow66(ship); - case 4: - return createBattleship(ship); - case 1: - return createAllienship(ship); - - default: - return createBox(ship); + case 4: return createBattleship(ship); + case 3: return createMarlow66(ship); + case 2: return createUX23(ship); + case 1: return createAllienship(ship); + default: return createBox(ship); } } - private Spatial createAllienship(Battleship ship) { - final Spatial model = app.getAssetManager().loadModel(ALIENSHIP); - - model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f); - model.scale(0.10f); - model.setShadowMode(ShadowMode.CastAndReceive); - - return model;} - private Spatial createUX23(Battleship ship) { - final Spatial model = app.getAssetManager().loadModel(UX23); - - model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f); - // model.move(0f, -0.05f, 0f); - model.scale(0.89f); - model.setShadowMode(ShadowMode.CastAndReceive); - - return model; - } - - - private Spatial createMarlow66(Battleship ship) { - final Spatial model = app.getAssetManager().loadModel(MARLOW66); - - model.rotate(0f, calculateRotationAngle(ship.getRot()), 0f); - model.move(0f, 0.25f, 0f); - model.scale(0.135f); - model.setShadowMode(ShadowMode.CastAndReceive); - - return model; - } - - private Spatial createBoje(Battleship ship) { - final Spatial model = app.getAssetManager().loadModel(BOJE); - - model.rotate(0f, calculateRotationAngle(ship.getRot()), 0f); - model.move(0.00f, 0.25f, 0.00f); - model.scale(12.23f); - model.setShadowMode(ShadowMode.CastAndReceive); - - return model; - } - /** - * Creates a simple box to represent a battleship that is not of the "King George V" type. - * - * @param ship the battleship to be represented - * @return the geometry representing the battleship as a box - */ + * Creates a simple box to represent a battleship that is not of the "King George V" type. + * + * @param ship the battleship to be represented + * @return the geometry representing the battleship as a box + */ private Spatial createBox(Battleship ship) { final Box box = new Box(0.5f * (ship.getMaxY() - ship.getMinY()) + 0.3f, 0.3f, @@ -256,6 +243,42 @@ class SeaSynchronizer extends ShipMapSynchronizer { model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f); model.scale(1.48f); + // model.scale(0.0007f); + model.setShadowMode(ShadowMode.CastAndReceive); + + return model; + } + + private Spatial createAllienship(Battleship ship) { + final Spatial model = app.getAssetManager().loadModel(ALIENSHIP); + + model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f); + model.scale(0.10f); + model.setShadowMode(ShadowMode.CastAndReceive); + + return model;} + private Spatial createUX23(Battleship ship) { + final Spatial model = app.getAssetManager().loadModel(UX23); + + model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f); + // model.move(0f, -0.05f, 0f); + model.scale(0.89f); + model.setShadowMode(ShadowMode.CastAndReceive); + + return model; + } + + + + + + + private Spatial createMarlow66(Battleship ship) { + final Spatial model = app.getAssetManager().loadModel(MARLOW66); + + model.rotate(0f, calculateRotationAngle(ship.getRot()), 0f); + model.move(0f, 0.25f, 0f); + model.scale(0.135f); model.setShadowMode(ShadowMode.CastAndReceive); return model; diff --git a/Projekte/battleship/client/src/main/resources/Effects/Debris.png b/Projekte/battleship/client/src/main/resources/Effects/Debris.png new file mode 100644 index 0000000..c6eab7b Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Debris.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Smoke.png b/Projekte/battleship/client/src/main/resources/Effects/Smoke.png new file mode 100644 index 0000000..ec46564 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Smoke.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/flame.png b/Projekte/battleship/client/src/main/resources/Effects/flame.png new file mode 100644 index 0000000..d3a31f7 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/flame.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/flash.png b/Projekte/battleship/client/src/main/resources/Effects/flash.png new file mode 100644 index 0000000..d240917 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/flash.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/roundspark.png b/Projekte/battleship/client/src/main/resources/Effects/roundspark.png new file mode 100644 index 0000000..bb2b5d8 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/roundspark.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/shockwave.png b/Projekte/battleship/client/src/main/resources/Effects/shockwave.png new file mode 100644 index 0000000..62856bd Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/shockwave.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/smoketrail.png b/Projekte/battleship/client/src/main/resources/Effects/smoketrail.png new file mode 100644 index 0000000..5b96518 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/smoketrail.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/spark.png b/Projekte/battleship/client/src/main/resources/Effects/spark.png new file mode 100644 index 0000000..fa2d25a Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/spark.png differ diff --git a/Projekte/settings.gradle b/Projekte/settings.gradle index 7440607..78bdb13 100644 --- a/Projekte/settings.gradle +++ b/Projekte/settings.gradle @@ -17,6 +17,9 @@ dependencyResolutionManagement { library('jme3-plugins', 'org.jmonkeyengine', 'jme3-plugins').versionRef('jme') library('jme3-jogg', 'org.jmonkeyengine', 'jme3-jogg').versionRef('jme') library('jme3-testdata', 'org.jmonkeyengine', 'jme3-testdata').versionRef('jme') + library('jme3-effects', 'org.jmonkeyengine', 'jme3-effects').versionRef('jme') + + library('jme3-lwjgl', 'org.jmonkeyengine', 'jme3-lwjgl').versionRef('jme') library('jme3-lwjgl3', 'org.jmonkeyengine', 'jme3-lwjgl3').versionRef('jme')