added realistic visual effects to the game
- imported the jme3-effects library - edited SeaSynchronizer to handle different effects - added to the ShipControl class in the controlUpdate(float tpf) method the handler that moves a destroyed ship downward
This commit is contained in:
@@ -9,11 +9,13 @@ 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
|
||||
runtimeOnly libs.jme3.jogg
|
||||
runtimeOnly libs.jme3.testdata
|
||||
|
||||
}
|
||||
|
||||
application {
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
|
||||
package pp.battleship.client.gui;
|
||||
|
||||
import com.jme3.effect.ParticleEmitter;
|
||||
import com.jme3.effect.ParticleMesh;
|
||||
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;
|
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||
import com.jme3.scene.Geometry;
|
||||
@@ -23,6 +26,10 @@
|
||||
import pp.battleship.model.ShipMap;
|
||||
import pp.battleship.model.Shot;
|
||||
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static pp.util.FloatMath.HALF_PI;
|
||||
import static pp.util.FloatMath.PI;
|
||||
@@ -34,6 +41,8 @@
|
||||
* logic for the sea map.
|
||||
*/
|
||||
class SeaSynchronizer extends ShipMapSynchronizer {
|
||||
private static final Logger LOGGER = System.getLogger(SeaSynchronizer.class.getName());
|
||||
|
||||
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 DESTROYER_MODEL = "Models/Destroyer/Destroyer.j3o"; //NON-NLS
|
||||
@@ -75,7 +84,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) : handleMiss(shot);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,33 +99,90 @@ 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 debris = createDebrisEffect(shot);
|
||||
shipNode.attachChild(debris);
|
||||
|
||||
final ParticleEmitter fire = createFireEffect(shot, shipNode);
|
||||
shipNode.attachChild(fire);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
private Spatial handleMiss(Shot shot) {
|
||||
return createMissEffect(shot);
|
||||
}
|
||||
|
||||
final Cylinder cylinder = new Cylinder(2, 20, 0.45f, height, true);
|
||||
final Geometry geometry = new Geometry(SHOT, cylinder);
|
||||
private ParticleEmitter createMissEffect(Shot shot) {
|
||||
final ParticleEmitter water = new ParticleEmitter("WaterEmitter", ParticleMesh.Type.Triangle, 20);
|
||||
|
||||
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);
|
||||
Material waterMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||
waterMaterial.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
|
||||
water.setMaterial(waterMaterial);
|
||||
|
||||
return geometry;
|
||||
water.setImagesX(2);
|
||||
water.setImagesY(2);
|
||||
water.setStartColor(ColorRGBA.Cyan);
|
||||
water.setEndColor(ColorRGBA.Blue);
|
||||
water.getParticleInfluencer().setInitialVelocity(new Vector3f(0.1f, 0.1f, 0.1f));
|
||||
water.setStartSize(0.4f);
|
||||
water.setEndSize(0.45f);
|
||||
water.setGravity(0, -0.5f, 0);
|
||||
water.setLowLife(1f);
|
||||
water.setHighLife(1f);
|
||||
water.setParticlesPerSec(0);
|
||||
|
||||
water.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
|
||||
water.emitAllParticles();
|
||||
return water;
|
||||
}
|
||||
|
||||
private ParticleEmitter createDebrisEffect(Shot shot) {
|
||||
final ParticleEmitter debris = new ParticleEmitter("DebrisEmitter", ParticleMesh.Type.Triangle, 2);
|
||||
|
||||
Material debrisMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||
debrisMaterial.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png"));
|
||||
debris.setMaterial(debrisMaterial);
|
||||
|
||||
debris.setImagesX(2);
|
||||
debris.setImagesY(2);
|
||||
debris.setStartColor(ColorRGBA.White);
|
||||
debris.setEndColor(ColorRGBA.White);
|
||||
debris.getParticleInfluencer().setInitialVelocity(new Vector3f(0.1f, 2f, 0.1f));
|
||||
debris.setStartSize(0.1f);
|
||||
debris.setEndSize(0.5f);
|
||||
debris.setGravity(0, 3f, 0);
|
||||
debris.getParticleInfluencer().setVelocityVariation(.40f);
|
||||
debris.setLowLife(1f);
|
||||
debris.setHighLife(1.5f);
|
||||
debris.setParticlesPerSec(0);
|
||||
|
||||
debris.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
|
||||
debris.emitAllParticles();
|
||||
return debris;
|
||||
}
|
||||
|
||||
private ParticleEmitter createFireEffect(Shot shot, Node shipNode) {
|
||||
ParticleEmitter fire = new ParticleEmitter("FireEmitter", ParticleMesh.Type.Triangle, 100);
|
||||
|
||||
Material fireMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||
fireMaterial.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
|
||||
fire.setMaterial(fireMaterial);
|
||||
|
||||
fire.setImagesX(2);
|
||||
fire.setImagesY(2);
|
||||
fire.setStartColor(ColorRGBA.Orange);
|
||||
fire.setEndColor(ColorRGBA.Red);
|
||||
fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 1.5f, 0));
|
||||
fire.setStartSize(0.2f);
|
||||
fire.setEndSize(0.05f);
|
||||
fire.setLowLife(1f);
|
||||
fire.setHighLife(2f);
|
||||
fire.getParticleInfluencer().setVelocityVariation(0.2f);
|
||||
|
||||
fire.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
|
||||
fire.getLocalTranslation().subtractLocal(shipNode.getLocalTranslation());
|
||||
|
||||
return fire;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import com.jme3.scene.control.AbstractControl;
|
||||
import pp.battleship.model.Battleship;
|
||||
import pp.battleship.model.ShipMap;
|
||||
|
||||
import static pp.util.FloatMath.DEG_TO_RAD;
|
||||
import static pp.util.FloatMath.TWO_PI;
|
||||
@@ -48,6 +49,10 @@ class ShipControl extends AbstractControl {
|
||||
*/
|
||||
private float time;
|
||||
|
||||
private final Battleship ship;
|
||||
|
||||
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
|
||||
@@ -65,6 +70,8 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,6 +85,15 @@ protected void controlUpdate(float tpf) {
|
||||
// If spatial is null, do nothing
|
||||
if (spatial == null) return;
|
||||
|
||||
// Handle ship sinking by moving it downwards
|
||||
if (ship.isDestroyed()) {
|
||||
if (spatial.getLocalTranslation().getY() < SINKING_HEIGHT) {
|
||||
spatial.getParent().detachChild(spatial);
|
||||
} else {
|
||||
spatial.move(0, -tpf * 0.1f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the time within the oscillation cycle
|
||||
time = (time + tpf) % cycle;
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user