Compare commits
14 Commits
f4694a1f1e
...
65880853ac
| Author | SHA1 | Date | |
|---|---|---|---|
| 65880853ac | |||
| b1cbef4dd8 | |||
| affa81a3e8 | |||
| 63c81f98c2 | |||
| 381ff306cb | |||
| a4bece69f9 | |||
| 967926c4d2 | |||
| f4d5a351fb | |||
| e7c614c6cd | |||
| b79865d59c | |||
| 0ad13ad368 | |||
| 67100eba23 | |||
| 94b618f66c | |||
| ac37bb43eb |
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
val libgdxVersion = "1.13.5"
|
val libgdxVersion = "1.13.5"
|
||||||
|
val gdxVideoVersion = "1.3.3"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// Apply the application plugin to add support for building a CLI application in Java.
|
// Apply the application plugin to add support for building a CLI application in Java.
|
||||||
@ -46,6 +47,10 @@ dependencies {
|
|||||||
// Logging
|
// Logging
|
||||||
implementation("org.slf4j:slf4j-api:2.1.0-alpha1")
|
implementation("org.slf4j:slf4j-api:2.1.0-alpha1")
|
||||||
implementation("ch.qos.logback:logback-classic:1.5.18")
|
implementation("ch.qos.logback:logback-classic:1.5.18")
|
||||||
|
|
||||||
|
// gdx-video
|
||||||
|
implementation("com.badlogicgames.gdx-video:gdx-video:${gdxVideoVersion}")
|
||||||
|
implementation("com.badlogicgames.gdx-video:gdx-video-lwjgl3:${gdxVideoVersion}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply a specific Java toolchain to ease working on different environments.
|
// Apply a specific Java toolchain to ease working on different environments.
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
package org.vibecoders.moongazer;
|
package org.vibecoders.moongazer;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color;
|
|
||||||
import com.badlogic.gdx.graphics.Pixmap;
|
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client configuration constants and default values
|
* Client configuration constants and default values
|
||||||
* used throughout the Moongazer client application.
|
* used throughout the Moongazer client application.
|
||||||
|
|||||||
@ -26,6 +26,7 @@ public class Game extends ApplicationAdapter {
|
|||||||
Scene currentScene;
|
Scene currentScene;
|
||||||
Scene introScene;
|
Scene introScene;
|
||||||
public Scene mainMenuScene;
|
public Scene mainMenuScene;
|
||||||
|
public Scene settingsScene;
|
||||||
public ArrayList<Scene> gameScenes;
|
public ArrayList<Scene> gameScenes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -44,7 +45,7 @@ public class Game extends ApplicationAdapter {
|
|||||||
gameScenes = new ArrayList<>();
|
gameScenes = new ArrayList<>();
|
||||||
currentScene = introScene = new Intro(this);
|
currentScene = introScene = new Intro(this);
|
||||||
gameScenes.add(introScene);
|
gameScenes.add(introScene);
|
||||||
// By the end of the intro, the main menu scene will be created and assigned to Game.mainMenuScene
|
// By the end of the intro, other secenes will be created and assigned to Game.mainMenuScene
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -65,6 +66,9 @@ public class Game extends ApplicationAdapter {
|
|||||||
case MAIN_MENU:
|
case MAIN_MENU:
|
||||||
currentScene = mainMenuScene;
|
currentScene = mainMenuScene;
|
||||||
break;
|
break;
|
||||||
|
case SETTINGS:
|
||||||
|
currentScene = settingsScene;
|
||||||
|
break;
|
||||||
case IN_GAME:
|
case IN_GAME:
|
||||||
// Render in-game scene
|
// Render in-game scene
|
||||||
break;
|
break;
|
||||||
@ -79,11 +83,6 @@ public class Game extends ApplicationAdapter {
|
|||||||
scene.root.setVisible(false);
|
scene.root.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentScene.root.isVisible()) {
|
|
||||||
log.trace("Showing current scene: {}", currentScene.getClass().getSimpleName());
|
|
||||||
currentScene.root.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.begin();
|
batch.begin();
|
||||||
currentScene.render(batch);
|
currentScene.render(batch);
|
||||||
|
|||||||
@ -3,5 +3,6 @@ package org.vibecoders.moongazer;
|
|||||||
public enum State {
|
public enum State {
|
||||||
INTRO,
|
INTRO,
|
||||||
MAIN_MENU,
|
MAIN_MENU,
|
||||||
|
SETTINGS,
|
||||||
IN_GAME
|
IN_GAME
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,11 @@ package org.vibecoders.moongazer.managers;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.assets.AssetManager;
|
import com.badlogic.gdx.assets.AssetManager;
|
||||||
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
|
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
|
||||||
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
|
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
|
||||||
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.graphics.Pixmap;
|
import com.badlogic.gdx.graphics.Pixmap;
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
@ -13,18 +15,26 @@ import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGeneratorLoader;
|
|||||||
import com.badlogic.gdx.graphics.g2d.freetype.FreetypeFontLoader;
|
import com.badlogic.gdx.graphics.g2d.freetype.FreetypeFontLoader;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class Assets {
|
public class Assets {
|
||||||
private static final AssetManager assetManager = new AssetManager();
|
private static final AssetManager assetManager = new AssetManager();
|
||||||
private static final FileHandleResolver resolver = new InternalFileHandleResolver();
|
private static final FileHandleResolver resolver = new InternalFileHandleResolver();
|
||||||
private static final Logger log = org.slf4j.LoggerFactory.getLogger(Assets.class);
|
private static final Logger log = org.slf4j.LoggerFactory.getLogger(Assets.class);
|
||||||
private static final ArrayList<String> loadedFonts = new ArrayList<>();
|
private static final ArrayList<String> loadedFonts = new ArrayList<>();
|
||||||
|
private static final HashMap<String, FileHandle> loadedFiles = new HashMap<>();
|
||||||
private static boolean startLoadAll = false;
|
private static boolean startLoadAll = false;
|
||||||
private static boolean loadedAll = false;
|
private static boolean loadedAll = false;
|
||||||
private static Texture textureWhite;
|
private static Texture textureWhite;
|
||||||
private static Texture textureBlack;
|
private static Texture textureBlack;
|
||||||
|
|
||||||
public static <T> T getAsset(String fileName, Class<T> type) {
|
public static <T> T getAsset(String fileName, Class<T> type) {
|
||||||
|
if (type == FileHandle.class) {
|
||||||
|
if (!loadedFiles.containsKey(fileName)) {
|
||||||
|
loadAny(fileName);
|
||||||
|
}
|
||||||
|
return type.cast(loadedFiles.get(fileName));
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (!assetManager.isLoaded(fileName, type)) {
|
if (!assetManager.isLoaded(fileName, type)) {
|
||||||
log.warn("Asset not loaded: {}", fileName);
|
log.warn("Asset not loaded: {}", fileName);
|
||||||
@ -41,10 +51,11 @@ public class Assets {
|
|||||||
/**
|
/**
|
||||||
* Loads and returns a BitmapFont of the specified size from the given TTF file.
|
* Loads and returns a BitmapFont of the specified size from the given TTF file.
|
||||||
* <p>
|
* <p>
|
||||||
* Special file name "ui" is mapped to "fonts/H7GBKHeavy.ttf" (Wuthering Waves UI font).
|
* Special file name "ui" is mapped to "fonts/H7GBKHeavy.ttf" (Wuthering Waves
|
||||||
|
* UI font).
|
||||||
*
|
*
|
||||||
* @param fileName the font name
|
* @param fileName the font name
|
||||||
* @param size the font size
|
* @param size the font size
|
||||||
* @return the loaded BitmapFont
|
* @return the loaded BitmapFont
|
||||||
*/
|
*/
|
||||||
public static BitmapFont getFont(String fileName, int size) {
|
public static BitmapFont getFont(String fileName, int size) {
|
||||||
@ -74,6 +85,19 @@ public class Assets {
|
|||||||
waitUntilLoaded();
|
waitUntilLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void loadAny(String fileName) {
|
||||||
|
FileHandle fh = Gdx.files.internal(fileName);
|
||||||
|
if (!fh.exists()) {
|
||||||
|
log.error("File does not exist: {}", fileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (loadedFiles.containsKey(fileName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadedFiles.put(fileName, fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void loadAll() {
|
public static void loadAll() {
|
||||||
if (startLoadAll) {
|
if (startLoadAll) {
|
||||||
log.warn("loadAll() called multiple times!");
|
log.warn("loadAll() called multiple times!");
|
||||||
@ -89,6 +113,12 @@ public class Assets {
|
|||||||
assetManager.load("textures/main_menu/background.png", Texture.class);
|
assetManager.load("textures/main_menu/background.png", Texture.class);
|
||||||
assetManager.load("textures/main_menu/title.png", Texture.class);
|
assetManager.load("textures/main_menu/title.png", Texture.class);
|
||||||
assetManager.load("textures/ui/text_button.png", Texture.class);
|
assetManager.load("textures/ui/text_button.png", Texture.class);
|
||||||
|
assetManager.load("textures/ui/IconExitGame.png", Texture.class);
|
||||||
|
assetManager.load("textures/ui/UI_Icon_Setting.png", Texture.class);
|
||||||
|
assetManager.load("textures/ui/ImgReShaSoundOn.png", Texture.class);
|
||||||
|
assetManager.load("textures/ui/UI_Gcg_Icon_Close.png", Texture.class);
|
||||||
|
// "Load" unsupported file types as FileHandle
|
||||||
|
loadAny("videos/main_menu_background.webm");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLoadedAll() {
|
public static boolean isLoadedAll() {
|
||||||
@ -101,7 +131,7 @@ public class Assets {
|
|||||||
|
|
||||||
public static void waitUntilLoaded() {
|
public static void waitUntilLoaded() {
|
||||||
assetManager.finishLoading();
|
assetManager.finishLoading();
|
||||||
if (startLoadAll) {;
|
if (startLoadAll) {
|
||||||
loadedAll = true;
|
loadedAll = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +146,7 @@ public class Assets {
|
|||||||
pixmap.setColor(Color.WHITE);
|
pixmap.setColor(Color.WHITE);
|
||||||
pixmap.fill();
|
pixmap.fill();
|
||||||
textureWhite = new Texture(pixmap);
|
textureWhite = new Texture(pixmap);
|
||||||
pixmap.dispose(); // Important: dispose pixmap after creating texture
|
pixmap.dispose();
|
||||||
}
|
}
|
||||||
return textureWhite;
|
return textureWhite;
|
||||||
}
|
}
|
||||||
@ -133,6 +163,13 @@ public class Assets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void dispose() {
|
public static void dispose() {
|
||||||
|
for (var fontKey : loadedFonts) {
|
||||||
|
if (assetManager.isLoaded(fontKey, BitmapFont.class)) {
|
||||||
|
assetManager.unload(fontKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedFonts.clear();
|
||||||
|
loadedFiles.clear();
|
||||||
assetManager.dispose();
|
assetManager.dispose();
|
||||||
if (textureWhite != null) {
|
if (textureWhite != null) {
|
||||||
textureWhite.dispose();
|
textureWhite.dispose();
|
||||||
|
|||||||
@ -29,8 +29,11 @@ public class Intro extends Scene {
|
|||||||
startTime = System.currentTimeMillis() + 500;
|
startTime = System.currentTimeMillis() + 500;
|
||||||
log.info("Starting to load all remaining assets...");
|
log.info("Starting to load all remaining assets...");
|
||||||
Assets.loadAll();
|
Assets.loadAll();
|
||||||
|
// Create scenes
|
||||||
game.mainMenuScene = new MainMenu(game);
|
game.mainMenuScene = new MainMenu(game);
|
||||||
|
game.settingsScene = new Settings(game);
|
||||||
game.gameScenes.add(game.mainMenuScene);
|
game.gameScenes.add(game.mainMenuScene);
|
||||||
|
game.gameScenes.add(game.settingsScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,8 +60,12 @@ public class Intro extends Scene {
|
|||||||
}
|
}
|
||||||
currentOpacity = 1 - ((float) (System.currentTimeMillis() - endTime) / 1000);
|
currentOpacity = 1 - ((float) (System.currentTimeMillis() - endTime) / 1000);
|
||||||
}
|
}
|
||||||
batch.setColor(1, 1, 1, currentOpacity);
|
// Multiply with any externally applied alpha (e.g., Transition)
|
||||||
|
float externalAlpha = batch.getColor().a;
|
||||||
|
float finalAlpha = currentOpacity * externalAlpha;
|
||||||
|
batch.setColor(1, 1, 1, finalAlpha);
|
||||||
batch.draw(logo, WINDOW_WIDTH / 2 - logo.getWidth() / 4, WINDOW_HEIGHT / 2 - logo.getHeight() / 4,
|
batch.draw(logo, WINDOW_WIDTH / 2 - logo.getWidth() / 4, WINDOW_HEIGHT / 2 - logo.getHeight() / 4,
|
||||||
logo.getWidth() / 2, logo.getHeight() / 2);
|
logo.getWidth() / 2, logo.getHeight() / 2);
|
||||||
|
batch.setColor(1, 1, 1, externalAlpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,32 +2,58 @@ package org.vibecoders.moongazer.scenes;
|
|||||||
|
|
||||||
import static org.vibecoders.moongazer.Constants.*;
|
import static org.vibecoders.moongazer.Constants.*;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.vibecoders.moongazer.Game;
|
||||||
|
import org.vibecoders.moongazer.State;
|
||||||
import org.vibecoders.moongazer.managers.Assets;
|
import org.vibecoders.moongazer.managers.Assets;
|
||||||
import org.vibecoders.moongazer.ui.UITextButton;
|
import org.vibecoders.moongazer.ui.UITextButton;
|
||||||
import org.vibecoders.moongazer.Game;
|
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
|
import com.badlogic.gdx.Input;
|
||||||
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||||
|
import com.badlogic.gdx.video.VideoPlayer;
|
||||||
|
import com.badlogic.gdx.video.VideoPlayerCreator;
|
||||||
|
|
||||||
public class MainMenu extends Scene {
|
public class MainMenu extends Scene {
|
||||||
private Texture backgroundTexture;
|
private VideoPlayer videoPlayer;
|
||||||
|
private FileHandle videoFileHandle;
|
||||||
private Texture titleTexture;
|
private Texture titleTexture;
|
||||||
private float titleY;
|
private UITextButton[] buttons;
|
||||||
private float titleX;
|
private HashMap<Integer, Long> currentKeyDown = new HashMap<>();
|
||||||
private float titleWidth;
|
private float titleY, titleX, titleWidth, titleHeight;
|
||||||
private float titleHeight;
|
private boolean videoPrepared = false;
|
||||||
|
private int currentChoice = -1;
|
||||||
|
|
||||||
public MainMenu(Game game) {
|
public MainMenu(Game game) {
|
||||||
super(game);
|
super(game);
|
||||||
backgroundTexture = Assets.getAsset("textures/main_menu/background.png", Texture.class);
|
initVideo();
|
||||||
|
initUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initVideo() {
|
||||||
|
videoPlayer = VideoPlayerCreator.createVideoPlayer();
|
||||||
|
videoFileHandle = Assets.getAsset("videos/main_menu_background.webm", FileHandle.class);
|
||||||
|
try {
|
||||||
|
videoPlayer.load(videoFileHandle);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log.error("Failed to load video", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUI() {
|
||||||
|
// Title
|
||||||
titleTexture = Assets.getAsset("textures/main_menu/title.png", Texture.class);
|
titleTexture = Assets.getAsset("textures/main_menu/title.png", Texture.class);
|
||||||
// Scale and position title
|
|
||||||
float targetTitleWidth = 500f;
|
float targetTitleWidth = 500f;
|
||||||
float originalWidth = titleTexture.getWidth();
|
float scale = targetTitleWidth / titleTexture.getWidth();
|
||||||
float originalHeight = titleTexture.getHeight();
|
titleWidth = titleTexture.getWidth() * scale;
|
||||||
float scale = targetTitleWidth / originalWidth;
|
titleHeight = titleTexture.getHeight() * scale;
|
||||||
titleWidth = originalWidth * scale;
|
|
||||||
titleHeight = originalHeight * scale;
|
|
||||||
titleX = (WINDOW_WIDTH - titleWidth) / 2f;
|
titleX = (WINDOW_WIDTH - titleWidth) / 2f;
|
||||||
titleY = WINDOW_HEIGHT / 2f - titleHeight / 8f;
|
titleY = WINDOW_HEIGHT / 2f - titleHeight / 8f;
|
||||||
|
|
||||||
@ -37,9 +63,11 @@ public class MainMenu extends Scene {
|
|||||||
UITextButton loadButton = new UITextButton("Load", font);
|
UITextButton loadButton = new UITextButton("Load", font);
|
||||||
UITextButton settingsButton = new UITextButton("Settings", font);
|
UITextButton settingsButton = new UITextButton("Settings", font);
|
||||||
UITextButton exitButton = new UITextButton("Exit", font);
|
UITextButton exitButton = new UITextButton("Exit", font);
|
||||||
|
buttons = new UITextButton[] { playButton, loadButton, settingsButton, exitButton };
|
||||||
|
|
||||||
int buttonWidth = 300;
|
int buttonWidth = 300;
|
||||||
int buttonHeight = 80;
|
int buttonHeight = 80;
|
||||||
|
|
||||||
playButton.setSize(buttonWidth, buttonHeight);
|
playButton.setSize(buttonWidth, buttonHeight);
|
||||||
loadButton.setSize(buttonWidth, buttonHeight);
|
loadButton.setSize(buttonWidth, buttonHeight);
|
||||||
settingsButton.setSize(buttonWidth, buttonHeight);
|
settingsButton.setSize(buttonWidth, buttonHeight);
|
||||||
@ -47,28 +75,147 @@ public class MainMenu extends Scene {
|
|||||||
|
|
||||||
int centerX = WINDOW_WIDTH / 2 - buttonWidth / 2;
|
int centerX = WINDOW_WIDTH / 2 - buttonWidth / 2;
|
||||||
int startY = WINDOW_HEIGHT / 2 - buttonHeight / 2;
|
int startY = WINDOW_HEIGHT / 2 - buttonHeight / 2;
|
||||||
int buttonSpacing = 65;
|
int spacing = 65;
|
||||||
|
|
||||||
|
playButton.setSize(buttonWidth, buttonHeight);
|
||||||
playButton.setPosition(centerX, startY);
|
playButton.setPosition(centerX, startY);
|
||||||
loadButton.setPosition(centerX, startY - buttonSpacing);
|
loadButton.setSize(buttonWidth, buttonHeight);
|
||||||
settingsButton.setPosition(centerX, startY - buttonSpacing * 2);
|
loadButton.setPosition(centerX, startY - spacing);
|
||||||
exitButton.setPosition(centerX, startY - buttonSpacing * 3);
|
settingsButton.setSize(buttonWidth, buttonHeight);
|
||||||
|
settingsButton.setPosition(centerX, startY - spacing * 2);
|
||||||
|
exitButton.setSize(buttonWidth, buttonHeight);
|
||||||
|
exitButton.setPosition(centerX, startY - spacing * 3);
|
||||||
|
|
||||||
|
// Mouse click handlers
|
||||||
playButton.onClick(() -> log.debug("Play clicked"));
|
playButton.onClick(() -> log.debug("Play clicked"));
|
||||||
loadButton.onClick(() -> log.debug("Load clicked"));
|
loadButton.onClick(() -> log.debug("Load clicked"));
|
||||||
settingsButton.onClick(() -> log.debug("Settings clicked"));
|
settingsButton.onClick(() -> {
|
||||||
exitButton.onClick(() -> log.debug("Exit clicked"));
|
log.debug("Settings clicked");
|
||||||
|
if (game.transition == null) {
|
||||||
|
game.transition = new Transition(game, this, game.settingsScene, State.SETTINGS, 350);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exitButton.onClick(() -> {
|
||||||
|
log.debug("Exit clicked");
|
||||||
|
Gdx.app.exit();
|
||||||
|
});
|
||||||
|
|
||||||
root.addActor(playButton.getActor());
|
root.addActor(playButton.getActor());
|
||||||
root.addActor(loadButton.getActor());
|
root.addActor(loadButton.getActor());
|
||||||
root.addActor(settingsButton.getActor());
|
root.addActor(settingsButton.getActor());
|
||||||
root.addActor(exitButton.getActor());
|
root.addActor(exitButton.getActor());
|
||||||
|
|
||||||
|
// Keyboard navigation handling
|
||||||
|
initKeyboardHandling();
|
||||||
game.stage.addActor(root);
|
game.stage.addActor(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initKeyboardHandling() {
|
||||||
|
for (int i = 0; i < buttons.length; i++) {
|
||||||
|
final int index = i;
|
||||||
|
buttons[i].onHoverEnter(() -> {
|
||||||
|
log.trace("Button hover enter: {}", index);
|
||||||
|
if (currentChoice != index) {
|
||||||
|
if (currentChoice != -1) {
|
||||||
|
buttons[currentChoice].hoverExit();
|
||||||
|
}
|
||||||
|
currentChoice = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
buttons[i].onHoverExit(() -> {
|
||||||
|
if (currentChoice == index) {
|
||||||
|
currentChoice = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addListener(new InputListener() {
|
||||||
|
@Override
|
||||||
|
public boolean keyDown(InputEvent event, int keycode) {
|
||||||
|
sKeyDown(event, keycode);
|
||||||
|
currentKeyDown.put(keycode, System.currentTimeMillis());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
root.addListener(new InputListener() {
|
||||||
|
@Override
|
||||||
|
public boolean keyUp(InputEvent event, int keycode) {
|
||||||
|
currentKeyDown.remove(keycode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startVideoOnce() {
|
||||||
|
if (videoPlayer == null || videoPrepared)
|
||||||
|
return;
|
||||||
|
if (videoFileHandle == null || !videoFileHandle.exists())
|
||||||
|
return;
|
||||||
|
if (game.transition == null && game.state != State.MAIN_MENU)
|
||||||
|
return;
|
||||||
|
videoPlayer.setLooping(true);
|
||||||
|
videoPlayer.play();
|
||||||
|
videoPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual key down handler. ((s)cene key down)
|
||||||
|
*
|
||||||
|
* @param event not used for now, can be null
|
||||||
|
* @param keycode the keycode of the pressed key
|
||||||
|
*/
|
||||||
|
public void sKeyDown(InputEvent event, int keycode) {
|
||||||
|
log.trace("Key pressed: {}", keycode);
|
||||||
|
switch (keycode) {
|
||||||
|
case Input.Keys.UP:
|
||||||
|
currentChoice = (currentChoice - 1 + 4) % 4;
|
||||||
|
break;
|
||||||
|
case Input.Keys.DOWN:
|
||||||
|
currentChoice = (currentChoice + 1) % 4;
|
||||||
|
break;
|
||||||
|
case Input.Keys.RIGHT:
|
||||||
|
case Input.Keys.ENTER:
|
||||||
|
if (currentChoice != -1) {
|
||||||
|
buttons[currentChoice].click();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (currentChoice != -1) {
|
||||||
|
log.trace("Current choice: {}", currentChoice);
|
||||||
|
for (int i = 0; i < buttons.length; i++) {
|
||||||
|
if (i == currentChoice) {
|
||||||
|
buttons[i].hoverEnter();
|
||||||
|
} else {
|
||||||
|
buttons[i].hoverExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(SpriteBatch batch) {
|
public void render(SpriteBatch batch) {
|
||||||
batch.draw(backgroundTexture, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
// SDL way of handling key input XD
|
||||||
|
for (Map.Entry<Integer, Long> entry : currentKeyDown.entrySet()) {
|
||||||
|
Integer keyCode = entry.getKey();
|
||||||
|
Long timeStamp = entry.getValue();
|
||||||
|
if (System.currentTimeMillis() - timeStamp > 100) {
|
||||||
|
sKeyDown(null, keyCode);
|
||||||
|
currentKeyDown.put(keyCode, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startVideoOnce();
|
||||||
|
videoPlayer.update();
|
||||||
|
Texture videoTexture = videoPlayer.getTexture();
|
||||||
|
batch.draw(videoTexture, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
batch.draw(titleTexture, titleX, titleY, titleWidth, titleHeight);
|
batch.draw(titleTexture, titleX, titleY, titleWidth, titleHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
videoPlayer.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -10,8 +10,10 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table;
|
|||||||
public abstract class Scene {
|
public abstract class Scene {
|
||||||
protected final Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
|
protected final Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
|
||||||
public Table root;
|
public Table root;
|
||||||
|
public Game game;
|
||||||
public Scene(Game game) {
|
public Scene(Game game) {
|
||||||
this();
|
this();
|
||||||
|
this.game = game;
|
||||||
}
|
}
|
||||||
public Scene() {
|
public Scene() {
|
||||||
root = new Table();
|
root = new Table();
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
package org.vibecoders.moongazer.scenes;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
|
|
||||||
|
import static org.vibecoders.moongazer.Constants.*;
|
||||||
|
|
||||||
|
import org.vibecoders.moongazer.Game;
|
||||||
|
import org.vibecoders.moongazer.State;
|
||||||
|
import org.vibecoders.moongazer.managers.Assets;
|
||||||
|
import org.vibecoders.moongazer.ui.UIImageButton;
|
||||||
|
|
||||||
|
public class Settings extends Scene {
|
||||||
|
public Settings(Game game) {
|
||||||
|
super(game);
|
||||||
|
// WIP
|
||||||
|
UIImageButton settingButton = new UIImageButton("textures/ui/UI_Icon_Setting.png");
|
||||||
|
UIImageButton exitImgButton = new UIImageButton("textures/ui/IconExitGame.png");
|
||||||
|
UIImageButton soundButton = new UIImageButton("textures/ui/ImgReShaSoundOn.png");
|
||||||
|
UIImageButton closeButton = new UIImageButton("textures/ui/UI_Gcg_Icon_Close.png");
|
||||||
|
settingButton.setSize(50, 50);
|
||||||
|
exitImgButton.setSize(50, 50);
|
||||||
|
soundButton.setSize(50, 50);
|
||||||
|
closeButton.setSize(50, 50);
|
||||||
|
settingButton.setPosition(20, WINDOW_HEIGHT - 70);
|
||||||
|
exitImgButton.setPosition(WINDOW_WIDTH - 70, WINDOW_HEIGHT - 70);
|
||||||
|
soundButton.setPosition(20, 20);
|
||||||
|
closeButton.setPosition(WINDOW_WIDTH - 70, 20);
|
||||||
|
|
||||||
|
root.addActor(settingButton.getActor());
|
||||||
|
root.addActor(exitImgButton.getActor());
|
||||||
|
root.addActor(soundButton.getActor());
|
||||||
|
root.addActor(closeButton.getActor());
|
||||||
|
settingButton.onClick(() -> log.debug("Settings clicked"));
|
||||||
|
exitImgButton.onClick(() -> {
|
||||||
|
log.debug("Exit clicked");
|
||||||
|
if (game.transition == null) {
|
||||||
|
game.transition = new Transition(game, this, game.mainMenuScene, State.MAIN_MENU, 350);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
soundButton.onClick(() -> log.debug("Sound clicked"));
|
||||||
|
closeButton.onClick(() -> log.debug("Close clicked"));
|
||||||
|
|
||||||
|
game.stage.addActor(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(SpriteBatch batch) {
|
||||||
|
batch.draw(Assets.getWhiteTexture(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,7 +9,6 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
|||||||
* Handles transitions between scenes with a linear transition effect.
|
* Handles transitions between scenes with a linear transition effect.
|
||||||
*/
|
*/
|
||||||
public class Transition extends Scene {
|
public class Transition extends Scene {
|
||||||
private Game game;
|
|
||||||
private Scene from;
|
private Scene from;
|
||||||
private Scene to;
|
private Scene to;
|
||||||
private State targetState;
|
private State targetState;
|
||||||
@ -25,8 +24,8 @@ public class Transition extends Scene {
|
|||||||
*/
|
*/
|
||||||
public Transition(Game game, Scene from, Scene to, State targetState, long duration) {
|
public Transition(Game game, Scene from, Scene to, State targetState, long duration) {
|
||||||
// Transition does not need to render UI elements
|
// Transition does not need to render UI elements
|
||||||
|
super(game);
|
||||||
this.root = null;
|
this.root = null;
|
||||||
this.game = game;
|
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.targetState = targetState;
|
this.targetState = targetState;
|
||||||
@ -45,6 +44,10 @@ public class Transition extends Scene {
|
|||||||
log.trace("Transition complete to state: {}", targetState);
|
log.trace("Transition complete to state: {}", targetState);
|
||||||
game.state = targetState;
|
game.state = targetState;
|
||||||
game.transition = null;
|
game.transition = null;
|
||||||
|
// Set keyboard focus to the new scene's root
|
||||||
|
from.root.setVisible(false);
|
||||||
|
to.root.setVisible(true);
|
||||||
|
game.stage.setKeyboardFocus(to.root);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var fromOpacity = 1 - toOpacity;
|
var fromOpacity = 1 - toOpacity;
|
||||||
@ -58,4 +61,4 @@ public class Transition extends Scene {
|
|||||||
to.root.setColor(1, 1, 1, toOpacity);
|
to.root.setColor(1, 1, 1, toOpacity);
|
||||||
to.render(batch);
|
to.render(batch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,15 @@
|
|||||||
package org.vibecoders.moongazer.ui;
|
package org.vibecoders.moongazer.ui;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Input;
|
||||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||||
import com.badlogic.gdx.scenes.scene2d.EventListener;
|
import com.badlogic.gdx.scenes.scene2d.EventListener;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Button;
|
import com.badlogic.gdx.scenes.scene2d.ui.Button;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||||
|
|
||||||
public abstract class UIButton {
|
public abstract class UIButton {
|
||||||
protected Actor actor;
|
public Actor actor;
|
||||||
protected Button button;
|
public Button button;
|
||||||
|
|
||||||
public Actor getActor() {
|
public Actor getActor() {
|
||||||
return actor;
|
return actor;
|
||||||
@ -24,12 +27,65 @@ public abstract class UIButton {
|
|||||||
actor.addListener(eventListener);
|
actor.addListener(eventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void click() {
|
||||||
|
// Thx ChatGPT
|
||||||
|
InputEvent down = new InputEvent();
|
||||||
|
down.setType(InputEvent.Type.touchDown);
|
||||||
|
down.setButton(Input.Buttons.LEFT);
|
||||||
|
down.setStageX(button.getX());
|
||||||
|
down.setStageY(button.getY());
|
||||||
|
button.fire(down);
|
||||||
|
|
||||||
|
InputEvent up = new InputEvent();
|
||||||
|
up.setType(InputEvent.Type.touchUp);
|
||||||
|
up.setButton(Input.Buttons.LEFT);
|
||||||
|
up.setStageX(button.getX());
|
||||||
|
up.setStageY(button.getY());
|
||||||
|
button.fire(up);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hoverEnter() {
|
||||||
|
InputEvent e = new InputEvent();
|
||||||
|
e.setType(InputEvent.Type.enter);
|
||||||
|
e.setPointer(-1);
|
||||||
|
button.fire(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hoverExit() {
|
||||||
|
InputEvent e = new InputEvent();
|
||||||
|
e.setType(InputEvent.Type.exit);
|
||||||
|
e.setPointer(-1);
|
||||||
|
button.fire(e);
|
||||||
|
}
|
||||||
|
|
||||||
public void onClick(Runnable action) {
|
public void onClick(Runnable action) {
|
||||||
button.addListener(new com.badlogic.gdx.scenes.scene2d.utils.ClickListener() {
|
button.addListener(new ClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void clicked(com.badlogic.gdx.scenes.scene2d.InputEvent event, float x, float y) {
|
public void clicked(InputEvent event, float x, float y) {
|
||||||
action.run();
|
action.run();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onHoverEnter(Runnable action) {
|
||||||
|
button.addListener(new ClickListener() {
|
||||||
|
@Override
|
||||||
|
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
|
||||||
|
if (pointer == -1) {
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onHoverExit(Runnable action) {
|
||||||
|
button.addListener(new ClickListener() {
|
||||||
|
@Override
|
||||||
|
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
|
||||||
|
if (pointer == -1) {
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,20 @@
|
|||||||
package org.vibecoders.moongazer.ui;
|
package org.vibecoders.moongazer.ui;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
|
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
|
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||||
|
import org.vibecoders.moongazer.managers.Assets;
|
||||||
|
|
||||||
public class UIImageButton extends UIButton {
|
public class UIImageButton extends UIButton {
|
||||||
public UIImageButton() {
|
public UIImageButton(String texturePath) {
|
||||||
this.button = new ImageButton(new ImageButton.ImageButtonStyle());
|
Texture texture = Assets.getAsset(texturePath, Texture.class);
|
||||||
|
TextureRegionDrawable drawable = new TextureRegionDrawable(new TextureRegion(texture));
|
||||||
|
ImageButton.ImageButtonStyle style = new ImageButton.ImageButtonStyle();
|
||||||
|
style.imageUp = drawable;
|
||||||
|
style.imageDown = drawable;
|
||||||
|
style.imageOver = drawable;
|
||||||
|
this.button = new ImageButton(style);
|
||||||
this.actor = button;
|
this.actor = button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
app/src/main/resources/textures/ui/IconExitGame.png
Normal file
BIN
app/src/main/resources/textures/ui/IconExitGame.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 480 B |
BIN
app/src/main/resources/textures/ui/ImgReShaSoundOn.png
Normal file
BIN
app/src/main/resources/textures/ui/ImgReShaSoundOn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/resources/textures/ui/UI_Gcg_Icon_Close.png
Normal file
BIN
app/src/main/resources/textures/ui/UI_Gcg_Icon_Close.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 869 B |
BIN
app/src/main/resources/textures/ui/UI_Icon_Setting.png
Normal file
BIN
app/src/main/resources/textures/ui/UI_Icon_Setting.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/resources/videos/main_menu_background.webm
Normal file
BIN
app/src/main/resources/videos/main_menu_background.webm
Normal file
Binary file not shown.
Reference in New Issue
Block a user