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 936f767d..a6304789 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 @@ -29,6 +29,11 @@ import pp.battleship.model.ShipMap; import pp.battleship.model.Shot; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.lang.System.Logger.Level; +import java.util.Timer; + import static java.util.Objects.requireNonNull; import static pp.util.FloatMath.HALF_PI; import static pp.util.FloatMath.PI; @@ -110,15 +115,54 @@ private Spatial handleHit(Shot shot) { 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); - } - } + final ParticleEmitter fire = createFire(shot); + shipNode.attachChild(fire); return null; } + /** + * this method attach fire to the ship, if it is hit + * @param shot the shot, that hit the ship + * @return the Fire + */ + + private ParticleEmitter createFire(Shot shot) { + ParticleEmitter hitEffect = new ParticleEmitter("HitEffect", Type.Triangle, 5000); + hitEffect.setMaterial(new Material(app.getAssetManager(), PARTICLE)); + hitEffect.setImagesX(2); + hitEffect.setImagesY(2); + hitEffect.setStartColor(ColorRGBA.Orange); + hitEffect.setEndColor(ColorRGBA.Red); + hitEffect.setStartSize(0.1f); + hitEffect.setEndSize(0.1f); + hitEffect.setParticlesPerSec(2); + hitEffect.setLowLife(1f); + hitEffect.setHighLife(1f); + final Node shipNode = requireNonNull((Node) getSpatial(map.findShipAt(shot.getX(), shot.getY())), "Missing ship node"); + Vector3f shipNodePos = shipNode.getLocalTranslation(); + Vector3f shotWorld = mapToWorldCord(shot.getX(), shot.getY()); + Vector3f firePos = shotWorld.subtract(shipNodePos); + if (map.findShipAt(shot.getX(), shot.getY()).getLength() == 2) { + hitEffect.setLocalTranslation(firePos.x, 0.25f, firePos.z); + } + else { + hitEffect.setLocalTranslation(firePos.x, 0.5f, firePos.z); + } + return hitEffect; + } + + /** + * this method converts 2d to 3d positions + * + * @param x x-Coordinate + * @param y-Coordinate + * @return the Position as a 3d Vector + */ + + private Vector3f mapToWorldCord(int x, int y) { + return new Vector3f(y + 0.5f, 0, x + 0.5f); + } + /** * this method creates the particles, when the shot misses * @@ -175,28 +219,6 @@ private ParticleEmitter createHitParticle(Shot shot) { 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. - * - * @param shot the shot to be represented - * @return the geometry representing the shot - */ - private Geometry createCylinder(Shot shot) { - final ColorRGBA color = shot.isHit() ? HIT_COLOR : SPLASH_COLOR; - final float height = shot.isHit() ? 1.2f : 0.1f; - - final Cylinder cylinder = new Cylinder(2, 20, 0.45f, height, true); - final Geometry geometry = new Geometry(SHOT, cylinder); - - geometry.setMaterial(createColoredMaterial(color)); - geometry.rotate(HALF_PI, 0f, 0f); - // compute the center of the shot in world coordinates - geometry.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f); - - return geometry; - } - /** * Visits a {@link Battleship} and creates a graphical representation of it. * The representation is either a 3D model or a simple box depending on the @@ -213,7 +235,7 @@ public Spatial visit(Battleship ship) { final float x = 0.5f * (ship.getMinY() + ship.getMaxY() + 1f); final float z = 0.5f * (ship.getMinX() + ship.getMaxX() + 1f); node.setLocalTranslation(x, 0f, z); - node.addControl(new ShipControl(ship)); + node.addControl(new ShipControl(ship, map)); return node; } @@ -271,6 +293,7 @@ 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.move(0, -0.2f, 0); model.setShadowMode(ShadowMode.CastAndReceive); diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipControl.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipControl.java index 81acfbb1..6ea7ca41 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipControl.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipControl.java @@ -11,9 +11,13 @@ import com.jme3.math.Vector3f; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; import com.jme3.scene.control.AbstractControl; import pp.battleship.model.Battleship; +import pp.battleship.model.ShipMap; +import static java.util.Objects.requireNonNull; import static pp.util.FloatMath.DEG_TO_RAD; import static pp.util.FloatMath.TWO_PI; import static pp.util.FloatMath.sin; @@ -48,6 +52,18 @@ class ShipControl extends AbstractControl { */ private float time; + /** + * this attribute is the ship, this ShipControl controls + */ + + private final Battleship ship; + + /** + * this CONST represents the sinking height, when the ship will be removed + */ + + private static final Float SINKING_HEIGHT = -0.6f; + /** * Constructs a new ShipControl instance for the specified Battleship. * The ship's orientation determines the axis of rotation, while its length influences @@ -55,7 +71,7 @@ class ShipControl extends AbstractControl { * * @param ship the Battleship object to control */ - public ShipControl(Battleship ship) { + public ShipControl(Battleship ship, ShipMap map) { // Determine the axis of rotation based on the ship's orientation axis = switch (ship.getRot()) { case LEFT, RIGHT -> Vector3f.UNIT_X; @@ -65,6 +81,7 @@ public ShipControl(Battleship ship) { // Set the cycle duration and amplitude based on the ship's length cycle = ship.getLength() * 2f; amplitude = 5f * DEG_TO_RAD / ship.getLength(); + this.ship = ship; } /** @@ -77,6 +94,12 @@ public ShipControl(Battleship ship) { protected void controlUpdate(float tpf) { // If spatial is null, do nothing if (spatial == null) return; + if (ship.isDestroyed() && spatial.getLocalTranslation().getY() < SINKING_HEIGHT) { // removes the ship, if it is completely sunk + spatial.getParent().detachChild(spatial); + } + else if (ship.isDestroyed() && spatial.getLocalTranslation().getY() >= SINKING_HEIGHT) { // sink the ship, if it's not completely sunk + spatial.move(0, tpf * 0.1f * -1, 0); + } // Update the time within the oscillation cycle time = (time + tpf) % cycle;