Aufgabe 12
added class ImpactEffectManager to implement the funktionality with hit or miss particles added map4 for particle testing purposes edited class SeaSynchronizer in order to implement the particle feature edited class ShipControl in order to implement the particle feature edited settings.gradle in order to implement the particle feature
This commit is contained in:
@@ -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
|
||||
|
||||
66
Projekte/battleship/client/maps/map4.json
Normal file
66
Projekte/battleship/client/maps/map4.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"width": 10,
|
||||
"height": 10,
|
||||
"ships": [
|
||||
{
|
||||
"length": 1,
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 1,
|
||||
"x": 9,
|
||||
"y": 1,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 1,
|
||||
"x": 8,
|
||||
"y": 0,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 1,
|
||||
"x": 8,
|
||||
"y": 1,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 2,
|
||||
"x": 8,
|
||||
"y": 2,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 2,
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 2,
|
||||
"x": 6,
|
||||
"y": 1,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 3,
|
||||
"x": 5,
|
||||
"y": 2,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 3,
|
||||
"x": 7,
|
||||
"y": 3,
|
||||
"rot": "RIGHT"
|
||||
},
|
||||
{
|
||||
"length": 4,
|
||||
"x": 6,
|
||||
"y": 4,
|
||||
"rot": "RIGHT"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* Manages the visual effects for impact events such as hits and misses.
|
||||
*/
|
||||
public class ImpactEffectManager {
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private static final Logger LOGGER = System.getLogger(ImpactEffectManager.class.getName());
|
||||
|
||||
private Material particleMaterial;
|
||||
|
||||
/**
|
||||
* Constructor to initialize the asset manager via the main application.
|
||||
*
|
||||
* @param app The main application instance.
|
||||
*/
|
||||
public ImpactEffectManager(Application app) {
|
||||
this.assetManager = app.getAssetManager();
|
||||
this.particleMaterial = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a visual effect for a hit at the specified position.
|
||||
*
|
||||
* @param battleshipNode The node where the effect should be attached.
|
||||
* @param shot The details of the shot where the hit occurred.
|
||||
*/
|
||||
public void triggerHitEffect(Node battleshipNode, Shot shot) {
|
||||
ParticleEmitter hitEffect = createParticleEmitter("HitEffect", 30, ColorRGBA.Orange, ColorRGBA.Red, 0.45f, 0.1f, 1f, 2f);
|
||||
hitEffect.setLocalTranslation(shot.getY() + 0.5f, 0, shot.getX() + 0.5f);
|
||||
|
||||
LOGGER.log(Level.DEBUG, "Hit effect created at position: {0}", hitEffect.getLocalTranslation().toString());
|
||||
|
||||
hitEffect.emitAllParticles();
|
||||
battleshipNode.attachChild(hitEffect);
|
||||
hitEffect.addControl(new EffectCleanupControl(hitEffect, battleshipNode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a visual effect for a missed shot at the specified location.
|
||||
*
|
||||
* @param shot The details of the missed shot.
|
||||
* @return The particle emitter representing the miss effect.
|
||||
*/
|
||||
public ParticleEmitter triggerMissEffect(Shot shot) {
|
||||
ParticleEmitter missEffect = createParticleEmitter("MissEffect", 15, ColorRGBA.Blue, ColorRGBA.Cyan, 0.3f, 0.05f, 0.5f, 1.5f);
|
||||
missEffect.setLocalTranslation(shot.getY() + 0.5f, 0, shot.getX() + 0.5f);
|
||||
missEffect.emitAllParticles();
|
||||
missEffect.addControl(new EffectCleanupControl(missEffect));
|
||||
return missEffect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a particle emitter with predefined parameters.
|
||||
*
|
||||
* @param name The name of the particle emitter.
|
||||
* @param count The number of particles to emit.
|
||||
* @param startColor The initial color of the particles.
|
||||
* @param endColor The final color of the particles.
|
||||
* @param startSize The initial size of the particles.
|
||||
* @param endSize The final size of the particles.
|
||||
* @param lowLife The minimum lifetime of the particles.
|
||||
* @param highLife The maximum lifetime of the particles.
|
||||
* @return The configured ParticleEmitter instance.
|
||||
*/
|
||||
private ParticleEmitter createParticleEmitter(String name, int count, ColorRGBA startColor, ColorRGBA endColor, float startSize, float endSize, float lowLife, float highLife) {
|
||||
ParticleEmitter emitter = new ParticleEmitter(name, Type.Triangle, count);
|
||||
emitter.setMaterial(particleMaterial);
|
||||
emitter.setImagesX(2);
|
||||
emitter.setImagesY(2);
|
||||
emitter.setStartColor(startColor);
|
||||
emitter.setEndColor(endColor);
|
||||
emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 1, 0));
|
||||
emitter.setStartSize(startSize);
|
||||
emitter.setEndSize(endSize);
|
||||
emitter.setGravity(0, -0.5f, 0);
|
||||
emitter.setLowLife(lowLife);
|
||||
emitter.setHighLife(highLife);
|
||||
emitter.setParticlesPerSec(0);
|
||||
return emitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom control class to handle the cleanup of particle effects once they are no longer visible.
|
||||
*/
|
||||
private static class EffectCleanupControl extends AbstractControl {
|
||||
|
||||
private final ParticleEmitter emitter;
|
||||
private final Node attachedNode;
|
||||
|
||||
/**
|
||||
* Constructor for managing cleanup when a particle emitter is attached to a specific node.
|
||||
*
|
||||
* @param emitter The particle emitter to manage.
|
||||
* @param attachedNode The node to which the emitter is attached.
|
||||
*/
|
||||
public EffectCleanupControl(ParticleEmitter emitter, Node attachedNode) {
|
||||
this.emitter = emitter;
|
||||
this.attachedNode = attachedNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for managing cleanup of a standalone particle emitter.
|
||||
*
|
||||
* @param emitter The particle emitter to manage.
|
||||
*/
|
||||
public EffectCleanupControl(ParticleEmitter emitter) {
|
||||
this(emitter, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the emitter when all particles are no longer visible.
|
||||
*
|
||||
* @param tpf Time per frame.
|
||||
*/
|
||||
@Override
|
||||
protected void controlUpdate(float tpf) {
|
||||
if (emitter.getParticlesPerSec() == 0 && emitter.getNumVisibleParticles() == 0) {
|
||||
if (attachedNode != null) {
|
||||
attachedNode.detachChild(emitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No custom rendering needed; particle behavior is handled in controlUpdate.
|
||||
* @param rm handles rendering, and ViewPort
|
||||
* @param vp defines the view where the scene is displayed.
|
||||
*/
|
||||
@Override
|
||||
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {
|
||||
// No rendering-specific behavior needed for this control.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
||||
|
||||
private final ShipMap map;
|
||||
private final BattleshipApp app;
|
||||
private ImpactEffectManager effectHandler;
|
||||
|
||||
/**
|
||||
* Constructs a {@code SeaSynchronizer} object with the specified application, root node, and ship map.
|
||||
@@ -59,6 +60,7 @@ public SeaSynchronizer(BattleshipApp app, Node root, ShipMap map) {
|
||||
super(app.getGameLogic().getOwnMap(), root);
|
||||
this.app = app;
|
||||
this.map = map;
|
||||
effectHandler = new ImpactEffectManager(app);
|
||||
addExisting();
|
||||
}
|
||||
|
||||
@@ -72,7 +74,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.triggerMissEffect(shot);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,9 +89,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.triggerHitEffect(shipNode, shot);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
import com.jme3.scene.control.AbstractControl;
|
||||
import pp.battleship.model.Battleship;
|
||||
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
|
||||
|
||||
import static pp.util.FloatMath.DEG_TO_RAD;
|
||||
import static pp.util.FloatMath.TWO_PI;
|
||||
import static pp.util.FloatMath.sin;
|
||||
@@ -47,6 +51,14 @@ class ShipControl extends AbstractControl {
|
||||
* The current time within the oscillation cycle, used to calculate the ship's pitch angle.
|
||||
*/
|
||||
private float time;
|
||||
/**
|
||||
* The current Ship
|
||||
*/
|
||||
private final Battleship battleship;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static final Logger LOGGER = System.getLogger(ShipControl.class.getName());
|
||||
|
||||
/**
|
||||
* Constructs a new ShipControl instance for the specified Battleship.
|
||||
@@ -56,6 +68,7 @@ class ShipControl extends AbstractControl {
|
||||
* @param ship the Battleship object to control
|
||||
*/
|
||||
public ShipControl(Battleship ship) {
|
||||
battleship = ship;
|
||||
// Determine the axis of rotation based on the ship's orientation
|
||||
axis = switch (ship.getRot()) {
|
||||
case LEFT, RIGHT -> Vector3f.UNIT_X;
|
||||
@@ -63,7 +76,7 @@ public ShipControl(Battleship ship) {
|
||||
};
|
||||
|
||||
// Set the cycle duration and amplitude based on the ship's length
|
||||
cycle = ship.getLength() * 2f;
|
||||
cycle = battleship.getLength() * 2f;
|
||||
amplitude = 5f * DEG_TO_RAD / ship.getLength();
|
||||
}
|
||||
|
||||
@@ -80,15 +93,21 @@ protected void controlUpdate(float tpf) {
|
||||
|
||||
// Update the time within the oscillation cycle
|
||||
time = (time + tpf) % cycle;
|
||||
if (battleship.isDestroyed() && spatial.getLocalTranslation().getY() <= -0.6f) {
|
||||
LOGGER.log(Level.INFO, "Ship removed {0}", spatial.getName());
|
||||
spatial.getParent().detachChild(spatial);
|
||||
} else if (battleship.isDestroyed()) {
|
||||
spatial.move(0, -0.05f * tpf, 0);
|
||||
} else {
|
||||
// Update the time within the oscillation cycle
|
||||
time = (time + tpf) % cycle;
|
||||
|
||||
// Calculate the current angle of the oscillation
|
||||
final float angle = amplitude * sin(time * TWO_PI / cycle);
|
||||
// Apply the pitch rotation to the spatial
|
||||
spatial.setLocalRotation(pitch);
|
||||
}
|
||||
|
||||
// Update the pitch Quaternion with the new angle
|
||||
pitch.fromAngleAxis(angle, axis);
|
||||
|
||||
// Apply the pitch rotation to the spatial
|
||||
spatial.setLocalRotation(pitch);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
library('mockito-core', 'org.mockito:mockito-core:3.+')
|
||||
library('groovy-jsr223', 'org.apache.groovy:groovy-jsr223:4.0.22')
|
||||
library('slf4j-nop', 'org.slf4j:slf4j-nop:2.0.13')
|
||||
|
||||
library('jme3-effects', 'org.jmonkeyengine', 'jme3-effects').versionRef('jme')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user