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.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;
|
||||
import wtf.beatrice.retrorender.engine.*;
|
||||
|
||||
public class GameScreen implements Screen {
|
||||
|
||||
@@ -32,9 +29,11 @@ public class GameScreen implements Screen {
|
||||
private DebugHud hud;
|
||||
|
||||
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
|
||||
private ModelBatch shadowBatch;
|
||||
@@ -53,14 +52,10 @@ public class GameScreen implements Screen {
|
||||
}
|
||||
|
||||
private void initCamera() {
|
||||
camera = new PerspectiveCamera(
|
||||
67f,
|
||||
RETRO_WIDTH,
|
||||
RETRO_HEIGHT
|
||||
);
|
||||
|
||||
// near/far + initial direction handled in controller constructor
|
||||
cameraController = new FpsCameraController(camera);
|
||||
camera = new PerspectiveCamera(67f, RETRO_WIDTH, RETRO_HEIGHT);
|
||||
settings = new GameSettings();
|
||||
settings.fov = camera.fieldOfView;
|
||||
cameraController = new FpsCameraController(camera, settings);
|
||||
}
|
||||
|
||||
private void initEnvironment() {
|
||||
@@ -75,12 +70,12 @@ public class GameScreen implements Screen {
|
||||
// shadow-casting directional light
|
||||
shadowLight = new DirectionalShadowLight(
|
||||
1024, 1024, // shadow map resolution
|
||||
60f, 60f, // viewport size
|
||||
1f, 50f // near/far for the light camera
|
||||
60f, 60f, // viewport size
|
||||
1f, 50f // near/far for the light camera
|
||||
);
|
||||
shadowLight.set(
|
||||
1.0f, 0.85f, 0.9f, // casting light color
|
||||
-0.7f, -1.0f, -0.3f // direction
|
||||
1.0f, 0.85f, 0.9f, // light color
|
||||
-0.7f, -1.0f, -0.3f // direction
|
||||
);
|
||||
|
||||
environment.add(shadowLight);
|
||||
@@ -95,12 +90,10 @@ public class GameScreen implements Screen {
|
||||
|
||||
private void initMenus() {
|
||||
hud = new DebugHud();
|
||||
pauseMenu = new PauseMenu();
|
||||
gameUi = new GameUi(settings);
|
||||
}
|
||||
|
||||
// --- screen methods
|
||||
private void initRetroBuffer() {
|
||||
// RGB + depth
|
||||
frameBuffer = new FrameBuffer(
|
||||
com.badlogic.gdx.graphics.Pixmap.Format.RGBA8888,
|
||||
RETRO_WIDTH,
|
||||
@@ -111,10 +104,9 @@ public class GameScreen implements Screen {
|
||||
Texture fbTex = frameBuffer.getColorBufferTexture();
|
||||
frameRegion = new TextureRegion(fbTex);
|
||||
fbTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
||||
// FBO textures are Y-flipped in libGDX, so flip once here
|
||||
frameRegion.flip(false, true);
|
||||
|
||||
screenBatch = new com.badlogic.gdx.graphics.g2d.SpriteBatch();
|
||||
screenBatch = new SpriteBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,24 +126,25 @@ public class GameScreen implements Screen {
|
||||
|
||||
// ESC: toggle pause and mouse capture
|
||||
if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) {
|
||||
paused = !paused;
|
||||
if (paused) {
|
||||
cameraController.releaseMouse();
|
||||
} else {
|
||||
UiMode newMode = gameUi.onEsc(uiMode);
|
||||
boolean goingToGameplay = (newMode == UiMode.GAMEPLAY);
|
||||
uiMode = newMode;
|
||||
|
||||
if (uiMode == UiMode.GAMEPLAY) {
|
||||
cameraController.captureMouse();
|
||||
} else {
|
||||
cameraController.releaseMouse();
|
||||
}
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
boolean gameplay = (uiMode == UiMode.GAMEPLAY);
|
||||
|
||||
if (gameplay) {
|
||||
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
|
||||
// --- shadow pass ---
|
||||
shadowLight.begin(new com.badlogic.gdx.math.Vector3(0f, 0f, 0f), camera.direction);
|
||||
|
||||
shadowBatch.begin(shadowLight.getCamera());
|
||||
@@ -160,7 +153,7 @@ public class GameScreen implements Screen {
|
||||
|
||||
shadowLight.end();
|
||||
|
||||
// --- render scene into low res framebuffer
|
||||
// --- render scene into low res framebuffer ---
|
||||
frameBuffer.begin();
|
||||
Gdx.gl.glViewport(0, 0, RETRO_WIDTH, RETRO_HEIGHT);
|
||||
Gdx.gl.glClearColor(0.5f, 0.6f, 1.0f, 1f);
|
||||
@@ -168,85 +161,89 @@ public class GameScreen implements Screen {
|
||||
|
||||
camera.update();
|
||||
modelBatch.begin(camera);
|
||||
world.render(modelBatch, environment); // DefaultShader uses shadowLight + env.shadowMap
|
||||
world.render(modelBatch, environment);
|
||||
modelBatch.end();
|
||||
|
||||
// --- HUD
|
||||
if (showHud)
|
||||
// HUD
|
||||
if (Gdx.input.isKeyJustPressed(Input.Keys.TAB)) {
|
||||
showHud = !showHud;
|
||||
}
|
||||
if (showHud) {
|
||||
hud.render(
|
||||
RETRO_WIDTH,
|
||||
RETRO_HEIGHT,
|
||||
camera,
|
||||
cameraController.getYaw(),
|
||||
cameraController.getPitch());
|
||||
cameraController.getPitch()
|
||||
);
|
||||
}
|
||||
|
||||
// pause menu
|
||||
if (paused && pauseMenu != null) {
|
||||
pauseMenu.render(RETRO_WIDTH, RETRO_HEIGHT);
|
||||
// pause UI
|
||||
if (uiMode != UiMode.GAMEPLAY) {
|
||||
gameUi.render(RETRO_WIDTH, RETRO_HEIGHT, uiMode);
|
||||
}
|
||||
|
||||
frameBuffer.end();
|
||||
|
||||
// -- 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 backW = Gdx.graphics.getBackBufferWidth();
|
||||
int backH = Gdx.graphics.getBackBufferHeight();
|
||||
|
||||
int backW = Gdx.graphics.getBackBufferWidth(); // actual GL framebuffer
|
||||
int backH = Gdx.graphics.getBackBufferHeight();
|
||||
RetroViewportHelper.RetroViewport vp =
|
||||
RetroViewportHelper.computeViewport(windowW, windowH, RETRO_WIDTH, RETRO_HEIGHT);
|
||||
|
||||
// compute how much we can scale the FBO into the window
|
||||
float scale = Math.min(
|
||||
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()) {
|
||||
// click → UI
|
||||
if (uiMode != UiMode.GAMEPLAY && Gdx.input.justTouched()) {
|
||||
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?
|
||||
if (mouseX >= offsetX && mouseX <= offsetX + drawW &&
|
||||
mouseY >= offsetY && mouseY <= offsetY + drawH) {
|
||||
RetroViewportHelper.RetroClick rc =
|
||||
RetroViewportHelper.toRetroCoords(
|
||||
vp,
|
||||
RETRO_WIDTH, RETRO_HEIGHT,
|
||||
mouseX, mouseY,
|
||||
windowH
|
||||
);
|
||||
|
||||
float relX = (mouseX - offsetX) / (float) drawW;
|
||||
float relY = (mouseY - offsetY) / (float) drawH;
|
||||
if (rc.inside) {
|
||||
GameUi.UiResult result = gameUi.handleClick(rc.x, rc.y, uiMode);
|
||||
|
||||
float retroX = relX * RETRO_WIDTH;
|
||||
float retroY = relY * RETRO_HEIGHT;
|
||||
|
||||
PauseMenu.MenuAction action = pauseMenu.getActionAt(retroX, retroY);
|
||||
switch (action) {
|
||||
switch (result) {
|
||||
case RESUME:
|
||||
paused = false;
|
||||
cameraController.onShow();
|
||||
uiMode = UiMode.GAMEPLAY;
|
||||
cameraController.captureMouse();
|
||||
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:
|
||||
Gdx.app.exit();
|
||||
break;
|
||||
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// viewport in backbuffer coordinates
|
||||
// final draw using vp.offsetX / vp.offsetY / vp.drawW / vp.drawH
|
||||
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.begin();
|
||||
screenBatch.draw(frameRegion, offsetX, offsetY, drawW, drawH);
|
||||
screenBatch.draw(frameRegion, vp.offsetX, vp.offsetY, vp.drawW, vp.drawH);
|
||||
screenBatch.end();
|
||||
}
|
||||
|
||||
@@ -273,10 +270,9 @@ public class GameScreen implements Screen {
|
||||
world.dispose();
|
||||
modelBatch.dispose();
|
||||
hud.dispose();
|
||||
|
||||
shadowBatch.dispose();
|
||||
shadowLight.dispose();
|
||||
if (pauseMenu != null) pauseMenu.dispose();
|
||||
if (gameUi != null) gameUi.dispose();
|
||||
if (frameBuffer != null) frameBuffer.dispose();
|
||||
if (screenBatch != null) screenBatch.dispose();
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@ import com.badlogic.gdx.math.Vector3;
|
||||
public class FpsCameraController {
|
||||
|
||||
private final PerspectiveCamera camera;
|
||||
private final GameSettings settings;
|
||||
|
||||
// movement + look
|
||||
private float moveSpeed = 5f; // units / second
|
||||
private float mouseSensitivity = 0.15f;
|
||||
private float yaw = 0f;
|
||||
private float pitch = 0f;
|
||||
|
||||
@@ -45,10 +44,10 @@ public class FpsCameraController {
|
||||
|
||||
private Cursor invisibleCursor;
|
||||
|
||||
public FpsCameraController(PerspectiveCamera camera) {
|
||||
public FpsCameraController(PerspectiveCamera camera, GameSettings settings) {
|
||||
this.camera = camera;
|
||||
this.settings = settings;
|
||||
|
||||
// default camera setup
|
||||
camera.position.set(0f, eyeHeight, 6f);
|
||||
camera.near = 0.1f;
|
||||
camera.far = 100f;
|
||||
@@ -128,8 +127,8 @@ public class FpsCameraController {
|
||||
// recenter for next frame
|
||||
Gdx.input.setCursorPosition(centerX, centerY);
|
||||
|
||||
float deltaX = -dx * mouseSensitivity;
|
||||
float deltaY = -dy * mouseSensitivity;
|
||||
float deltaX = -dx * settings.mouseSensitivity;
|
||||
float deltaY = -dy * settings.mouseSensitivity;
|
||||
|
||||
yaw += deltaX;
|
||||
pitch += deltaY;
|
||||
@@ -149,7 +148,9 @@ public class FpsCameraController {
|
||||
// right vector (perpendicular)
|
||||
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 moveZ = 0f;
|
||||
|
||||
@@ -260,7 +261,4 @@ public class FpsCameraController {
|
||||
|
||||
public float getYaw() { return yaw; }
|
||||
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 {
|
||||
NONE,
|
||||
RESUME,
|
||||
SETTINGS,
|
||||
QUIT
|
||||
}
|
||||
|
||||
@@ -62,9 +63,10 @@ public class PauseMenu {
|
||||
font = generator.generateFont(param);
|
||||
generator.dispose();
|
||||
|
||||
// create buttons
|
||||
buttons.add(new Button(" Resume ", MenuAction.RESUME));
|
||||
buttons.add(new Button(" Quit ", MenuAction.QUIT));
|
||||
// create buttons (ordered left → right)
|
||||
buttons.add(new Button(" Resume ", MenuAction.RESUME));
|
||||
buttons.add(new Button(" Settings ", MenuAction.SETTINGS));
|
||||
buttons.add(new Button(" Quit ", MenuAction.QUIT));
|
||||
|
||||
// 1x1 pixel texture (white, we tint it)
|
||||
Pixmap pm = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
|
||||
@@ -79,14 +81,14 @@ public class PauseMenu {
|
||||
batch.begin();
|
||||
|
||||
// --- black bar at bottom ---
|
||||
float barH = 40f; // in retro pixels
|
||||
float barH = 80f; // in retro pixels
|
||||
float barY = 0f;
|
||||
|
||||
batch.setColor(0f, 0f, 0f, 0.9f);
|
||||
batch.draw(pixel, 0f, barY, width, barH);
|
||||
|
||||
// --- buttons ---
|
||||
float spacing = 8f; // horizontal space between buttons
|
||||
float spacing = 10f; // horizontal space between buttons
|
||||
|
||||
// measure each button label
|
||||
GlyphLayout layout = new GlyphLayout();
|
||||
@@ -96,7 +98,7 @@ public class PauseMenu {
|
||||
for (Button b : buttons) {
|
||||
layout.setText(font, b.label);
|
||||
float bw = layout.width + 16f; // padding
|
||||
float bh = layout.height + 8f;
|
||||
float bh = layout.height + 12f;
|
||||
b.w = bw;
|
||||
b.h = bh;
|
||||
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