diff --git a/Projekte/battleship/client/build.gradle b/Projekte/battleship/client/build.gradle index a8aeff3c..248d1e83 100644 --- a/Projekte/battleship/client/build.gradle +++ b/Projekte/battleship/client/build.gradle @@ -9,6 +9,7 @@ implementation project(":jme-common") implementation project(":battleship:model") implementation libs.jme3.desktop + implementation libs.jme3.effects runtimeOnly libs.jme3.awt.dialogs runtimeOnly libs.jme3.plugins diff --git a/Projekte/battleship/client/client.properties b/Projekte/battleship/client/client.properties index 4403fa3b..89d59df4 100644 --- a/Projekte/battleship/client/client.properties +++ b/Projekte/battleship/client/client.properties @@ -9,7 +9,7 @@ # # Specifies the map used by the opponent in single mode. # Single mode is activated if this property is set. -#map.opponent=maps/map2.json +map.opponent=maps/map2.json # # Specifies the map used by the player in single mode. # The player must define their own map if this property is not set. diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/BackgroundMusic.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/BackgroundMusic.java index 7b497cbd..888d5231 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/BackgroundMusic.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/BackgroundMusic.java @@ -174,28 +174,28 @@ public boolean isMusicEnabled() { */ public void changeMusic(Music music) { if(music == Music.MENU_THEME && !lastNodePlayed.equals(MENU_MUSIC)) { - LOGGER.log(Level.DEBUG, "Received Music change Event {0}", music.toString()); + LOGGER.log(Level.INFO, "Received Music change Event {0}", music.toString()); stop(battleMusic); stop(gameOverMusicL); stop(gameOverMusicV); play(menuMusic); lastNodePlayed = menuMusic.getName(); } else if (music == Music.BATTLE_THEME && !lastNodePlayed.equals(BATTLE_MUSIC)) { - LOGGER.log(Level.DEBUG, "Received Music change Event {0}", music.toString()); + LOGGER.log(Level.INFO, "Received Music change Event {0}", music.toString()); stop(menuMusic); stop(gameOverMusicL); stop(gameOverMusicV); play(battleMusic); lastNodePlayed = battleMusic.getName(); } else if (music == Music.GAME_OVER_THEME_L && !lastNodePlayed.equals(GAME_OVER_MUSIC_L)) { - LOGGER.log(Level.DEBUG, "Received Music change Event {0}", music.toString()); + LOGGER.log(Level.INFO, "Received Music change Event {0}", music.toString()); stop(menuMusic); stop(battleMusic); stop(gameOverMusicV); play(gameOverMusicL); lastNodePlayed = gameOverMusicL.getName(); } else if (music == Music.GAME_OVER_THEME_V && !lastNodePlayed.equals(GAME_OVER_MUSIC_V)){ - LOGGER.log(Level.DEBUG, "Received Music change Event {0}", music.toString()); + LOGGER.log(Level.INFO, "Received Music change Event {0}", music.toString()); stop(menuMusic); stop(battleMusic); stop(gameOverMusicL); @@ -211,7 +211,7 @@ public void changeMusic(Music music) { */ @Override public void receivedEvent (MusicEvent music){ - LOGGER.log(Level.DEBUG, "Received Music change Event {0}", music.toString()); + LOGGER.log(Level.INFO, "Received Music change Event {0}", music.toString()); switch (music.music()){ case MENU_THEME: changeMusic(Music.MENU_THEME); diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java index ead1a6de..dba290e1 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java @@ -115,7 +115,7 @@ private void connect() { private void startServer() { new Thread(() -> { try{ - BattleshipServer battleshipServer = new BattleshipServer(); + BattleshipServer battleshipServer = new BattleshipServer(Integer.parseInt(port.getText())); battleshipServer.run(); } catch (Exception e) { LOGGER.log(Level.ERROR, e.getMessage(), e); diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EffectHandler.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EffectHandler.java new file mode 100644 index 00000000..3b9c956a --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EffectHandler.java @@ -0,0 +1,142 @@ +package pp.battleship.client.gui; + +import com.jme3.app.Application; +import com.jme3.asset.AssetManager; +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh; +import com.jme3.effect.ParticleMesh.Type; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.scene.control.AbstractControl; +import pp.battleship.model.Shot; + +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + +/** + * This class is used to handle the effects for impacts + */ +public class EffectHandler { + + private final AssetManager assetManager; + static final Logger LOGGER = System.getLogger(EffectHandler.class.getName()); + + private Material particleMat; + + /** + * the constructor is used to get the asset manager from the app + * + * @param app the main application + */ + public EffectHandler(Application app) { + assetManager = app.getAssetManager(); + particleMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); + } + + /** + * this method is used to create a hit effect at a position + * + * @param battleshipNode the node of the battleship where the effect should be attached to + */ + public void createHitEffect(Node battleshipNode, Shot shot) { + ParticleEmitter hitEffect = new ParticleEmitter("HitEffect", Type.Triangle,30); + hitEffect.setMaterial(particleMat); + hitEffect.setImagesX(2); + hitEffect.setImagesY(2); + hitEffect.setStartColor(ColorRGBA.Orange); + hitEffect.setEndColor(ColorRGBA.Red); + hitEffect.getParticleInfluencer().setInitialVelocity(new Vector3f(0,1,0)); + 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); + LOGGER.log(Level.DEBUG, "Created HitEffect at {0}", hitEffect.getLocalTranslation().toString()); + LOGGER.log(Level.DEBUG, "Created HitEffect at {0}", hitEffect.getLocalTranslation().toString()); + + hitEffect.emitAllParticles(); + + battleshipNode.attachChild(hitEffect); + + hitEffect.addControl(new EffectControl(hitEffect, battleshipNode)); + } + + /** + * This method is used to create a miss effect at a certain location + */ + public ParticleEmitter createMissEffect(Shot shot) { + ParticleEmitter missEffect = new ParticleEmitter("MissEffect", Type.Triangle, 15); + missEffect.setMaterial(particleMat); + missEffect.setImagesX(2); + missEffect.setImagesY(2); + missEffect.setStartColor(ColorRGBA.Blue); // Water color + missEffect.setEndColor(ColorRGBA.Cyan); + missEffect.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 1, 0)); + missEffect.setStartSize(0.3f); + missEffect.setEndSize(0.05f); + missEffect.setGravity(0, -0.1f, 0); + missEffect.setLowLife(0.5f); + missEffect.setHighLife(1.5f); + missEffect.setParticlesPerSec(0); + missEffect.setLocalTranslation(shot.getY() + 0.5f, 0 , shot.getX() + 0.5f); + missEffect.emitAllParticles(); + + missEffect.addControl(new EffectControl(missEffect)); + + return missEffect; + } + + /** + * This inner class is used to control the effects + */ + private static class EffectControl extends AbstractControl { + private final ParticleEmitter emitter; + private final Node parentNode; + + /** + * this constructor is used to when the effect should be attached to a specific node + * + * @param emitter the Particle emitter to be controlled + * @param parentNode the node to be attached + */ + public EffectControl(ParticleEmitter emitter, Node parentNode) { + this.emitter = emitter; + this.parentNode = parentNode; + } + + /** + * This constructor is used when the effect shouldn't be attached to + * a specific node + * + * @param emitter the Particle emitter to be controlled + */ + public EffectControl(ParticleEmitter emitter){ + this.emitter = emitter; + this.parentNode = null; + } + + /** + * The method which checks if the Effect is not rendered anymore so it can be removed + * + * @param tpf time per frame (in seconds) + */ + @Override + protected void controlUpdate(float tpf) { + if (emitter.getParticlesPerSec() == 0 && emitter.getNumVisibleParticles() == 0) { + if (parentNode != null) + parentNode.detachChild(emitter); + } + } + + /** + * @param rm the RenderManager rendering the controlled Spatial (not null) + * @param vp the ViewPort being rendered (not null) + */ + @Override + protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {} + } +} 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 17cebcfd..a3f5d351 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; @@ -45,6 +46,8 @@ class SeaSynchronizer extends ShipMapSynchronizer { private static final ColorRGBA SPLASH_COLOR = new ColorRGBA(0f, 0f, 1f, 0.4f); private static final ColorRGBA HIT_COLOR = new ColorRGBA(1f, 0f, 0f, 0.4f); + private EffectHandler effectHandler; + private final ShipMap map; private final BattleshipApp app; @@ -59,6 +62,7 @@ public SeaSynchronizer(BattleshipApp app, Node root, ShipMap map) { super(app.getGameLogic().getOwnMap(), root); this.app = app; this.map = map; + effectHandler = new EffectHandler(app); addExisting(); } @@ -72,7 +76,7 @@ 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) : effectHandler.createMissEffect(shot); } /** @@ -87,9 +91,7 @@ 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); + effectHandler.createHitEffect(shipNode, shot); return null; } diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipServer.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipServer.java index f2d52ecb..67b5e9f9 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipServer.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipServer.java @@ -45,6 +45,8 @@ public class BattleshipServer implements MessageListener, Conn private static final Logger LOGGER = System.getLogger(BattleshipServer.class.getName()); private static final File CONFIG_FILE = new File("server.properties"); + private static int PORT; + private final BattleshipConfig config = new BattleshipConfig(); private Server myServer; private final ServerGameLogic logic; @@ -65,8 +67,9 @@ public class BattleshipServer implements MessageListener, Conn /** * Creates the server. */ - public BattleshipServer() { + public BattleshipServer(int port) { config.readFromIfExists(CONFIG_FILE); + PORT = port; LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS logic = new ServerGameLogic(this, config); } @@ -80,7 +83,7 @@ public void run() { private void startServer() { try { LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS - myServer = Network.createServer(config.getPort()); + myServer = Network.createServer(PORT); initializeSerializables(); myServer.start(); registerListeners(); diff --git a/Projekte/settings.gradle b/Projekte/settings.gradle index 7440607c..bb7e8c72 100644 --- a/Projekte/settings.gradle +++ b/Projekte/settings.gradle @@ -19,6 +19,7 @@ library('jme3-testdata', 'org.jmonkeyengine', 'jme3-testdata').versionRef('jme') library('jme3-lwjgl', 'org.jmonkeyengine', 'jme3-lwjgl').versionRef('jme') library('jme3-lwjgl3', 'org.jmonkeyengine', 'jme3-lwjgl3').versionRef('jme') + library('jme3-effects', 'org.jmonkeyengine', 'jme3-effects').versionRef('jme') library('lemur', 'com.simsilica:lemur:1.16.0') library('lemur-proto', 'com.simsilica:lemur-proto:1.13.0')