diff --git a/core/src/main/java/wtf/beatrice/retrorender/GameScreen.java b/core/src/main/java/wtf/beatrice/retrorender/GameScreen.java index 1fc7194..0157f58 100644 --- a/core/src/main/java/wtf/beatrice/retrorender/GameScreen.java +++ b/core/src/main/java/wtf/beatrice/retrorender/GameScreen.java @@ -16,6 +16,7 @@ import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider; import com.badlogic.gdx.graphics.glutils.FrameBuffer; import wtf.beatrice.retrorender.engine.DebugHud; import wtf.beatrice.retrorender.engine.FpsCameraController; +import wtf.beatrice.retrorender.engine.PauseMenu; import wtf.beatrice.retrorender.engine.World3D; public class GameScreen implements Screen { @@ -30,6 +31,11 @@ public class GameScreen implements Screen { private World3D world; private DebugHud hud; + private boolean showHud = false; + private boolean paused = false; + + private PauseMenu pauseMenu; + // Shadow Mapping private ModelBatch shadowBatch; private DirectionalShadowLight shadowLight; @@ -42,8 +48,6 @@ public class GameScreen implements Screen { private static final int RETRO_WIDTH = 320; private static final int RETRO_HEIGHT = 240; - private boolean showHud = false; - public GameScreen(Main game) { this.game = game; } @@ -89,8 +93,9 @@ public class GameScreen implements Screen { world = new World3D(); } - private void initHud() { + private void initMenus() { hud = new DebugHud(); + pauseMenu = new PauseMenu(); } // --- screen methods @@ -118,25 +123,35 @@ public class GameScreen implements Screen { initCamera(); initEnvironment(); initWorld(); - initHud(); + initMenus(); initRetroBuffer(); - cameraController.onShow(); + cameraController.onShow(); // captures mouse with warmup } @Override public void render(float delta) { - cameraController.update(delta, world); - world.update(delta); + // ESC: toggle pause and mouse capture + if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) { + paused = !paused; + if (paused) { + cameraController.releaseMouse(); + } else { + cameraController.captureMouse(); + } + } + + if (!paused) { + cameraController.update(delta, world); + world.update(delta); + } if (Gdx.input.isKeyJustPressed(Input.Keys.TAB)) { showHud = !showHud; } // --- shadow pass: render depth from light's point of view - // point to center the shadow camera on; - // can be adjusted this later (e.g. follow player). shadowLight.begin(new com.badlogic.gdx.math.Vector3(0f, 0f, 0f), camera.direction); shadowBatch.begin(shadowLight.getCamera()); @@ -165,6 +180,11 @@ public class GameScreen implements Screen { cameraController.getYaw(), cameraController.getPitch()); + // pause menu + if (paused && pauseMenu != null) { + pauseMenu.render(RETRO_WIDTH, RETRO_HEIGHT); + } + frameBuffer.end(); // -- scale framebuffer to screen, preserve aspect ratio -- @@ -180,9 +200,6 @@ public class GameScreen implements Screen { windowH / (float) RETRO_HEIGHT ); - // strict integer scaling - // scale = (float)Math.max(1, Math.floor(scale)); - int drawW = Math.round(RETRO_WIDTH * scale); int drawH = Math.round(RETRO_HEIGHT * scale); @@ -190,6 +207,36 @@ public class GameScreen implements Screen { int offsetX = (windowW - drawW) / 2; int offsetY = (windowH - drawH) / 2; + // --- handle mouse click on pause button (window -> retro coords) --- + if (paused && pauseMenu != null && Gdx.input.justTouched()) { + int mouseX = Gdx.input.getX(); + int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY(); // to bottom-left origin + + // is the click inside the scaled game area? + if (mouseX >= offsetX && mouseX <= offsetX + drawW && + mouseY >= offsetY && mouseY <= offsetY + drawH) { + + float relX = (mouseX - offsetX) / (float) drawW; + float relY = (mouseY - offsetY) / (float) drawH; + + float retroX = relX * RETRO_WIDTH; + float retroY = relY * RETRO_HEIGHT; + + PauseMenu.MenuAction action = pauseMenu.getActionAt(retroX, retroY); + switch (action) { + case RESUME: + paused = false; + cameraController.onShow(); + break; + case QUIT: + Gdx.app.exit(); + break; + default: + break; + } + } + } + // viewport in backbuffer coordinates Gdx.gl.glViewport(0, 0, backW, backH); Gdx.gl.glClearColor(0f, 0f, 0f, 1f); @@ -229,5 +276,8 @@ public class GameScreen implements Screen { shadowBatch.dispose(); shadowLight.dispose(); + if (pauseMenu != null) pauseMenu.dispose(); + if (frameBuffer != null) frameBuffer.dispose(); + if (screenBatch != null) screenBatch.dispose(); } } diff --git a/core/src/main/java/wtf/beatrice/retrorender/engine/FpsCameraController.java b/core/src/main/java/wtf/beatrice/retrorender/engine/FpsCameraController.java index 6e9c884..9d9a88d 100644 --- a/core/src/main/java/wtf/beatrice/retrorender/engine/FpsCameraController.java +++ b/core/src/main/java/wtf/beatrice/retrorender/engine/FpsCameraController.java @@ -65,6 +65,16 @@ public class FpsCameraController { /** Call from Screen.show() */ public void onShow() { + captureMouse(); + } + + /** Call from Screen.hide() */ + public void onHide() { + releaseMouse(); + } + + /** Explicitly capture mouse (used when resuming from pause). */ + public void captureMouse() { mouseCaptured = true; centerX = Gdx.graphics.getWidth() / 2; centerY = Gdx.graphics.getHeight() / 2; @@ -76,8 +86,8 @@ public class FpsCameraController { captureWarmupFrames = WARMUP_FRAMES; } - /** Call from Screen.hide() */ - public void onHide() { + /** Explicitly release mouse (used when pausing). */ + public void releaseMouse() { mouseCaptured = false; Gdx.graphics.setSystemCursor(Cursor.SystemCursor.Arrow); } @@ -95,23 +105,8 @@ public class FpsCameraController { /** Update each frame with delta time */ public void update(float delta, World3D world) { - // --- ESC: release mouse, stop movement / camera - if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) { - mouseCaptured = false; - Gdx.graphics.setSystemCursor(Cursor.SystemCursor.Arrow); - } - - // only capture inputs if focused/captured + // if we don't own the mouse, just don't move camera / player if (!mouseCaptured) { - if (Gdx.input.justTouched()) { // if clicked - mouseCaptured = true; - centerX = Gdx.graphics.getWidth() / 2; - centerY = Gdx.graphics.getHeight() / 2; - Gdx.input.setCursorPosition(centerX, centerY); - ensureInvisibleCursor(); - Gdx.graphics.setCursor(invisibleCursor); - captureWarmupFrames = WARMUP_FRAMES; - } return; } diff --git a/core/src/main/java/wtf/beatrice/retrorender/engine/PauseMenu.java b/core/src/main/java/wtf/beatrice/retrorender/engine/PauseMenu.java new file mode 100644 index 0000000..66b37d8 --- /dev/null +++ b/core/src/main/java/wtf/beatrice/retrorender/engine/PauseMenu.java @@ -0,0 +1,163 @@ +package wtf.beatrice.retrorender.engine; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.GlyphLayout; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; + +import java.util.ArrayList; +import java.util.List; + +public class PauseMenu { + + private final SpriteBatch batch; + private final BitmapFont font; + private final Texture pixel; + + // simple action enum for the menu + public enum MenuAction { + NONE, + RESUME, + QUIT + } + + // simple button model + private static class Button { + String label; + MenuAction action; + float x, y, w, h; + + Button(String label, MenuAction action) { + this.label = label; + this.action = action; + } + } + + private final List