Merge branch 'main' into tunxd

This commit is contained in:
Nguyễn Thế Hưng
2025-09-30 16:33:18 +07:00
committed by GitHub
8 changed files with 122 additions and 26 deletions

View File

@ -16,12 +16,4 @@ public class Constants {
public static final int WINDOW_WIDTH = 1280; public static final int WINDOW_WIDTH = 1280;
public static final int WINDOW_HEIGHT = 720; public static final int WINDOW_HEIGHT = 720;
public static final String WINDOW_TITLE = "Moongazer"; public static final String WINDOW_TITLE = "Moongazer";
public static final Texture TEXTURE_WHITE = new Texture(new Pixmap(1, 1, Pixmap.Format.RGBA8888) {{
setColor(Color.WHITE);
fill();
}});
public static final Texture TEXTURE_BLACK = new Texture(new Pixmap(1, 1, Pixmap.Format.RGBA8888) {{
setColor(Color.BLACK);
fill();
}});
} }

View File

@ -1,8 +1,13 @@
package org.vibecoders.moongazer; package org.vibecoders.moongazer;
import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import java.util.ArrayList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -11,13 +16,17 @@ import org.vibecoders.moongazer.scenes.*;
public class Game extends ApplicationAdapter { public class Game extends ApplicationAdapter {
private static final Logger log = LoggerFactory.getLogger(Game.class); private static final Logger log = LoggerFactory.getLogger(Game.class);
public static State state = State.INTRO; public State state = State.INTRO;
public static Transition transition = null; public Transition transition = null;
SpriteBatch batch; SpriteBatch batch;
Texture logo; // UI stage
public Stage stage;
public Table root;
// Scenes
Scene currentScene; Scene currentScene;
Scene introScene; Scene introScene;
public static Scene mainMenuScene; public Scene mainMenuScene;
public ArrayList<Scene> gameScenes;
@Override @Override
public void create() { public void create() {
@ -25,7 +34,16 @@ public class Game extends ApplicationAdapter {
Assets.loadIntroAndWait(); Assets.loadIntroAndWait();
log.info("Intro assets loaded successfully."); log.info("Intro assets loaded successfully.");
batch = new SpriteBatch(); batch = new SpriteBatch();
currentScene = introScene = new Intro(); // Stage for UI elements
stage = new Stage(new ScreenViewport(), batch);
Gdx.input.setInputProcessor(stage);
root = new Table();
root.setFillParent(true);
stage.addActor(root);
// Scene initialization
gameScenes = new ArrayList<>();
currentScene = introScene = new Intro(this);
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, the main menu scene will be created and assigned to Game.mainMenuScene
} }
@ -36,9 +54,12 @@ public class Game extends ApplicationAdapter {
batch.begin(); batch.begin();
transition.render(batch); transition.render(batch);
batch.end(); batch.end();
// Handle stage drawing for UI elements
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
return; return;
} }
switch (Game.state) { switch (this.state) {
case INTRO: case INTRO:
currentScene = introScene; currentScene = introScene;
break; break;
@ -51,9 +72,26 @@ public class Game extends ApplicationAdapter {
default: default:
log.warn("Unknown state: {}", state); log.warn("Unknown state: {}", state);
} }
for (var scene : gameScenes) {
// log.trace("Checking scene visibility: {}", scene.getClass().getSimpleName());
if (scene != currentScene && scene.root.isVisible()) {
log.trace("Hiding scene: {}", scene.getClass().getSimpleName());
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);
batch.end(); batch.end();
// Handle stage drawing for UI elements
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
} }
@Override @Override
@ -61,6 +99,8 @@ public class Game extends ApplicationAdapter {
introScene.dispose(); introScene.dispose();
mainMenuScene.dispose(); mainMenuScene.dispose();
Assets.dispose(); Assets.dispose();
batch.dispose();
stage.dispose();
log.debug("Resources disposed"); log.debug("Resources disposed");
} }
} }

View File

@ -2,6 +2,8 @@ package org.vibecoders.moongazer;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration.GLEmulation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static org.vibecoders.moongazer.Constants.*; import static org.vibecoders.moongazer.Constants.*;
@ -20,6 +22,7 @@ public class Main {
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Lwjgl3ApplicationConfiguration cfg = new Lwjgl3ApplicationConfiguration(); Lwjgl3ApplicationConfiguration cfg = new Lwjgl3ApplicationConfiguration();
cfg.setOpenGLEmulation(GLEmulation.GL32, 3, 2);
cfg.setTitle(WINDOW_TITLE); cfg.setTitle(WINDOW_TITLE);
cfg.setWindowedMode(WINDOW_WIDTH, WINDOW_HEIGHT); cfg.setWindowedMode(WINDOW_WIDTH, WINDOW_HEIGHT);
cfg.useVsync(true); cfg.useVsync(true);

View File

@ -5,6 +5,8 @@ import org.slf4j.Logger;
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.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGeneratorLoader; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGeneratorLoader;
@ -19,9 +21,21 @@ public class Assets {
private static final ArrayList<String> loadedFonts = new ArrayList<>(); private static final ArrayList<String> loadedFonts = new ArrayList<>();
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 textureBlack;
public static <T> T getAsset(String fileName, Class<T> type) { public static <T> T getAsset(String fileName, Class<T> type) {
return assetManager.get(fileName, type); try {
if (!assetManager.isLoaded(fileName, type)) {
log.warn("Asset not loaded: {}", fileName);
assetManager.load(fileName, type);
assetManager.finishLoadingAsset(fileName);
}
return assetManager.get(fileName, type);
} catch (Exception e) {
log.error("Failed to load asset: {}", fileName, e);
throw new RuntimeException("Asset loading failed: " + fileName, e);
}
} }
/** /**
@ -85,8 +99,7 @@ public class Assets {
public static void waitUntilLoaded() { public static void waitUntilLoaded() {
assetManager.finishLoading(); assetManager.finishLoading();
if (startLoadAll) { if (startLoadAll) {;
log.info("All assets loaded.");
loadedAll = true; loadedAll = true;
} }
} }
@ -95,7 +108,37 @@ public class Assets {
return assetManager; return assetManager;
} }
public static Texture getWhiteTexture() {
if (textureWhite == null) {
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.WHITE);
pixmap.fill();
textureWhite = new Texture(pixmap);
pixmap.dispose(); // Important: dispose pixmap after creating texture
}
return textureWhite;
}
public static Texture getBlackTexture() {
if (textureBlack == null) {
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.BLACK);
pixmap.fill();
textureBlack = new Texture(pixmap);
pixmap.dispose();
}
return textureBlack;
}
public static void dispose() { public static void dispose() {
assetManager.dispose(); assetManager.dispose();
if (textureWhite != null) {
textureWhite.dispose();
textureWhite = null;
}
if (textureBlack != null) {
textureBlack.dispose();
textureBlack = null;
}
} }
} }

View File

@ -16,17 +16,21 @@ import com.badlogic.gdx.utils.ScreenUtils;
*/ */
public class Intro extends Scene { public class Intro extends Scene {
private Texture logo; private Texture logo;
private Game game;
private long startTime; private long startTime;
private long endTime = 0; private long endTime = 0;
/** /**
* Initializes the intro scene, starts loading assets. * Initializes the intro scene, starts loading assets.
*/ */
public Intro() { public Intro(Game game) {
this.game = game;
logo = Assets.getAsset("icons/logo.png", Texture.class); logo = Assets.getAsset("icons/logo.png", Texture.class);
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();
game.mainMenuScene = new MainMenu(game);
game.gameScenes.add(game.mainMenuScene);
} }
/** /**
@ -36,12 +40,12 @@ public class Intro extends Scene {
@Override @Override
public void render(SpriteBatch batch) { public void render(SpriteBatch batch) {
if (System.currentTimeMillis() > endTime + 2000 && endTime != 0) { if (System.currentTimeMillis() > endTime + 2000 && endTime != 0) {
if (Game.transition == null) { if (game.transition == null) {
Assets.waitUntilLoaded(); Assets.waitUntilLoaded();
Game.mainMenuScene = new MainMenu(); log.info("All assets loaded successfully.");
Game.transition = new Transition(this, Game.mainMenuScene, State.MAIN_MENU, 1000); game.transition = new Transition(game, this, game.mainMenuScene, State.MAIN_MENU, 1000);
} }
batch.draw(TEXTURE_BLACK, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); batch.draw(Assets.getBlackTexture(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
return; return;
} }
ScreenUtils.clear(Color.BLACK); ScreenUtils.clear(Color.BLACK);

View File

@ -3,6 +3,7 @@ package org.vibecoders.moongazer.scenes;
import static org.vibecoders.moongazer.Constants.*; import static org.vibecoders.moongazer.Constants.*;
import org.vibecoders.moongazer.managers.Assets; import org.vibecoders.moongazer.managers.Assets;
import org.vibecoders.moongazer.Game;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;

View File

@ -1,13 +1,20 @@
package org.vibecoders.moongazer.scenes; package org.vibecoders.moongazer.scenes;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.vibecoders.moongazer.Game;
import org.vibecoders.moongazer.managers.Assets; import org.vibecoders.moongazer.managers.Assets;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
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 Scene(Game game) {
this();
}
public Scene() { public Scene() {
root = new Table();
if (!Assets.isLoadedAll() && Assets.isStartLoadAll()) { if (!Assets.isLoadedAll() && Assets.isStartLoadAll()) {
Assets.waitUntilLoaded(); Assets.waitUntilLoaded();
} }

View File

@ -9,6 +9,7 @@ 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;
@ -22,7 +23,10 @@ public class Transition extends Scene {
* @param targetState The target state of the game after the transition. * @param targetState The target state of the game after the transition.
* @param duration The duration of the transition in milliseconds. * @param duration The duration of the transition in milliseconds.
*/ */
public Transition(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
this.root = null;
this.game = game;
this.from = from; this.from = from;
this.to = to; this.to = to;
this.targetState = targetState; this.targetState = targetState;
@ -39,15 +43,17 @@ public class Transition extends Scene {
var toOpacity = ((float) (System.currentTimeMillis() - startTime)) / duration; var toOpacity = ((float) (System.currentTimeMillis() - startTime)) / duration;
if (toOpacity >= 0.99) { if (toOpacity >= 0.99) {
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;
return; return;
} }
var fromOpacity = 1 - toOpacity; var fromOpacity = 1 - toOpacity;
log.trace("Transition opacities - from: {}, to: {}", fromOpacity, toOpacity); log.trace("Transition opacities - from: {}, to: {}", fromOpacity, toOpacity);
batch.setColor(1, 1, 1, fromOpacity); batch.setColor(1, 1, 1, fromOpacity);
from.root.setVisible(true);
from.render(batch); from.render(batch);
batch.setColor(1, 1, 1, toOpacity); batch.setColor(1, 1, 1, toOpacity);
to.root.setVisible(true);
to.render(batch); to.render(batch);
} }
} }