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 396f54f0..936f767d 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,8 @@ package pp.battleship.client.gui; +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh.Type; import com.jme3.light.AmbientLight; import com.jme3.light.DirectionalLight; import com.jme3.light.Light; @@ -20,6 +22,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Cylinder; +import com.simsilica.lemur.effect.EffectControl; import pp.battleship.client.BattleshipApp; import pp.battleship.model.Battleship; import pp.battleship.model.Rotation; @@ -30,7 +33,6 @@ import static pp.util.FloatMath.HALF_PI; import static pp.util.FloatMath.PI; - /** * The {@code SeaSynchronizer} class is responsible for synchronizing the graphical * representation of the ships and shots on the sea map with the underlying data model. @@ -40,9 +42,10 @@ class SeaSynchronizer extends ShipMapSynchronizer { private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; //NON-NLS - private static final String SUBMARINE ="Models/Submarine/submarine.obj"; - private static final String DESTROYER ="Models/Destroyer/10619_Battleship.obj"; - private static final String SMALL_SHIP ="Models/SmallShip/10634_SpeedBoat_v01_LOD3.obj"; + private static final String SUBMARINE = "Models/Submarine/submarine.obj"; + private static final String DESTROYER = "Models/Destroyer/10619_Battleship.obj"; + private static final String SMALL_SHIP = "Models/SmallShip/10634_SpeedBoat_v01_LOD3.obj"; + private static final String PARTICLE = "Common/MatDefs/Misc/Particle.j3md"; private static final String COLOR = "Color"; //NON-NLS private static final String SHIP = "ship"; //NON-NLS private static final String SHOT = "shot"; //NON-NLS @@ -77,7 +80,20 @@ public SeaSynchronizer(BattleshipApp app, Node root, ShipMap map) { */ @Override public Spatial visit(Shot shot) { - return shot.isHit() ? handleHit(shot) : createCylinder(shot); + return shot.isHit() ? handleHit(shot) : handleMiss(shot); + } + + /** + * Handles a miss + * + * @param shot a hit + * @return always null to prevent the representation from being attached + * to the items node as well + */ + + private Spatial handleMiss(Shot shot) { + final ParticleEmitter emitter = createMissParticle(shot); + return emitter; } /** @@ -91,14 +107,74 @@ public Spatial visit(Shot shot) { 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); - + final ParticleEmitter particleEmitter = createHitParticle(shot); + particleEmitter.getLocalTranslation().subtractLocal(shipNode.getLocalTranslation()); + shipNode.attachChild(particleEmitter); + if (ship.isDestroyed()) { + for (int i = 0; i < 50; i++) { + float r = (float) 0.001 * i; + shipNode.move(0, -r, 0); + } + } return null; } + /** + * this method creates the particles, when the shot misses + * + * @param shot is the parameter, where was shot + * @return the particle + */ + + private ParticleEmitter createMissParticle(Shot shot) { + ParticleEmitter hitEffect = new ParticleEmitter("HitEffect", Type.Triangle, 30); + hitEffect.setMaterial(new Material(app.getAssetManager(), PARTICLE)); + hitEffect.setImagesX(2); + hitEffect.setImagesY(2); + hitEffect.setStartColor(ColorRGBA.Cyan); + hitEffect.setEndColor(ColorRGBA.Blue); + hitEffect.getParticleInfluencer().setInitialVelocity(new Vector3f(0.1f, 0.1f, 0.1f)); + hitEffect.move(0, -2, 0); + hitEffect.setStartSize(0.45f); + hitEffect.setEndSize(0.45f); + hitEffect.setGravity(0, -0.5f, 0); + hitEffect.setLowLife(1f); + hitEffect.setHighLife(1f); + hitEffect.setParticlesPerSec(0); + hitEffect.setLocalTranslation(shot.getY() + 0.5f, 0, shot.getX() + 0.5f); + + hitEffect.emitAllParticles(); + return hitEffect; + } + + /** + * this method creates the particles, when a ship is hit + * + * @param shot says, where was shot + * @return the particle + */ + + private ParticleEmitter createHitParticle(Shot shot) { + ParticleEmitter hitEffect = new ParticleEmitter("HitEffect", Type.Triangle, 30); + hitEffect.setMaterial(new Material(app.getAssetManager(), PARTICLE)); + hitEffect.setImagesX(2); + hitEffect.setImagesY(2); + hitEffect.setStartColor(ColorRGBA.Orange); + hitEffect.setEndColor(ColorRGBA.Red); + hitEffect.getParticleInfluencer().setInitialVelocity(new Vector3f(1, 1, 1)); + + hitEffect.setStartSize(0.45f); + hitEffect.setEndSize(0.1f); + hitEffect.setGravity(0, -0.5f, 0); + hitEffect.setLowLife(1f); + hitEffect.setHighLife(2f); + hitEffect.setParticlesPerSec(0); + hitEffect.setLocalTranslation(shot.getY() + 0.5f, 0, shot.getX() + 0.5f); + + hitEffect.emitAllParticles(); + return hitEffect; + } + /** * Creates a cylinder geometry representing the specified shot. * The appearance of the cylinder depends on whether the shot is a hit or a miss. @@ -150,11 +226,11 @@ public Spatial visit(Battleship ship) { */ private Spatial createShip(Battleship ship) { //return ship.getLength() == 4 ? createBattleship(ship) : createBox(ship); - return switch(ship.getLength()){ - case(1)-> createSmallShip(ship); - case(2)-> createSubmarine(ship); - case(3)-> createDestroyer(ship); - case(4)-> createBattleship(ship); + return switch (ship.getLength()) { + case (1) -> createSmallShip(ship); + case (2) -> createSubmarine(ship); + case (3) -> createDestroyer(ship); + case (4) -> createBattleship(ship); default -> createBox(ship); }; } @@ -176,13 +252,13 @@ private Spatial createBox(Battleship ship) { return geometry; } - private Spatial createDestroyer(Battleship ship){ - final Spatial model=app.getAssetManager().loadModel(DESTROYER); - model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot())-HALF_PI, 0f); + private Spatial createDestroyer(Battleship ship) { + final Spatial model = app.getAssetManager().loadModel(DESTROYER); + model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()) - HALF_PI, 0f); model.scale(0.0001f); - model.move(0,0.3f ,0); + model.move(0, 0.3f, 0); model.setShadowMode(ShadowMode.CastAndReceive); - model.setMaterial(app.getAssetManager().loadMaterial("Models/Destroyer/10619_Battleship.mtl")); + //model.setMaterial(app.getAssetManager().loadMaterial("Models/Destroyer/10619_Battleship.mtl")); //TODO //Material m = new Material(); @@ -191,18 +267,17 @@ private Spatial createDestroyer(Battleship ship){ return model; } - private Spatial createSubmarine(Battleship ship){ - final Spatial model=app.getAssetManager().loadModel(SUBMARINE); + private Spatial createSubmarine(Battleship ship) { + final Spatial model = app.getAssetManager().loadModel(SUBMARINE); model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f); model.scale(0.25f); model.setShadowMode(ShadowMode.CastAndReceive); - return model; } - private Spatial createSmallShip(Battleship ship){ + private Spatial createSmallShip(Battleship ship) { final Spatial model = app.getAssetManager().loadModel(SMALL_SHIP); model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()) + HALF_PI, 0f);