diff --git a/Projekte/battleship/client/client.properties b/Projekte/battleship/client/client.properties index 89d59df4..48eb5ef9 100644 --- a/Projekte/battleship/client/client.properties +++ b/Projekte/battleship/client/client.properties @@ -26,7 +26,10 @@ map.own=maps/map1.json robot.targets=2, 0,\ 2, 1,\ 2, 2,\ - 2, 3 + 2, 3,\ + 2, 4,\ + 2, 5,\ + 2, 6 # # Delay in milliseconds between each shot fired by the RobotClient. robot.delay=500 diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/BattleshipApp.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/BattleshipApp.java index 0062e4e5..9aa48c95 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/BattleshipApp.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/BattleshipApp.java @@ -40,6 +40,9 @@ import java.io.IOException; import java.lang.System.Logger; import java.lang.System.Logger.Level; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.LogManager; @@ -122,6 +125,8 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient */ private final ActionListener escapeListener = (name, isPressed, tpf) -> escape(isPressed); + private EffectHandler effectHandler; + static { // Configure logging LogManager manager = LogManager.getLogManager(); @@ -155,6 +160,7 @@ private BattleshipApp() { logic.addListener(this); setShowSettings(config.getShowSettings()); setSettings(makeSettings()); + effectHandler = null; } /** @@ -224,6 +230,7 @@ public void simpleInitApp() { setupInput(); setupStates(); setupGui(); + effectHandler = new EffectHandler(this); serverConnection.connect(); } @@ -302,6 +309,19 @@ public void simpleUpdate(float tpf) { super.simpleUpdate(tpf); dialogManager.update(tpf); logic.update(tpf); + handleTimers(tpf); + } + + private void handleTimers(float tpf) { + Iterator iter = timerList.iterator(); + + while (iter.hasNext()) { + Timer next = iter.next(); + next.update(tpf); + if (next.isFinished()){ + iter.remove(); + } + } } /** @@ -437,4 +457,14 @@ void errorDialog(String errorMessage) { .build() .open(); } + + public EffectHandler getEffectHandler(){ + return effectHandler; + } + + private final List timerList = new ArrayList<>(); + + public void setTimer(float time, Runnable runnable){ + timerList.add(new Timer(time, runnable)); + } } diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/EffectHandler.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/EffectHandler.java new file mode 100644 index 00000000..3de68f52 --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/EffectHandler.java @@ -0,0 +1,140 @@ +package pp.battleship.client; + +import com.jme3.app.Application; +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh; +import com.jme3.effect.influencers.RadialParticleInfluencer; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import pp.battleship.model.Battleship; +import pp.battleship.model.IntPoint; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EffectHandler { + private BattleshipApp app; + private Map> effects; + + + public EffectHandler(BattleshipApp app){ + this.app = app; + effects = new HashMap<>(); + } + + public Node createFire(Vector3f point, Battleship ship){ + Node parent = new Node(); + parent.setLocalTranslation(point); + + ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 50); + Material matRed = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + matRed.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png")); + fire.setMaterial(matRed); + fire.setImagesX(2); + fire.setImagesY(2); + fire.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); + fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); + fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 1.5f, 0)); + fire.setStartSize(.4f); + fire.setEndSize(0.05f); + fire.setGravity(0, 0, 0); + fire.setLowLife(1f); + fire.setHighLife(2f); + fire.getParticleInfluencer().setVelocityVariation(0.2f); + parent.attachChild(fire); + + ParticleEmitter smoke = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 600); + Material matBlack = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + matBlack.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png")); + smoke.setMaterial(matBlack); + smoke.setImagesX(15); + smoke.setImagesY(1); + smoke.setEndColor(new ColorRGBA(1f,1f,1f,0f)); + smoke.setStartColor(new ColorRGBA(0.5f,0.5f,0.5f,0.5f)); + smoke.getParticleInfluencer().setInitialVelocity(new Vector3f(0,1f, 0)); + smoke.setStartSize(.2f); + smoke.setEndSize(0.1f); + smoke.setGravity(0, 0, 0); + smoke.setLowLife(1f); + smoke.setHighLife(5f); + smoke.getParticleInfluencer().setVelocityVariation(0.25f); + parent.attachChild(smoke); + + + List oldEffects = new ArrayList<>(effects.getOrDefault(ship,new ArrayList<>())); + oldEffects.add(fire); + oldEffects.add(smoke); + effects.put(ship,oldEffects); + + return parent; + } + + public void destroyShip(Battleship ship){ + for (ParticleEmitter emitter: effects.get(ship)){ + emitter.setParticlesPerSec(0); + } + } + + public Geometry waterSplash(Vector3f pos){ + ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 100); + Material matRed = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + matRed.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flash.png")); + fire.setMaterial(matRed); + fire.setImagesX(2); + fire.setImagesY(2); // 2x2 texture animation + fire.setEndColor(new ColorRGBA(0.3f, 0.8f, 1f, 0f)); + fire.setStartColor(new ColorRGBA(0f, 0f, 1f, 1f)); + RadialParticleInfluencer inf = new RadialParticleInfluencer(); + inf.setRadialVelocity(5); + inf.setVelocityVariation(0.3f); + inf.setInitialVelocity(new Vector3f(0,3,0)); + fire.setParticleInfluencer(inf); + fire.setStartSize(.6f); + fire.setEndSize(0.05f); + fire.setGravity(0, 4f, 0); + fire.setLowLife(1f); + fire.setHighLife(1.5f); + fire.setLocalTranslation(pos); + fire.emitAllParticles(); + fire.setParticlesPerSec(0); + app.setTimer(2,()->deleteSplash(fire)); + return fire; + } + + private void deleteSplash(Geometry splash){ + splash.getParent().detachChild(splash); + } + + + public Geometry debrisSplash(Vector3f pos){ + ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 50); + Material matRed = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md"); + matRed.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png")); + fire.setMaterial(matRed); + fire.setImagesX(3); + fire.setImagesY(3); // 2x2 texture animation + fire.setEndColor(new ColorRGBA(0.1f, 0.1f, 0.1f, 0f)); + fire.setStartColor(new ColorRGBA(0.5f, 0.5f, 0.5f, .8f)); + fire.setStartSize(0.1f); + fire.setEndSize(0.5f); + fire.setGravity(0, 2f, 0); + fire.setLowLife(1f); + fire.setHighLife(1.5f); + fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0,2f,0)); + fire.getParticleInfluencer().setVelocityVariation(.5f); + + + fire.setLocalTranslation(pos); + + fire.emitAllParticles(); + fire.setParticlesPerSec(0); + + return fire; + } +} diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/GameMusic.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/GameMusic.java index 69bd3461..1c636e31 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/GameMusic.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/GameMusic.java @@ -3,18 +3,13 @@ import com.jme3.app.Application; import com.jme3.app.state.AbstractAppState; import com.jme3.app.state.AppStateManager; -import com.jme3.asset.AssetLoadException; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.audio.AudioData; import com.jme3.audio.AudioNode; import pp.battleship.notification.GameEventListener; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; import java.util.prefs.Preferences; +import static pp.JmeUtil.loadSound; import static pp.util.PreferencesUtils.getPreferences; -import static pp.util.Util.loadSound; /** * An application state that plays sounds. diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java index 8adcb432..189eef96 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java @@ -18,8 +18,8 @@ import java.lang.System.Logger.Level; import java.util.prefs.Preferences; +import static pp.JmeUtil.loadSound; import static pp.util.PreferencesUtils.getPreferences; -import static pp.util.Util.loadSound; /** * An application state that plays sounds. diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/Timer.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/Timer.java new file mode 100644 index 00000000..5fab313a --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/Timer.java @@ -0,0 +1,28 @@ +package pp.battleship.client; + +public class Timer { + private float time; + private final Runnable runnable; + private boolean finished; + + public Timer(float time, Runnable runnable){ + this.time = time; + this.runnable = runnable; + this.finished = false; + } + + public void update(float delta){ + if(!finished){ + time -= delta; + if(time < 0){ + finished = true; + runnable.run(); + } + } + } + + public boolean isFinished(){ + return finished; + } + +} 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 92cc4529..df40f173 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,17 +7,13 @@ package pp.battleship.client.gui; -import com.jme3.bounding.BoundingVolume; -import com.jme3.collision.Collidable; -import com.jme3.collision.CollisionResults; -import com.jme3.collision.UnsupportedCollisionException; import com.jme3.material.Material; import com.jme3.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Node; -import com.jme3.scene.SceneGraphVisitor; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Cylinder; @@ -27,8 +23,6 @@ import pp.battleship.model.ShipMap; import pp.battleship.model.Shot; -import java.util.Queue; - import static java.util.Objects.requireNonNull; import static pp.util.FloatMath.HALF_PI; import static pp.util.FloatMath.PI; @@ -76,7 +70,11 @@ 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); + } + + private Spatial handleMiss(Shot shot) { + return app.getEffectHandler().waterSplash(mapToWorldCord(shot.getX(), shot.getY())); } /** @@ -91,13 +89,24 @@ 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); - + shipNode.getControl(ShipEffectControl.class).hit(shot); + if(ship.isDestroyed()){ + shipNode.getControl(ShipEffectControl.class).destroyed(); + shipNode.getControl(ShipMovementControl.class).destroyed(); + app.setTimer(9,()->handleShipDestroy(shipNode)); + } return null; } + private void handleShipDestroy(Node shipNode) { + shipNode.getParent().detachChild(shipNode); + } + + private Vector3f mapToWorldCord(int x, int y){ + return new Vector3f(y+0.5f, 0, x+0.5f); + } + + /** * Creates a cylinder geometry representing the specified shot. * The appearance of the cylinder depends on whether the shot is a hit or a miss. @@ -136,7 +145,8 @@ 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 ShipMovementControl(ship)); + node.addControl(new ShipEffectControl(node, ship, app)); return node; } diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipEffectControl.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipEffectControl.java new file mode 100644 index 00000000..fe9d1d09 --- /dev/null +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipEffectControl.java @@ -0,0 +1,55 @@ +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; +import pp.battleship.client.BattleshipApp; +import pp.battleship.client.EffectHandler; +import pp.battleship.model.Battleship; +import pp.battleship.model.Shot; + +public class ShipEffectControl extends AbstractControl { + private final Node shipNode; + private final Battleship battleship; + private final BattleshipApp app; + + public ShipEffectControl(Node node, Battleship battleship, BattleshipApp app){ + this.shipNode = node; + this.battleship = battleship; + this.app = app; + } + + + @Override + protected void controlUpdate(float tpf) { + + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + + } + + public void hit(Shot shot){ + Vector3f shipNodePos = shipNode.getLocalTranslation(); + Vector3f shotWorld = mapToWorldCord(shot.getX(),shot.getY()); + Vector3f firePos = shotWorld.subtract(shipNodePos); + shipNode.attachChild(app.getEffectHandler().createFire(firePos, battleship)); + + } + + private Vector3f mapToWorldCord(int x, int y){ + return new Vector3f(y+0.5f, 0, x+0.5f); + } + + public void destroyed() { + shipNode.attachChild(app.getEffectHandler().debrisSplash(shipNode.getLocalTranslation())); + app.setTimer(4,this::stopEffects); + } + + private void stopEffects(){ + app.getEffectHandler().destroyShip(battleship); + } +} 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/ShipMovementControl.java similarity index 81% rename from Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipControl.java rename to Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShipMovementControl.java index 81acfbb1..32a7b1af 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/ShipMovementControl.java @@ -22,7 +22,10 @@ * Controls the oscillating pitch motion of a battleship model in the game. * The ship oscillates to simulate a realistic movement on water, based on its orientation and length. */ -class ShipControl extends AbstractControl { +class ShipMovementControl extends AbstractControl { + private final float sinkingSpeed = 0.04f; + private final float sinkRotSpeed = 0.1f; + /** * The axis of rotation for the ship's pitch (tilting forward and backward). */ @@ -48,14 +51,17 @@ class ShipControl extends AbstractControl { */ private float time; + private boolean sinking; + /** - * Constructs a new ShipControl instance for the specified Battleship. + * Constructs a new ShipMovementControl instance for the specified Battleship. * The ship's orientation determines the axis of rotation, while its length influences * the cycle duration and amplitude of the oscillation. * * @param ship the Battleship object to control */ - public ShipControl(Battleship ship) { + public ShipMovementControl(Battleship ship) { + sinking = false; // Determine the axis of rotation based on the ship's orientation axis = switch (ship.getRot()) { case LEFT, RIGHT -> Vector3f.UNIT_X; @@ -75,6 +81,18 @@ public ShipControl(Battleship ship) { */ @Override protected void controlUpdate(float tpf) { + if(sinking) handleSinking(tpf); + else handlePitch(tpf); + } + + private void handleSinking(float tpf) { + if (spatial == null) return; + + spatial.setLocalTranslation(spatial.getLocalTranslation().add(new Vector3f(0,-1,0).mult(tpf * sinkingSpeed))); + spatial.rotate(tpf * sinkRotSpeed, 0, 0); + } + + private void handlePitch(float tpf){ // If spatial is null, do nothing if (spatial == null) return; @@ -103,4 +121,8 @@ protected void controlUpdate(float tpf) { protected void controlRender(RenderManager rm, ViewPort vp) { // No rendering logic is needed for this control } + + public void destroyed() { + sinking = true; + } } diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/Debris.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/Debris.png new file mode 100644 index 00000000..c6eab7ba Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/Debris.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/flame.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/flame.png new file mode 100644 index 00000000..d3a31f79 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/flame.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/flash.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/flash.png new file mode 100644 index 00000000..d2409175 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/flash.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/roundspark.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/roundspark.png new file mode 100644 index 00000000..bb2b5d84 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/roundspark.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/shockwave.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/shockwave.png new file mode 100644 index 00000000..62856bd1 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/shockwave.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/smoketrail.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/smoketrail.png new file mode 100644 index 00000000..5b965182 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/smoketrail.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Explosion/spark.png b/Projekte/battleship/client/src/main/resources/Effects/Explosion/spark.png new file mode 100644 index 00000000..fa2d25a0 Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Explosion/spark.png differ diff --git a/Projekte/battleship/client/src/main/resources/Effects/Smoke/Smoke.png b/Projekte/battleship/client/src/main/resources/Effects/Smoke/Smoke.png new file mode 100644 index 00000000..ec46564b Binary files /dev/null and b/Projekte/battleship/client/src/main/resources/Effects/Smoke/Smoke.png differ diff --git a/Projekte/common/src/main/java/pp/util/Util.java b/Projekte/common/src/main/java/pp/util/Util.java index 2523cb19..46c07ee2 100644 --- a/Projekte/common/src/main/java/pp/util/Util.java +++ b/Projekte/common/src/main/java/pp/util/Util.java @@ -7,14 +7,14 @@ package pp.util; -import com.jme3.app.Application; -import com.jme3.asset.AssetLoadException; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.audio.AudioData; -import com.jme3.audio.AudioNode; +//import com.jme3.app.Application; +//import com.jme3.asset.AssetLoadException; +//import com.jme3.asset.AssetNotFoundException; +//import com.jme3.audio.AudioData; +//import com.jme3.audio.AudioNode; import java.lang.System.Logger; -import java.lang.System.Logger.Level; +//import java.lang.System.Logger.Level; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -25,8 +25,6 @@ * A class with auxiliary functions. */ public class Util { - private static final Logger LOGGER = System.getLogger(Util.class.getName()); - private Util() { /* do not instantiate */ } /** @@ -97,24 +95,4 @@ public static Set add(Set set, E element) { newSet.add(element); return newSet; } - - /** - * Loads a sound from the specified file. - * - * @param app The application - * @param name The name of the sound file. - * @return The loaded AudioNode. - */ - public static AudioNode loadSound(Application app, String name) { - try { - final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer); - sound.setLooping(false); - sound.setPositional(false); - return sound; - } - catch (AssetLoadException | AssetNotFoundException ex) { - LOGGER.log(Level.ERROR, ex.getMessage(), ex); - } - return null; - } } diff --git a/Projekte/jme-common/src/main/java/pp/JmeUtil.java b/Projekte/jme-common/src/main/java/pp/JmeUtil.java new file mode 100644 index 00000000..564cb444 --- /dev/null +++ b/Projekte/jme-common/src/main/java/pp/JmeUtil.java @@ -0,0 +1,37 @@ +package pp; + +import com.jme3.app.Application; +import com.jme3.asset.AssetLoadException; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.audio.AudioData; +import com.jme3.audio.AudioNode; +import pp.util.Util; + +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + +public class JmeUtil { + private JmeUtil(){ /* Do not initialize */ } + + private static final Logger LOGGER = System.getLogger(Util.class.getName()); + + /** + * Loads a sound from the specified file. + * + * @param app The application + * @param name The name of the sound file. + * @return The loaded AudioNode. + */ + public static AudioNode loadSound(Application app, String name) { + try { + final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer); + sound.setLooping(false); + sound.setPositional(false); + return sound; + } + catch (AssetLoadException | AssetNotFoundException ex) { + LOGGER.log(Level.ERROR, ex.getMessage(), ex); + } + return null; + } +}