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 e04e3d0..f729ca4 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 @@ -48,6 +48,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. @@ -60,6 +61,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { super(app.getGameLogic().getOwnMap(), root); this.app = app; this.map = map; + this.particleFactory = new ParticleEffectFactory(app); addExisting(); } @@ -84,17 +86,95 @@ class SeaSynchronizer extends ShipMapSynchronizer { * @return always null to prevent the representation from being attached * to the items node as well */ + 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; + } + + * @param shot a hit + * @return always null to prevent the representation from being attached to the items node as well + */ private Spatial handleHit(Shot shot) { 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(); + + //Checks if ship is destroyed and triggers animation accordingly + if (ship.isDestroyed()) { + sinkAndRemoveShip(ship); + } return null; } + /** + * Handles the sinking animation and removal of ship if destroyed + * @param ship the ship to be sunk + */ + private void sinkAndRemoveShip(Battleship ship) { + + final Node shipNode = (Node) getSpatial(ship); + 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(); + } + + /** * Creates a cylinder geometry representing the specified shot. * The appearance of the cylinder depends on whether the shot is a hit or a miss. 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..dc6775e --- /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.scene.control.AbstractControl; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Node; + +/** + * 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 = 5f; // Duration of the sinking animation + private static final float SINK_SPEED = 0.1f; // Speed at which the ship sinks + private float elapsedTime = 0; + + private final Node shipNode; + + /** + * Constructs a {@code SinkingControl.java} 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, currentPos.y - SINK_SPEED * tpf, currentPos.z); + + // 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 + } +}