add background video
This commit is contained in:
@ -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.
|
||||||
|
|||||||
@ -57,8 +57,13 @@ 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; // alpha set by Transition before calling render
|
||||||
|
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);
|
||||||
|
// Reset color for safety (Transition will set again anyway)
|
||||||
|
batch.setColor(1,1,1,externalAlpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,62 +2,84 @@ package org.vibecoders.moongazer.scenes;
|
|||||||
|
|
||||||
import static org.vibecoders.moongazer.Constants.*;
|
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.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.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.utils.TimeUtils;
|
||||||
|
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 final Game game;
|
||||||
|
private VideoPlayer videoPlayer;
|
||||||
|
private FileHandle videoFileHandle;
|
||||||
private Texture titleTexture;
|
private Texture titleTexture;
|
||||||
private float titleY;
|
private float titleY, titleX, titleWidth, titleHeight;
|
||||||
private float titleX;
|
private boolean videoPrepared = false;
|
||||||
private float titleWidth;
|
private boolean firstFrameLogged = false;
|
||||||
private float titleHeight;
|
private long videoStartTime;
|
||||||
|
|
||||||
|
private static final String WEBM_PATH = "videos/main_menu_background.webm";
|
||||||
|
private static final String OGV_PATH = "videos/main_menu_background.ogv";
|
||||||
|
|
||||||
public MainMenu(Game game) {
|
public MainMenu(Game game) {
|
||||||
super(game);
|
super(game);
|
||||||
backgroundTexture = Assets.getAsset("textures/main_menu/background.png", Texture.class);
|
this.game = game;
|
||||||
|
initVideo();
|
||||||
|
initUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initVideo() {
|
||||||
|
try {
|
||||||
|
videoPlayer = VideoPlayerCreator.createVideoPlayer();
|
||||||
|
FileHandle webm = Gdx.files.internal(WEBM_PATH);
|
||||||
|
FileHandle ogv = Gdx.files.internal(OGV_PATH);
|
||||||
|
if (webm.exists()) {
|
||||||
|
videoFileHandle = webm;
|
||||||
|
log.info("Using menu background (WebM): {}", webm.path());
|
||||||
|
} else if (ogv.exists()) {
|
||||||
|
videoFileHandle = ogv;
|
||||||
|
log.info("Using menu background (OGV): {}", ogv.path());
|
||||||
|
} else {
|
||||||
|
log.warn("No background video found (expected {} or {}).", WEBM_PATH, OGV_PATH);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Cannot create VideoPlayer", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUI() {
|
||||||
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;
|
||||||
|
|
||||||
// Buttons
|
|
||||||
var font = Assets.getFont("ui", 24);
|
var font = Assets.getFont("ui", 24);
|
||||||
UITextButton playButton = new UITextButton("Play", font);
|
UITextButton playButton = new UITextButton("Play", font);
|
||||||
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);
|
||||||
|
|
||||||
int buttonWidth = 300;
|
int buttonWidth = 300, buttonHeight = 80;
|
||||||
int buttonHeight = 80;
|
|
||||||
playButton.setSize(buttonWidth, buttonHeight);
|
|
||||||
loadButton.setSize(buttonWidth, buttonHeight);
|
|
||||||
settingsButton.setSize(buttonWidth, buttonHeight);
|
|
||||||
exitButton.setSize(buttonWidth, buttonHeight);
|
|
||||||
|
|
||||||
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.setPosition(centerX, startY);
|
playButton.setSize(buttonWidth, buttonHeight); playButton.setPosition(centerX, startY);
|
||||||
loadButton.setPosition(centerX, startY - buttonSpacing);
|
loadButton.setSize(buttonWidth, buttonHeight); loadButton.setPosition(centerX, startY - spacing);
|
||||||
settingsButton.setPosition(centerX, startY - buttonSpacing * 2);
|
settingsButton.setSize(buttonWidth, buttonHeight); settingsButton.setPosition(centerX, startY - spacing * 2);
|
||||||
exitButton.setPosition(centerX, startY - buttonSpacing * 3);
|
exitButton.setSize(buttonWidth, buttonHeight); exitButton.setPosition(centerX, startY - spacing * 3);
|
||||||
|
|
||||||
playButton.onClick(() -> log.debug("Play clicked"));
|
exitButton.onClick(() -> log.info("Exit clicked"));
|
||||||
loadButton.onClick(() -> log.debug("Load clicked"));
|
|
||||||
settingsButton.onClick(() -> log.debug("Settings clicked"));
|
|
||||||
exitButton.onClick(() -> log.debug("Exit clicked"));
|
|
||||||
|
|
||||||
root.addActor(playButton.getActor());
|
root.addActor(playButton.getActor());
|
||||||
root.addActor(loadButton.getActor());
|
root.addActor(loadButton.getActor());
|
||||||
@ -66,9 +88,56 @@ public class MainMenu extends Scene {
|
|||||||
game.stage.addActor(root);
|
game.stage.addActor(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureVideoStarted() {
|
||||||
|
if (videoPlayer == null || videoPrepared) return;
|
||||||
|
if (videoFileHandle == null || !videoFileHandle.exists()) return;
|
||||||
|
if (game.transition == null && game.state != State.MAIN_MENU) return;
|
||||||
|
startVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startVideo() {
|
||||||
|
try {
|
||||||
|
videoPlayer.setLooping(true);
|
||||||
|
videoPlayer.play(videoFileHandle);
|
||||||
|
videoPrepared = true;
|
||||||
|
videoStartTime = TimeUtils.millis();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to play video: {}", videoFileHandle.path(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceStartVideo() { // used by Transition for early warm-up
|
||||||
|
if (!videoPrepared && videoFileHandle != null && videoFileHandle.exists()) startVideo();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(SpriteBatch batch) {
|
public void render(SpriteBatch batch) {
|
||||||
batch.draw(backgroundTexture, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
ensureVideoStarted();
|
||||||
|
if (videoPlayer != null && videoPrepared) {
|
||||||
|
videoPlayer.update();
|
||||||
|
Texture videoTexture = videoPlayer.getTexture();
|
||||||
|
if (videoTexture != null) {
|
||||||
|
if (!firstFrameLogged) {
|
||||||
|
firstFrameLogged = true;
|
||||||
|
log.info("Menu video first frame in {} ms", TimeUtils.timeSinceMillis(videoStartTime));
|
||||||
|
}
|
||||||
|
batch.draw(videoTexture, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
} else {
|
||||||
|
batch.draw(Assets.getBlackTexture(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
batch.draw(Assets.getBlackTexture(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
}
|
||||||
batch.draw(titleTexture, titleX, titleY, titleWidth, titleHeight);
|
batch.draw(titleTexture, titleX, titleY, titleWidth, titleHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateVideo() {
|
||||||
|
if (videoPlayer != null && videoPrepared) videoPlayer.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
if (videoPlayer != null) videoPlayer.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -58,4 +58,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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