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..93973d8 --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java @@ -0,0 +1,283 @@ +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.setLocalTranslation(0, 2f, 0); + 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.setLocalTranslation(0, 2f, 0); + 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; + } + public ParticleEmitter createWaterSplash() { + // Create a new particle emitter for the splash effect + ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30); + + // Set the shape of the emitter, making particles emit from a point or small area + waterSplash.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.2f)); + + // Start and end colors for water (blue, fading out) + waterSplash.setStartColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f)); // Light blue at start + waterSplash.setEndColor(new ColorRGBA(0.4f, 0.4f, 1f, 0f)); // Transparent at the end + + // Particle size: small at start, larger before fading out + waterSplash.setStartSize(0.1f); + waterSplash.setEndSize(0.3f); + + // Particle lifespan (how long particles live) + waterSplash.setLowLife(0.5f); + waterSplash.setHighLife(1f); + + // Gravity: Pull the water particles downwards + waterSplash.setGravity(0, -9.81f, 0); // Earth's gravity simulation + + // Velocity: Give particles an initial burst upward (simulates splash) + waterSplash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0)); + waterSplash.getParticleInfluencer().setVelocityVariation(0.6f); // Add randomness to splash + + // Set how many particles are emitted per second (0 to emit all particles at once) + waterSplash.setParticlesPerSec(0); + + // Load a texture for the water splash (assuming a texture exists at this path) + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Splash/splash.png")); + waterSplash.setMaterial(mat); + + return waterSplash; + } + +} 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 1f31648..da923fd 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 @@ -11,8 +11,6 @@ package pp.battleship.client.gui; import static java.util.Objects.requireNonNull; 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.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; @@ -56,198 +54,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { private final ShipMap map; private final BattleshipApp app; - - private ParticleEmitter flame, flash, spark, roundspark, smoketrail, debris, - shockwave; - - - 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; - /** - * Creates a Flame texture with indefininite Lifetime - * @return flame texture - */ - - private ParticleEmitter createFlame() { - 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); - - // Setze die Höhe auf 2f - flame.setLocalTranslation(0, 2f, 0); - - return flame; - } - - private ParticleEmitter createSpark() { - 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); - - // Setze die Höhe auf 2f - spark.setLocalTranslation(0, 2f, 0); - - return spark; - } - - private ParticleEmitter createRoundSpark() { - 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); - - // Setze die Höhe auf 2f - roundspark.setLocalTranslation(0, 2f, 0); - - return roundspark; - } - - - - private ParticleEmitter createSmokeTrail() { - 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); - - // Setze die Höhe auf 2f - smoketrail.setLocalTranslation(0, 2f, 0); - - return smoketrail; - } - - - private ParticleEmitter createDebris() { - 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); - - // Setze die Höhe auf 2f - debris.setLocalTranslation(0, 2f, 0); - - return debris; - } - - - private ParticleEmitter createShockwave(){ - shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR); -// shockwave.setRandomAngle(true); - 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; - } - - private 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); - Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); - mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png")); - smokeEmitter.setMaterial(mat); - - // Setze die Höhe auf 2f - smokeEmitter.setLocalTranslation(0, 2f, 0); - - return smokeEmitter; - } + private final ParticleEffectFactory particleFactory; /** @@ -261,6 +68,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { super(app.getGameLogic().getOwnMap(), root); this.app = app; this.map = map; + this.particleFactory = new ParticleEffectFactory(app); addExisting(); } @@ -274,8 +82,35 @@ class SeaSynchronizer extends ShipMapSynchronizer { */ @Override public Spatial visit(Shot shot) { - return shot.isHit() ? handleHit(shot) : createCylinder(shot); + return shot.isHit() ? handleHit(shot) : handleMiss(shot); } + private Spatial handleMiss(Shot shot) { + Node shotNode = new Node("ShotNode"); + Geometry shotCylinder = createCylinder(shot); + shotNode.attachChild(shotCylinder); + ParticleEmitter waterSplash = particleFactory.createWaterSplash(); + waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f); + shotNode.attachChild(waterSplash); + waterSplash.emitAllParticles(); + + return shotNode; + } + private void sinkAndRemoveShip(Battleship ship) { + Battleship wilkeningklaunichtmeinencode = ship; + final Node shipNode = (Node) getSpatial(wilkeningklaunichtmeinencode); + if (shipNode == null) return; + + // Add sinking control to animate the sinking + shipNode.addControl(new SinkingControl(shipNode)); + + // Add particle effects + ParticleEmitter bubbles = particleFactory.createWaterSplash(); + bubbles.setLocalTranslation(shipNode.getLocalTranslation()); + shipNode.attachChild(bubbles); + bubbles.emitAllParticles(); + } + + /** * Handles a hit by attaching its representation to the node that @@ -289,21 +124,17 @@ 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 = createFlame(); - // ParticleEmitter flash = createFlash(); - ParticleEmitter spark = createSpark(); - ParticleEmitter roundSpark = createRoundSpark(); - ParticleEmitter smokeTrail = createSmokeTrail(); - ParticleEmitter debris = createDebris(); - ParticleEmitter shockwave = createShockwave(); - ParticleEmitter movingSmoke = createMovingSmokeEmitter(); // New moving smoke emitter + 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); @@ -332,9 +163,13 @@ class SeaSynchronizer extends ShipMapSynchronizer { flame.emitAllParticles(); roundSpark.emitAllParticles(); + if (ship.isDestroyed()) { + sinkAndRemoveShip(ship); + } - return null; + + return null; } /** diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SinkingControl.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SinkingControl.java new file mode 100644 index 0000000..19ee120 --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SinkingControl.java @@ -0,0 +1,53 @@ +package pp.battleship.client.gui; + +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Node; +import com.jme3.scene.control.AbstractControl; + +/** + * Control that handles the sinking effect for destroyed ships. + * It will gradually move the ship downwards and then remove it from the scene. + */ +class SinkingControl extends AbstractControl { + private static final float SINK_DURATION = 6f; // Duration of the sinking animation + private static final float SINK_SPEED = 0.8f; // Speed at which the ship sinks + private float elapsedTime = 0; + + private final Node shipNode; + + /** + * Constructs a {@code SinkingControl} object with the shipNode to be to be sunk + * @param shipNode the node to handeld + */ + public SinkingControl(Node shipNode) { + this.shipNode = shipNode; + } + + /** + * Updated the Map to sink the ship + * + * @param tpf time per frame + */ + @Override + protected void controlUpdate(float tpf) { + // Update the sinking effect + elapsedTime += tpf; + + // Move the ship down over time + Vector3f currentPos = shipNode.getLocalTranslation(); + shipNode.setLocalTranslation(currentPos.x+SINK_SPEED*tpf, currentPos.y - SINK_SPEED * tpf, currentPos.z+SINK_SPEED*tpf); + + // Check if sinking duration has passed + if (elapsedTime >= SINK_DURATION) { + // Remove the ship from the scene + shipNode.removeFromParent(); + } + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + // No rendering-related code needed + } +}