refactor menu logic
This commit is contained in:
@@ -14,10 +14,7 @@ import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
|
|||||||
import com.badlogic.gdx.graphics.g3d.environment.DirectionalShadowLight;
|
import com.badlogic.gdx.graphics.g3d.environment.DirectionalShadowLight;
|
||||||
import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider;
|
import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider;
|
||||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
||||||
import wtf.beatrice.retrorender.engine.DebugHud;
|
import wtf.beatrice.retrorender.engine.*;
|
||||||
import wtf.beatrice.retrorender.engine.FpsCameraController;
|
|
||||||
import wtf.beatrice.retrorender.engine.PauseMenu;
|
|
||||||
import wtf.beatrice.retrorender.engine.World3D;
|
|
||||||
|
|
||||||
public class GameScreen implements Screen {
|
public class GameScreen implements Screen {
|
||||||
|
|
||||||
@@ -32,9 +29,11 @@ public class GameScreen implements Screen {
|
|||||||
private DebugHud hud;
|
private DebugHud hud;
|
||||||
|
|
||||||
private boolean showHud = false;
|
private boolean showHud = false;
|
||||||
private boolean paused = false;
|
|
||||||
|
|
||||||
private PauseMenu pauseMenu;
|
// menus
|
||||||
|
private GameSettings settings;
|
||||||
|
private GameUi gameUi;
|
||||||
|
private UiMode uiMode = UiMode.GAMEPLAY;
|
||||||
|
|
||||||
// Shadow Mapping
|
// Shadow Mapping
|
||||||
private ModelBatch shadowBatch;
|
private ModelBatch shadowBatch;
|
||||||
@@ -53,14 +52,10 @@ public class GameScreen implements Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initCamera() {
|
private void initCamera() {
|
||||||
camera = new PerspectiveCamera(
|
camera = new PerspectiveCamera(67f, RETRO_WIDTH, RETRO_HEIGHT);
|
||||||
67f,
|
settings = new GameSettings();
|
||||||
RETRO_WIDTH,
|
settings.fov = camera.fieldOfView;
|
||||||
RETRO_HEIGHT
|
cameraController = new FpsCameraController(camera, settings);
|
||||||
);
|
|
||||||
|
|
||||||
// near/far + initial direction handled in controller constructor
|
|
||||||
cameraController = new FpsCameraController(camera);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initEnvironment() {
|
private void initEnvironment() {
|
||||||
@@ -75,12 +70,12 @@ public class GameScreen implements Screen {
|
|||||||
// shadow-casting directional light
|
// shadow-casting directional light
|
||||||
shadowLight = new DirectionalShadowLight(
|
shadowLight = new DirectionalShadowLight(
|
||||||
1024, 1024, // shadow map resolution
|
1024, 1024, // shadow map resolution
|
||||||
60f, 60f, // viewport size
|
60f, 60f, // viewport size
|
||||||
1f, 50f // near/far for the light camera
|
1f, 50f // near/far for the light camera
|
||||||
);
|
);
|
||||||
shadowLight.set(
|
shadowLight.set(
|
||||||
1.0f, 0.85f, 0.9f, // casting light color
|
1.0f, 0.85f, 0.9f, // light color
|
||||||
-0.7f, -1.0f, -0.3f // direction
|
-0.7f, -1.0f, -0.3f // direction
|
||||||
);
|
);
|
||||||
|
|
||||||
environment.add(shadowLight);
|
environment.add(shadowLight);
|
||||||
@@ -95,12 +90,10 @@ public class GameScreen implements Screen {
|
|||||||
|
|
||||||
private void initMenus() {
|
private void initMenus() {
|
||||||
hud = new DebugHud();
|
hud = new DebugHud();
|
||||||
pauseMenu = new PauseMenu();
|
gameUi = new GameUi(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- screen methods
|
|
||||||
private void initRetroBuffer() {
|
private void initRetroBuffer() {
|
||||||
// RGB + depth
|
|
||||||
frameBuffer = new FrameBuffer(
|
frameBuffer = new FrameBuffer(
|
||||||
com.badlogic.gdx.graphics.Pixmap.Format.RGBA8888,
|
com.badlogic.gdx.graphics.Pixmap.Format.RGBA8888,
|
||||||
RETRO_WIDTH,
|
RETRO_WIDTH,
|
||||||
@@ -111,10 +104,9 @@ public class GameScreen implements Screen {
|
|||||||
Texture fbTex = frameBuffer.getColorBufferTexture();
|
Texture fbTex = frameBuffer.getColorBufferTexture();
|
||||||
frameRegion = new TextureRegion(fbTex);
|
frameRegion = new TextureRegion(fbTex);
|
||||||
fbTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
fbTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
||||||
// FBO textures are Y-flipped in libGDX, so flip once here
|
|
||||||
frameRegion.flip(false, true);
|
frameRegion.flip(false, true);
|
||||||
|
|
||||||
screenBatch = new com.badlogic.gdx.graphics.g2d.SpriteBatch();
|
screenBatch = new SpriteBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,24 +126,25 @@ public class GameScreen implements Screen {
|
|||||||
|
|
||||||
// ESC: toggle pause and mouse capture
|
// ESC: toggle pause and mouse capture
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) {
|
if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) {
|
||||||
paused = !paused;
|
UiMode newMode = gameUi.onEsc(uiMode);
|
||||||
if (paused) {
|
boolean goingToGameplay = (newMode == UiMode.GAMEPLAY);
|
||||||
cameraController.releaseMouse();
|
uiMode = newMode;
|
||||||
} else {
|
|
||||||
|
if (uiMode == UiMode.GAMEPLAY) {
|
||||||
cameraController.captureMouse();
|
cameraController.captureMouse();
|
||||||
|
} else {
|
||||||
|
cameraController.releaseMouse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!paused) {
|
boolean gameplay = (uiMode == UiMode.GAMEPLAY);
|
||||||
|
|
||||||
|
if (gameplay) {
|
||||||
cameraController.update(delta, world);
|
cameraController.update(delta, world);
|
||||||
world.update(delta);
|
world.update(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.TAB)) {
|
// --- shadow pass ---
|
||||||
showHud = !showHud;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- shadow pass: render depth from light's point of view
|
|
||||||
shadowLight.begin(new com.badlogic.gdx.math.Vector3(0f, 0f, 0f), camera.direction);
|
shadowLight.begin(new com.badlogic.gdx.math.Vector3(0f, 0f, 0f), camera.direction);
|
||||||
|
|
||||||
shadowBatch.begin(shadowLight.getCamera());
|
shadowBatch.begin(shadowLight.getCamera());
|
||||||
@@ -160,7 +153,7 @@ public class GameScreen implements Screen {
|
|||||||
|
|
||||||
shadowLight.end();
|
shadowLight.end();
|
||||||
|
|
||||||
// --- render scene into low res framebuffer
|
// --- render scene into low res framebuffer ---
|
||||||
frameBuffer.begin();
|
frameBuffer.begin();
|
||||||
Gdx.gl.glViewport(0, 0, RETRO_WIDTH, RETRO_HEIGHT);
|
Gdx.gl.glViewport(0, 0, RETRO_WIDTH, RETRO_HEIGHT);
|
||||||
Gdx.gl.glClearColor(0.5f, 0.6f, 1.0f, 1f);
|
Gdx.gl.glClearColor(0.5f, 0.6f, 1.0f, 1f);
|
||||||
@@ -168,85 +161,89 @@ public class GameScreen implements Screen {
|
|||||||
|
|
||||||
camera.update();
|
camera.update();
|
||||||
modelBatch.begin(camera);
|
modelBatch.begin(camera);
|
||||||
world.render(modelBatch, environment); // DefaultShader uses shadowLight + env.shadowMap
|
world.render(modelBatch, environment);
|
||||||
modelBatch.end();
|
modelBatch.end();
|
||||||
|
|
||||||
// --- HUD
|
// HUD
|
||||||
if (showHud)
|
if (Gdx.input.isKeyJustPressed(Input.Keys.TAB)) {
|
||||||
|
showHud = !showHud;
|
||||||
|
}
|
||||||
|
if (showHud) {
|
||||||
hud.render(
|
hud.render(
|
||||||
RETRO_WIDTH,
|
RETRO_WIDTH,
|
||||||
RETRO_HEIGHT,
|
RETRO_HEIGHT,
|
||||||
camera,
|
camera,
|
||||||
cameraController.getYaw(),
|
cameraController.getYaw(),
|
||||||
cameraController.getPitch());
|
cameraController.getPitch()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// pause menu
|
// pause UI
|
||||||
if (paused && pauseMenu != null) {
|
if (uiMode != UiMode.GAMEPLAY) {
|
||||||
pauseMenu.render(RETRO_WIDTH, RETRO_HEIGHT);
|
gameUi.render(RETRO_WIDTH, RETRO_HEIGHT, uiMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
frameBuffer.end();
|
frameBuffer.end();
|
||||||
|
|
||||||
// -- scale framebuffer to screen, preserve aspect ratio --
|
// -- scale framebuffer to screen, preserve aspect ratio --
|
||||||
int windowW = Gdx.graphics.getWidth(); // logical size
|
int windowW = Gdx.graphics.getWidth();
|
||||||
int windowH = Gdx.graphics.getHeight();
|
int windowH = Gdx.graphics.getHeight();
|
||||||
|
int backW = Gdx.graphics.getBackBufferWidth();
|
||||||
|
int backH = Gdx.graphics.getBackBufferHeight();
|
||||||
|
|
||||||
int backW = Gdx.graphics.getBackBufferWidth(); // actual GL framebuffer
|
RetroViewportHelper.RetroViewport vp =
|
||||||
int backH = Gdx.graphics.getBackBufferHeight();
|
RetroViewportHelper.computeViewport(windowW, windowH, RETRO_WIDTH, RETRO_HEIGHT);
|
||||||
|
|
||||||
// compute how much we can scale the FBO into the window
|
// click → UI
|
||||||
float scale = Math.min(
|
if (uiMode != UiMode.GAMEPLAY && Gdx.input.justTouched()) {
|
||||||
windowW / (float) RETRO_WIDTH,
|
|
||||||
windowH / (float) RETRO_HEIGHT
|
|
||||||
);
|
|
||||||
|
|
||||||
int drawW = Math.round(RETRO_WIDTH * scale);
|
|
||||||
int drawH = Math.round(RETRO_HEIGHT * scale);
|
|
||||||
|
|
||||||
// center the image
|
|
||||||
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 mouseX = Gdx.input.getX();
|
||||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY(); // to bottom-left origin
|
int mouseY = Gdx.input.getY();
|
||||||
|
|
||||||
// is the click inside the scaled game area?
|
RetroViewportHelper.RetroClick rc =
|
||||||
if (mouseX >= offsetX && mouseX <= offsetX + drawW &&
|
RetroViewportHelper.toRetroCoords(
|
||||||
mouseY >= offsetY && mouseY <= offsetY + drawH) {
|
vp,
|
||||||
|
RETRO_WIDTH, RETRO_HEIGHT,
|
||||||
|
mouseX, mouseY,
|
||||||
|
windowH
|
||||||
|
);
|
||||||
|
|
||||||
float relX = (mouseX - offsetX) / (float) drawW;
|
if (rc.inside) {
|
||||||
float relY = (mouseY - offsetY) / (float) drawH;
|
GameUi.UiResult result = gameUi.handleClick(rc.x, rc.y, uiMode);
|
||||||
|
|
||||||
float retroX = relX * RETRO_WIDTH;
|
switch (result) {
|
||||||
float retroY = relY * RETRO_HEIGHT;
|
|
||||||
|
|
||||||
PauseMenu.MenuAction action = pauseMenu.getActionAt(retroX, retroY);
|
|
||||||
switch (action) {
|
|
||||||
case RESUME:
|
case RESUME:
|
||||||
paused = false;
|
uiMode = UiMode.GAMEPLAY;
|
||||||
cameraController.onShow();
|
cameraController.captureMouse();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPEN_SETTINGS:
|
||||||
|
uiMode = UiMode.SETTINGS;
|
||||||
|
// mouse already released when we entered PAUSE, so nothing to do
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CLOSE_SETTINGS:
|
||||||
|
uiMode = UiMode.PAUSE;
|
||||||
|
// stay in pause, mouse remains free
|
||||||
|
break;
|
||||||
|
|
||||||
case QUIT:
|
case QUIT:
|
||||||
Gdx.app.exit();
|
Gdx.app.exit();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NONE:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// viewport in backbuffer coordinates
|
// viewport in backbuffer coordinates
|
||||||
|
// final draw using vp.offsetX / vp.offsetY / vp.drawW / vp.drawH
|
||||||
Gdx.gl.glViewport(0, 0, backW, backH);
|
Gdx.gl.glViewport(0, 0, backW, backH);
|
||||||
Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
|
|
||||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
// projection in window (logical) coordinates
|
|
||||||
screenBatch.getProjectionMatrix().setToOrtho2D(0, 0, windowW, windowH);
|
screenBatch.getProjectionMatrix().setToOrtho2D(0, 0, windowW, windowH);
|
||||||
|
|
||||||
screenBatch.begin();
|
screenBatch.begin();
|
||||||
screenBatch.draw(frameRegion, offsetX, offsetY, drawW, drawH);
|
screenBatch.draw(frameRegion, vp.offsetX, vp.offsetY, vp.drawW, vp.drawH);
|
||||||
screenBatch.end();
|
screenBatch.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,10 +270,9 @@ public class GameScreen implements Screen {
|
|||||||
world.dispose();
|
world.dispose();
|
||||||
modelBatch.dispose();
|
modelBatch.dispose();
|
||||||
hud.dispose();
|
hud.dispose();
|
||||||
|
|
||||||
shadowBatch.dispose();
|
shadowBatch.dispose();
|
||||||
shadowLight.dispose();
|
shadowLight.dispose();
|
||||||
if (pauseMenu != null) pauseMenu.dispose();
|
if (gameUi != null) gameUi.dispose();
|
||||||
if (frameBuffer != null) frameBuffer.dispose();
|
if (frameBuffer != null) frameBuffer.dispose();
|
||||||
if (screenBatch != null) screenBatch.dispose();
|
if (screenBatch != null) screenBatch.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import com.badlogic.gdx.math.Vector3;
|
|||||||
public class FpsCameraController {
|
public class FpsCameraController {
|
||||||
|
|
||||||
private final PerspectiveCamera camera;
|
private final PerspectiveCamera camera;
|
||||||
|
private final GameSettings settings;
|
||||||
|
|
||||||
// movement + look
|
// movement + look
|
||||||
private float moveSpeed = 5f; // units / second
|
|
||||||
private float mouseSensitivity = 0.15f;
|
|
||||||
private float yaw = 0f;
|
private float yaw = 0f;
|
||||||
private float pitch = 0f;
|
private float pitch = 0f;
|
||||||
|
|
||||||
@@ -45,10 +44,10 @@ public class FpsCameraController {
|
|||||||
|
|
||||||
private Cursor invisibleCursor;
|
private Cursor invisibleCursor;
|
||||||
|
|
||||||
public FpsCameraController(PerspectiveCamera camera) {
|
public FpsCameraController(PerspectiveCamera camera, GameSettings settings) {
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
|
this.settings = settings;
|
||||||
|
|
||||||
// default camera setup
|
|
||||||
camera.position.set(0f, eyeHeight, 6f);
|
camera.position.set(0f, eyeHeight, 6f);
|
||||||
camera.near = 0.1f;
|
camera.near = 0.1f;
|
||||||
camera.far = 100f;
|
camera.far = 100f;
|
||||||
@@ -128,8 +127,8 @@ public class FpsCameraController {
|
|||||||
// recenter for next frame
|
// recenter for next frame
|
||||||
Gdx.input.setCursorPosition(centerX, centerY);
|
Gdx.input.setCursorPosition(centerX, centerY);
|
||||||
|
|
||||||
float deltaX = -dx * mouseSensitivity;
|
float deltaX = -dx * settings.mouseSensitivity;
|
||||||
float deltaY = -dy * mouseSensitivity;
|
float deltaY = -dy * settings.mouseSensitivity;
|
||||||
|
|
||||||
yaw += deltaX;
|
yaw += deltaX;
|
||||||
pitch += deltaY;
|
pitch += deltaY;
|
||||||
@@ -149,7 +148,9 @@ public class FpsCameraController {
|
|||||||
// right vector (perpendicular)
|
// right vector (perpendicular)
|
||||||
tmpRight.set(tmpForward.z, 0f, -tmpForward.x).nor();
|
tmpRight.set(tmpForward.z, 0f, -tmpForward.x).nor();
|
||||||
|
|
||||||
float moveAmount = moveSpeed * delta;
|
camera.fieldOfView = settings.fov;
|
||||||
|
|
||||||
|
float moveAmount = settings.moveSpeed * delta;
|
||||||
float moveX = 0f;
|
float moveX = 0f;
|
||||||
float moveZ = 0f;
|
float moveZ = 0f;
|
||||||
|
|
||||||
@@ -260,7 +261,4 @@ public class FpsCameraController {
|
|||||||
|
|
||||||
public float getYaw() { return yaw; }
|
public float getYaw() { return yaw; }
|
||||||
public float getPitch() { return pitch; }
|
public float getPitch() { return pitch; }
|
||||||
|
|
||||||
public void setMoveSpeed(float moveSpeed) { this.moveSpeed = moveSpeed; }
|
|
||||||
public void setMouseSensitivity(float mouseSensitivity) { this.mouseSensitivity = mouseSensitivity; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
public class GameSettings {
|
||||||
|
|
||||||
|
public float fov = 67f; // degrees
|
||||||
|
public float mouseSensitivity = 0.15f;
|
||||||
|
public float moveSpeed = 5f;
|
||||||
|
|
||||||
|
public float minFov = 40f;
|
||||||
|
public float maxFov = 100f;
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
public class GameUi {
|
||||||
|
|
||||||
|
public enum UiResult {
|
||||||
|
NONE,
|
||||||
|
RESUME,
|
||||||
|
OPEN_SETTINGS,
|
||||||
|
CLOSE_SETTINGS,
|
||||||
|
QUIT
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PauseMenu pauseMenu;
|
||||||
|
private final SettingsMenu settingsMenu;
|
||||||
|
private final GameSettings settings;
|
||||||
|
|
||||||
|
public GameUi(GameSettings settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.pauseMenu = new PauseMenu();
|
||||||
|
this.settingsMenu = new SettingsMenu();
|
||||||
|
|
||||||
|
// sync initial settings
|
||||||
|
this.settingsMenu.setFov(settings.fov);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiMode onEsc(UiMode current) {
|
||||||
|
switch (current) {
|
||||||
|
case GAMEPLAY:
|
||||||
|
return UiMode.PAUSE;
|
||||||
|
case PAUSE:
|
||||||
|
case SETTINGS:
|
||||||
|
return UiMode.GAMEPLAY;
|
||||||
|
default:
|
||||||
|
return UiMode.GAMEPLAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(int retroW, int retroH, UiMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case PAUSE:
|
||||||
|
pauseMenu.render(retroW, retroH);
|
||||||
|
break;
|
||||||
|
case SETTINGS:
|
||||||
|
settingsMenu.render(retroW, retroH);
|
||||||
|
break;
|
||||||
|
case GAMEPLAY:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiResult handleClick(float retroX, float retroY, UiMode mode) {
|
||||||
|
if (mode == UiMode.SETTINGS) {
|
||||||
|
boolean close = settingsMenu.handleClick(retroX, retroY);
|
||||||
|
// always sync FOV to settings
|
||||||
|
settings.fov = settingsMenu.getFov();
|
||||||
|
|
||||||
|
if (close) {
|
||||||
|
// Settings “Close” button clicked -> go back to PAUSE
|
||||||
|
return UiResult.CLOSE_SETTINGS;
|
||||||
|
}
|
||||||
|
return UiResult.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == UiMode.PAUSE) {
|
||||||
|
PauseMenu.MenuAction action = pauseMenu.getActionAt(retroX, retroY);
|
||||||
|
return switch (action)
|
||||||
|
{
|
||||||
|
case RESUME -> UiResult.RESUME;
|
||||||
|
case QUIT -> UiResult.QUIT;
|
||||||
|
case SETTINGS -> UiResult.OPEN_SETTINGS;
|
||||||
|
default -> UiResult.NONE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return UiResult.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
pauseMenu.dispose();
|
||||||
|
settingsMenu.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettingsMenu getSettingsMenu() {
|
||||||
|
return settingsMenu;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ public class PauseMenu {
|
|||||||
public enum MenuAction {
|
public enum MenuAction {
|
||||||
NONE,
|
NONE,
|
||||||
RESUME,
|
RESUME,
|
||||||
|
SETTINGS,
|
||||||
QUIT
|
QUIT
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +63,10 @@ public class PauseMenu {
|
|||||||
font = generator.generateFont(param);
|
font = generator.generateFont(param);
|
||||||
generator.dispose();
|
generator.dispose();
|
||||||
|
|
||||||
// create buttons
|
// create buttons (ordered left → right)
|
||||||
buttons.add(new Button(" Resume ", MenuAction.RESUME));
|
buttons.add(new Button(" Resume ", MenuAction.RESUME));
|
||||||
buttons.add(new Button(" Quit ", MenuAction.QUIT));
|
buttons.add(new Button(" Settings ", MenuAction.SETTINGS));
|
||||||
|
buttons.add(new Button(" Quit ", MenuAction.QUIT));
|
||||||
|
|
||||||
// 1x1 pixel texture (white, we tint it)
|
// 1x1 pixel texture (white, we tint it)
|
||||||
Pixmap pm = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
|
Pixmap pm = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
|
||||||
@@ -79,14 +81,14 @@ public class PauseMenu {
|
|||||||
batch.begin();
|
batch.begin();
|
||||||
|
|
||||||
// --- black bar at bottom ---
|
// --- black bar at bottom ---
|
||||||
float barH = 40f; // in retro pixels
|
float barH = 80f; // in retro pixels
|
||||||
float barY = 0f;
|
float barY = 0f;
|
||||||
|
|
||||||
batch.setColor(0f, 0f, 0f, 0.9f);
|
batch.setColor(0f, 0f, 0f, 0.9f);
|
||||||
batch.draw(pixel, 0f, barY, width, barH);
|
batch.draw(pixel, 0f, barY, width, barH);
|
||||||
|
|
||||||
// --- buttons ---
|
// --- buttons ---
|
||||||
float spacing = 8f; // horizontal space between buttons
|
float spacing = 10f; // horizontal space between buttons
|
||||||
|
|
||||||
// measure each button label
|
// measure each button label
|
||||||
GlyphLayout layout = new GlyphLayout();
|
GlyphLayout layout = new GlyphLayout();
|
||||||
@@ -96,7 +98,7 @@ public class PauseMenu {
|
|||||||
for (Button b : buttons) {
|
for (Button b : buttons) {
|
||||||
layout.setText(font, b.label);
|
layout.setText(font, b.label);
|
||||||
float bw = layout.width + 16f; // padding
|
float bw = layout.width + 16f; // padding
|
||||||
float bh = layout.height + 8f;
|
float bh = layout.height + 12f;
|
||||||
b.w = bw;
|
b.w = bw;
|
||||||
b.h = bh;
|
b.h = bh;
|
||||||
totalWidth += bw;
|
totalWidth += bw;
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
public class RetroViewportHelper {
|
||||||
|
|
||||||
|
public static class RetroViewport {
|
||||||
|
public int offsetX, offsetY;
|
||||||
|
public int drawW, drawH;
|
||||||
|
public float scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RetroClick {
|
||||||
|
public boolean inside;
|
||||||
|
public float x, y; // retro coords
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetroViewport computeViewport(
|
||||||
|
int windowW, int windowH,
|
||||||
|
int retroW, int retroH) {
|
||||||
|
|
||||||
|
RetroViewport vp = new RetroViewport();
|
||||||
|
|
||||||
|
float scale = Math.min(
|
||||||
|
windowW / (float) retroW,
|
||||||
|
windowH / (float) retroH
|
||||||
|
);
|
||||||
|
|
||||||
|
vp.scale = scale;
|
||||||
|
vp.drawW = Math.round(retroW * scale);
|
||||||
|
vp.drawH = Math.round(retroH * scale);
|
||||||
|
vp.offsetX = (windowW - vp.drawW) / 2;
|
||||||
|
vp.offsetY = (windowH - vp.drawH) / 2;
|
||||||
|
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetroClick toRetroCoords(
|
||||||
|
RetroViewport vp,
|
||||||
|
int retroW, int retroH,
|
||||||
|
int mouseX, int mouseYWindow,
|
||||||
|
int windowHeight) {
|
||||||
|
|
||||||
|
RetroClick rc = new RetroClick();
|
||||||
|
// libGDX gives mouse from top-left; you already invert Y like this:
|
||||||
|
int yBottomOrigin = windowHeight - mouseYWindow;
|
||||||
|
|
||||||
|
if (mouseX < vp.offsetX || mouseX > vp.offsetX + vp.drawW ||
|
||||||
|
yBottomOrigin < vp.offsetY || yBottomOrigin > vp.offsetY + vp.drawH) {
|
||||||
|
rc.inside = false;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
float relX = (mouseX - vp.offsetX) / (float) vp.drawW;
|
||||||
|
float relY = (yBottomOrigin - vp.offsetY) / (float) vp.drawH;
|
||||||
|
|
||||||
|
rc.inside = true;
|
||||||
|
rc.x = relX * retroW;
|
||||||
|
rc.y = relY * retroH;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
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 com.badlogic.gdx.math.MathUtils;
|
||||||
|
|
||||||
|
public class SettingsMenu {
|
||||||
|
|
||||||
|
private final SpriteBatch batch;
|
||||||
|
private final BitmapFont font;
|
||||||
|
private final Texture pixel;
|
||||||
|
|
||||||
|
// FOV value stored here, GameScreen syncs it to the camera
|
||||||
|
private float fov;
|
||||||
|
private final float minFov = 40f;
|
||||||
|
private final float maxFov = 100f;
|
||||||
|
private final float fovStep = 2f;
|
||||||
|
|
||||||
|
// button / hit areas
|
||||||
|
private float fovDecX, fovDecY, fovDecW, fovDecH;
|
||||||
|
private float fovIncX, fovIncY, fovIncW, fovIncH;
|
||||||
|
private float closeX, closeY, closeW, closeH;
|
||||||
|
|
||||||
|
public SettingsMenu() {
|
||||||
|
batch = new SpriteBatch();
|
||||||
|
|
||||||
|
FreeTypeFontGenerator generator =
|
||||||
|
new FreeTypeFontGenerator(Gdx.files.internal("fonts/red-hat-mono.ttf"));
|
||||||
|
FreeTypeFontGenerator.FreeTypeFontParameter param =
|
||||||
|
new FreeTypeFontGenerator.FreeTypeFontParameter();
|
||||||
|
|
||||||
|
param.size = 12;
|
||||||
|
param.color = Color.WHITE;
|
||||||
|
param.mono = true;
|
||||||
|
param.hinting = FreeTypeFontGenerator.Hinting.None;
|
||||||
|
param.borderWidth = 0.2f;
|
||||||
|
param.borderColor = Color.WHITE;
|
||||||
|
param.shadowOffsetX = 0;
|
||||||
|
param.shadowOffsetY = 0;
|
||||||
|
param.minFilter = Texture.TextureFilter.Nearest;
|
||||||
|
param.magFilter = Texture.TextureFilter.Nearest;
|
||||||
|
|
||||||
|
font = generator.generateFont(param);
|
||||||
|
generator.dispose();
|
||||||
|
|
||||||
|
// 1x1 pixel
|
||||||
|
Pixmap pm = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
|
||||||
|
pm.setColor(1f, 1f, 1f, 1f);
|
||||||
|
pm.fill();
|
||||||
|
pixel = new Texture(pm);
|
||||||
|
pm.dispose();
|
||||||
|
|
||||||
|
// default FOV
|
||||||
|
fov = 67f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFov(float fov) {
|
||||||
|
this.fov = MathUtils.clamp(fov, minFov, maxFov);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFov() {
|
||||||
|
return fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param width RETRO_WIDTH
|
||||||
|
* @param height RETRO_HEIGHT
|
||||||
|
*/
|
||||||
|
public void render(int width, int height) {
|
||||||
|
batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
|
||||||
|
batch.begin();
|
||||||
|
|
||||||
|
GlyphLayout layout = new GlyphLayout();
|
||||||
|
|
||||||
|
// --- central panel ---
|
||||||
|
float panelW = 200f;
|
||||||
|
float panelH = 220f;
|
||||||
|
float panelX = (width - panelW) / 2f;
|
||||||
|
float panelY = (height - panelH) / 2f;
|
||||||
|
|
||||||
|
// background
|
||||||
|
batch.setColor(0f, 0f, 0f, 0.9f);
|
||||||
|
batch.draw(pixel, panelX, panelY, panelW, panelH);
|
||||||
|
|
||||||
|
// border
|
||||||
|
batch.setColor(1f, 1f, 1f, 1f);
|
||||||
|
float border = 1f;
|
||||||
|
|
||||||
|
// --- title ---
|
||||||
|
String title = "Settings";
|
||||||
|
layout.setText(font, title);
|
||||||
|
float titleX = panelX + (panelW - layout.width) / 2f;
|
||||||
|
float titleY = panelY + panelH - 10f;
|
||||||
|
font.draw(batch, layout, titleX, titleY);
|
||||||
|
|
||||||
|
// small separator line under title
|
||||||
|
batch.draw(pixel, panelX + 8f, titleY - 14f, panelW - 16f, 1f);
|
||||||
|
|
||||||
|
// --- FOV row ---
|
||||||
|
float rowY = panelY + panelH - 50f;
|
||||||
|
float rowH = 18f;
|
||||||
|
|
||||||
|
// label
|
||||||
|
String fovLabel = "FOV";
|
||||||
|
layout.setText(font, fovLabel);
|
||||||
|
float labelX = panelX + 12f;
|
||||||
|
float labelY = rowY + rowH - 4f;
|
||||||
|
font.draw(batch, layout, labelX, labelY);
|
||||||
|
|
||||||
|
// buttons + value area
|
||||||
|
fovDecW = 20f;
|
||||||
|
fovDecH = rowH;
|
||||||
|
fovDecX = panelX + 80f;
|
||||||
|
fovDecY = rowY;
|
||||||
|
|
||||||
|
fovIncW = 20f;
|
||||||
|
fovIncH = rowH;
|
||||||
|
fovIncX = panelX + panelW - 32f;
|
||||||
|
fovIncY = rowY;
|
||||||
|
|
||||||
|
// draw dec button "<"
|
||||||
|
drawButtonWithLabel("<", fovDecX, fovDecY, fovDecW, fovDecH);
|
||||||
|
|
||||||
|
// draw inc button ">"
|
||||||
|
drawButtonWithLabel(">", fovIncX, fovIncY, fovIncW, fovIncH);
|
||||||
|
|
||||||
|
// numeric value centered between dec/inc
|
||||||
|
String fovText = String.format("%3.0f°", fov);
|
||||||
|
layout.setText(font, fovText);
|
||||||
|
|
||||||
|
float valueCenter = (fovDecX + fovDecW + fovIncX) / 2f;
|
||||||
|
float valueX = valueCenter - layout.width / 2f;
|
||||||
|
float valueY = rowY + rowH - 4f;
|
||||||
|
font.draw(batch, layout, valueX, valueY);
|
||||||
|
|
||||||
|
// --- Close button at bottom ---
|
||||||
|
String closeLabel = " Close ";
|
||||||
|
layout.setText(font, closeLabel);
|
||||||
|
closeW = layout.width + 16f;
|
||||||
|
closeH = layout.height + 8f;
|
||||||
|
closeX = panelX + (panelW - closeW) / 2f;
|
||||||
|
closeY = panelY + 16f;
|
||||||
|
|
||||||
|
// button bg + border
|
||||||
|
batch.setColor(0.1f, 0.1f, 0.1f, 1f);
|
||||||
|
batch.draw(pixel, closeX, closeY, closeW, closeH);
|
||||||
|
batch.setColor(1f, 1f, 1f, 1f);
|
||||||
|
// border
|
||||||
|
batch.draw(pixel, closeX, closeY + closeH - border, closeW, border);
|
||||||
|
batch.draw(pixel, closeX, closeY, closeW, border);
|
||||||
|
batch.draw(pixel, closeX, closeY, border, closeH);
|
||||||
|
batch.draw(pixel, closeX + closeW - border, closeY, border, closeH);
|
||||||
|
|
||||||
|
float closeTextX = closeX + (closeW - layout.width) / 2f;
|
||||||
|
float closeTextY = closeY + closeH - (closeH - layout.height) / 2f - 2f;
|
||||||
|
font.draw(batch, closeLabel, closeTextX, closeTextY);
|
||||||
|
|
||||||
|
batch.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawButtonWithLabel(String text, float x, float y, float w, float h) {
|
||||||
|
// background
|
||||||
|
batch.setColor(0.1f, 0.1f, 0.1f, 1f);
|
||||||
|
batch.draw(pixel, x, y, w, h);
|
||||||
|
// border
|
||||||
|
batch.setColor(1f, 1f, 1f, 1f);
|
||||||
|
float border = 1f;
|
||||||
|
batch.draw(pixel, x, y + h - border, w, border);
|
||||||
|
batch.draw(pixel, x, y, w, border);
|
||||||
|
batch.draw(pixel, x, y, border, h);
|
||||||
|
batch.draw(pixel, x + w - border, y, border, h);
|
||||||
|
|
||||||
|
GlyphLayout layout = new GlyphLayout(font, text);
|
||||||
|
float tx = x + (w - layout.width) / 2f;
|
||||||
|
float ty = y + h - (h - layout.height) / 2f - 2f;
|
||||||
|
font.draw(batch, layout, tx, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a click in RETRO coords.
|
||||||
|
*
|
||||||
|
* @return true if the menu wants to close (Close button clicked)
|
||||||
|
*/
|
||||||
|
public boolean handleClick(float x, float y) {
|
||||||
|
// FOV -
|
||||||
|
if (x >= fovDecX && x <= fovDecX + fovDecW &&
|
||||||
|
y >= fovDecY && y <= fovDecY + fovDecH) {
|
||||||
|
fov = MathUtils.clamp(fov - fovStep, minFov, maxFov);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOV +
|
||||||
|
if (x >= fovIncX && x <= fovIncX + fovIncW &&
|
||||||
|
y >= fovIncY && y <= fovIncY + fovIncH) {
|
||||||
|
fov = MathUtils.clamp(fov + fovStep, minFov, maxFov);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close
|
||||||
|
if (x >= closeX && x <= closeX + closeW &&
|
||||||
|
y >= closeY && y <= closeY + closeH) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
batch.dispose();
|
||||||
|
font.dispose();
|
||||||
|
pixel.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
public enum UiMode {
|
||||||
|
GAMEPLAY,
|
||||||
|
PAUSE, // only bottom bar
|
||||||
|
SETTINGS // pause bar + center settings
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user