Mehrere Probleme behoben
This commit is contained in:
@@ -28,6 +28,7 @@ import de.blight.game.state.DrownState;
|
||||
import de.blight.game.state.HotbarState;
|
||||
import de.blight.game.state.HudState;
|
||||
import de.blight.game.state.LoadingScreenState;
|
||||
import de.blight.game.state.SplashScreenState;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.nio.IntBuffer;
|
||||
@@ -56,6 +57,7 @@ public class BlightGame extends SimpleApplication {
|
||||
private PauseMenu pauseMenu;
|
||||
private MainMenuState mainMenuState;
|
||||
private LoadingScreenState loadingScreen;
|
||||
private SplashScreenState splashScreen;
|
||||
|
||||
/** Routed durch onClose-Callbacks, damit Config/Grafik-Screen zurück zum richtigen Menü springen. */
|
||||
private Runnable screenCloseTarget;
|
||||
@@ -445,7 +447,8 @@ public class BlightGame extends SimpleApplication {
|
||||
if (autostart) {
|
||||
startWorld();
|
||||
} else {
|
||||
showMainMenu();
|
||||
splashScreen = new SplashScreenState(this::showMainMenu);
|
||||
stateManager.attach(splashScreen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,10 +189,10 @@ public class WorldScene extends BaseAppState {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Liefert die Wassertiefe an (worldX, worldZ) in Metern; 0 wenn an Land. */
|
||||
/** Liefert die Wassertiefe des Spielers in Metern; 0 wenn über Y=0. */
|
||||
public float queryWaterDepth(float worldX, float worldZ) {
|
||||
if (terrainChunkState == null) return 0f;
|
||||
return Math.max(0f, -terrainChunkState.getHeightAt(worldX, worldZ));
|
||||
if (physicsChar == null) return 0f;
|
||||
return Math.max(0f, -physicsChar.getPhysicsLocation().y);
|
||||
}
|
||||
|
||||
/** Wird von ConfigScreen nach dem Speichern aufgerufen. */
|
||||
|
||||
@@ -130,7 +130,7 @@ public class DrownState extends BaseAppState {
|
||||
private void updateMonitoring() {
|
||||
if (physicsChar == null) return;
|
||||
Vector3f pos = physicsChar.getPhysicsLocation();
|
||||
float depth = Math.max(0f, -terrain.getHeightAt(pos.x, pos.z));
|
||||
float depth = Math.max(0f, -pos.y);
|
||||
if (depth >= TRIGGER_DEPTH) {
|
||||
log.info("[DrownState] Tiefe {}m – Ertrink-Sequenz gestartet", depth);
|
||||
playerInput.blockInputsKeepMoving();
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
package de.blight.game.state;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.app.state.BaseAppState;
|
||||
import com.jme3.input.RawInputListener;
|
||||
import com.jme3.input.event.*;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.shape.Quad;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.ui.Picture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SplashScreenState extends BaseAppState {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SplashScreenState.class);
|
||||
|
||||
private static final float FADE_DURATION = 1f;
|
||||
private static final float HOLD_DURATION = 5f;
|
||||
private static final float BLACK_DURATION = 1f;
|
||||
private static final String JME_LOGO_PATH = "jme_logo.png";
|
||||
|
||||
private enum Phase { BLACK, FADE_IN, HOLD, FADE_OUT }
|
||||
|
||||
private final Runnable onComplete;
|
||||
|
||||
private SimpleApplication app;
|
||||
private Node overlayNode;
|
||||
private Geometry fadeOverlay;
|
||||
|
||||
private int currentLogo = 0;
|
||||
private Phase phase = Phase.BLACK;
|
||||
private float timer = 0f;
|
||||
private boolean skipRequested = false;
|
||||
|
||||
private final RawInputListener skipListener = new RawInputListener() {
|
||||
@Override public void onKeyEvent(KeyInputEvent evt) { if (evt.isPressed()) skipRequested = true; }
|
||||
@Override public void onMouseButtonEvent(MouseButtonEvent evt) { if (evt.isPressed()) skipRequested = true; }
|
||||
@Override public void beginInput() {}
|
||||
@Override public void endInput() {}
|
||||
@Override public void onMouseMotionEvent(MouseMotionEvent evt) {}
|
||||
@Override public void onTouchEvent(TouchEvent evt) {}
|
||||
@Override public void onJoyAxisEvent(JoyAxisEvent evt) {}
|
||||
@Override public void onJoyButtonEvent(JoyButtonEvent evt) {}
|
||||
};
|
||||
|
||||
public SplashScreenState(Runnable onComplete) {
|
||||
this.onComplete = onComplete;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(Application application) {
|
||||
app = (SimpleApplication) application;
|
||||
}
|
||||
|
||||
@Override protected void cleanup(Application application) {}
|
||||
|
||||
@Override
|
||||
protected void onEnable() {
|
||||
currentLogo = 0;
|
||||
phase = Phase.BLACK;
|
||||
timer = 0f;
|
||||
skipRequested = false;
|
||||
app.getInputManager().addRawInputListener(skipListener);
|
||||
buildOverlay(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDisable() {
|
||||
app.getInputManager().removeRawInputListener(skipListener);
|
||||
clearOverlay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (fadeOverlay == null) return;
|
||||
timer += tpf;
|
||||
|
||||
if (skipRequested) {
|
||||
skipRequested = false;
|
||||
if (phase == Phase.BLACK || phase == Phase.FADE_IN || phase == Phase.HOLD) {
|
||||
phase = Phase.FADE_OUT;
|
||||
timer = 0f;
|
||||
} else {
|
||||
advanceLogo();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (phase) {
|
||||
case BLACK -> {
|
||||
setFadeAlpha(1f);
|
||||
if (timer >= BLACK_DURATION) { phase = Phase.FADE_IN; timer = 0f; }
|
||||
}
|
||||
case FADE_IN -> {
|
||||
setFadeAlpha(1f - Math.min(timer / FADE_DURATION, 1f));
|
||||
if (timer >= FADE_DURATION) { phase = Phase.HOLD; timer = 0f; }
|
||||
}
|
||||
case HOLD -> {
|
||||
setFadeAlpha(0f);
|
||||
if (timer >= HOLD_DURATION) { phase = Phase.FADE_OUT; timer = 0f; }
|
||||
}
|
||||
case FADE_OUT -> {
|
||||
setFadeAlpha(Math.min(timer / FADE_DURATION, 1f));
|
||||
if (timer >= FADE_DURATION) { advanceLogo(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceLogo() {
|
||||
currentLogo++;
|
||||
if (currentLogo >= 2) {
|
||||
setEnabled(false);
|
||||
onComplete.run();
|
||||
} else {
|
||||
clearOverlay();
|
||||
buildOverlay(currentLogo);
|
||||
phase = Phase.FADE_IN;
|
||||
timer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void clearOverlay() {
|
||||
if (overlayNode != null) {
|
||||
app.getGuiNode().detachChild(overlayNode);
|
||||
overlayNode = null;
|
||||
fadeOverlay = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void buildOverlay(int logoIndex) {
|
||||
float sw = app.getCamera().getWidth();
|
||||
float sh = app.getCamera().getHeight();
|
||||
overlayNode = new Node("splash_overlay");
|
||||
|
||||
overlayNode.attachChild(makeRect(sw, sh, ColorRGBA.Black, 300f));
|
||||
|
||||
if (logoIndex == 0) {
|
||||
buildOaaContent(sw, sh);
|
||||
} else {
|
||||
buildJmeContent(sw, sh);
|
||||
}
|
||||
|
||||
// Black fade-overlay on top: alpha 1 = content hidden, alpha 0 = content fully visible
|
||||
fadeOverlay = makeRect(sw, sh, new ColorRGBA(0f, 0f, 0f, 1f), 305f);
|
||||
overlayNode.attachChild(fadeOverlay);
|
||||
|
||||
app.getGuiNode().attachChild(overlayNode);
|
||||
}
|
||||
|
||||
private void buildOaaContent(float sw, float sh) {
|
||||
overlayNode.attachChild(makeRect(sw, sh, ColorRGBA.White, 301f));
|
||||
try {
|
||||
Texture2D tex = (Texture2D) app.getAssetManager().loadTexture("logo_oaa.png");
|
||||
int imgW = tex.getImage().getWidth();
|
||||
int imgH = tex.getImage().getHeight();
|
||||
float logW = sw * 0.45f;
|
||||
float logH = (imgW > 0) ? logW * imgH / imgW : logW * 0.5f;
|
||||
Picture logo = new Picture("oaa_logo");
|
||||
logo.setTexture(app.getAssetManager(), tex, true);
|
||||
logo.setWidth(logW);
|
||||
logo.setHeight(logH);
|
||||
logo.setLocalTranslation((sw - logW) / 2f, (sh - logH) / 2f, 302f);
|
||||
logo.setQueueBucket(RenderQueue.Bucket.Gui);
|
||||
overlayNode.attachChild(logo);
|
||||
} catch (Exception e) {
|
||||
log.warn("[SplashScreen] OAA-Logo nicht ladbar: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void buildJmeContent(float sw, float sh) {
|
||||
overlayNode.attachChild(makeRect(sw, sh, ColorRGBA.White, 301f));
|
||||
try {
|
||||
Texture2D tex = (Texture2D) app.getAssetManager().loadTexture(JME_LOGO_PATH);
|
||||
int imgW = tex.getImage().getWidth();
|
||||
int imgH = tex.getImage().getHeight();
|
||||
float logW = sw * 0.45f;
|
||||
float logH = (imgW > 0) ? logW * imgH / imgW : logW * 0.5f;
|
||||
Picture logo = new Picture("jme_logo");
|
||||
logo.setTexture(app.getAssetManager(), tex, true);
|
||||
logo.setWidth(logW);
|
||||
logo.setHeight(logH);
|
||||
logo.setLocalTranslation((sw - logW) / 2f, (sh - logH) / 2f, 302f);
|
||||
logo.setQueueBucket(RenderQueue.Bucket.Gui);
|
||||
overlayNode.attachChild(logo);
|
||||
} catch (Exception e) {
|
||||
log.warn("[SplashScreen] JME-Logo nicht ladbar: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void setFadeAlpha(float alpha) {
|
||||
fadeOverlay.getMaterial().setColor("Color", new ColorRGBA(0f, 0f, 0f, alpha));
|
||||
}
|
||||
|
||||
private Geometry makeRect(float w, float h, ColorRGBA color, float z) {
|
||||
Geometry g = new Geometry("splash_rect", new Quad(w, h));
|
||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
mat.setColor("Color", color);
|
||||
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
|
||||
g.setMaterial(mat);
|
||||
g.setQueueBucket(RenderQueue.Bucket.Gui);
|
||||
g.setLocalTranslation(0f, 0f, z);
|
||||
return g;
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,9 @@ public class TerrainChunkState extends BaseAppState {
|
||||
|
||||
// ── Konstanten ────────────────────────────────────────────────────────────
|
||||
|
||||
private static final int N = ChunkTerrainIO.CHUNKS_PER_AXIS; // 32
|
||||
private static final int TOTAL = ChunkTerrainIO.CHUNK_COUNT; // 1024
|
||||
private static final int N = ChunkTerrainIO.CHUNKS_PER_AXIS; // 16
|
||||
private static final int TOTAL = ChunkTerrainIO.CHUNK_COUNT; // 256
|
||||
private static final float WORLD_HALF = ChunkTerrainIO.WORLD_SIZE / 2f; // 1024
|
||||
|
||||
// ── Eingabe ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -261,8 +262,8 @@ public class TerrainChunkState extends BaseAppState {
|
||||
float[] h = chunkHeights[ChunkTerrainIO.chunkIndex(cx, cz)];
|
||||
if (h == null) return 1f;
|
||||
|
||||
float originX = -2048f + cx * ChunkTerrainIO.CHUNK_SIZE;
|
||||
float originZ = -2048f + cz * ChunkTerrainIO.CHUNK_SIZE;
|
||||
float originX = -WORLD_HALF + cx * ChunkTerrainIO.CHUNK_SIZE;
|
||||
float originZ = -WORLD_HALF + cz * ChunkTerrainIO.CHUNK_SIZE;
|
||||
float fx = Math.max(0, Math.min(ChunkTerrainIO.CHUNK_SIZE, worldX - originX));
|
||||
float fz = Math.max(0, Math.min(ChunkTerrainIO.CHUNK_SIZE, worldZ - originZ));
|
||||
|
||||
@@ -296,8 +297,8 @@ public class TerrainChunkState extends BaseAppState {
|
||||
|
||||
// Knoten anlegen oder altes Mesh entfernen
|
||||
if (chunkNodes[ci] == null) {
|
||||
float nx = -2048f + cx * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float nz = -2048f + cz * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float nx = -WORLD_HALF + cx * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float nz = -WORLD_HALF + cz * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
Node node = new Node("tn_" + cx + "_" + cz);
|
||||
node.setLocalTranslation(nx, 0f, nz);
|
||||
chunkNodes[ci] = node;
|
||||
@@ -346,8 +347,8 @@ public class TerrainChunkState extends BaseAppState {
|
||||
}
|
||||
|
||||
private static Mesh buildMesh(int cx, int cz, float[] lodH, int verts, float spacing) {
|
||||
float chunkCX = -2048f + cx * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float chunkCZ = -2048f + cz * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float chunkCX = -WORLD_HALF + cx * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float chunkCZ = -WORLD_HALF + cz * ChunkTerrainIO.CHUNK_SIZE + 64f;
|
||||
float half = (verts - 1) * spacing * 0.5f; // immer 64 m
|
||||
|
||||
int vertCount = verts * verts;
|
||||
@@ -392,12 +393,13 @@ public class TerrainChunkState extends BaseAppState {
|
||||
int gi = vi * 4;
|
||||
tangents[gi] = tx; tangents[gi+1] = ty; tangents[gi+2] = tz; tangents[gi+3] = 1f;
|
||||
|
||||
// Welt-Raum UV für Splat-Map: JME3-TerrainQuad-Konvention V=0 → worldZ=+2048
|
||||
// Welt-Raum UV für Splat-Map: V=0 → worldZ=+WORLD_HALF
|
||||
float worldX = chunkCX + lx;
|
||||
float worldZ = chunkCZ + lz;
|
||||
float worldSize = (float) ChunkTerrainIO.WORLD_SIZE;
|
||||
int ti = vi * 2;
|
||||
texCoords[ti] = (worldX + 2048f) / 4096f;
|
||||
texCoords[ti+1] = 1f - (worldZ + 2048f) / 4096f;
|
||||
texCoords[ti] = (worldX + WORLD_HALF) / worldSize;
|
||||
texCoords[ti+1] = 1f - (worldZ + WORLD_HALF) / worldSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +472,7 @@ public class TerrainChunkState extends BaseAppState {
|
||||
// ── Hilfsmethoden ────────────────────────────────────────────────────────
|
||||
|
||||
private static int worldToChunk(float world) {
|
||||
return Math.max(0, Math.min(N - 1, (int) ((world + 2048f) / ChunkTerrainIO.CHUNK_SIZE)));
|
||||
return Math.max(0, Math.min(N - 1, (int) ((world + WORLD_HALF) / ChunkTerrainIO.CHUNK_SIZE)));
|
||||
}
|
||||
|
||||
private static float[] flatChunk() {
|
||||
|
||||
BIN
blight-game/src/main/resources/jme_logo.png
Normal file
BIN
blight-game/src/main/resources/jme_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 288 KiB |
BIN
blight-game/src/main/resources/logo_oaa.png
Normal file
BIN
blight-game/src/main/resources/logo_oaa.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 336 KiB |
Reference in New Issue
Block a user