3

I have a UI bar and a game board rectangle bellow it.

I want the UI elements to maintain their original size, regardless of screen resolution. This can be achieved via a ScreenWievport (as per suggested here), but that completely messes up my game board.

What I have right now works, but the UI elements resize with the window.

My code:

MyGame.java (entry point)

import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.utils.viewport.FitViewport;

public final class MyGame extends Game {
    private int screenWidth;
    private int screenHeight;

    private ShapeRenderer renderer;

    public MyGame(int screenWidth, int screenHeight) {
        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;
    }

    @Override
    public void create() {
        renderer = new ShapeRenderer();

        this.setScreen(
            new MyGameScreen(
                // new FitViewport(screenWidth, screenHeight)
                new FitViewport(640, 480),
                renderer
            )
        );
    }
}

MyGameScreen.java (main application Screen)

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Event;
import com.badlogic.gdx.scenes.scene2d.EventListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
import com.badlogic.gdx.utils.viewport.Viewport;

public final class MyGameScreen extends ScreenAdapter {
    /** Starting sprouts slider minimum value. */
    private static final int SLIDER_MIN = 2;
    /** Starting sprouts slider maximum value. */
    private static final int SLIDER_MAX = 7;
    /** Starting sprouts slider step value. */
    private static final int SLIDER_STEP = 1;

    /** The spacing between the elements in a toolbar. */
    private static final int TOOLBAR_CELL_SPACING = 10;
    /** The padding of the toolbar row. */
    private static final int TOOLBAR_PADDING = 10;
    /** The spacing between the rows of the root layout. */
    private static final float ROOT_ROW_SPACING = 5;
    /** The padding of the root layout. */
    private static final float ROOT_PADDING = 10;

    private final Viewport viewport;
    private final ShapeRenderer renderer;

    private Stage stage;

    public MyGameScreen(Viewport viewport, ShapeRenderer renderer) {
        this.viewport = viewport;
        this.renderer = renderer;
    }

    @Override
    public void show() {
        stage = new Stage(viewport);

        Skin skin =
            new Skin(
                Gdx.files.internal("default/skin/uiskin.json")
            );

        final Label sliderLabel = new Label(""+SLIDER_MIN, skin);
        sliderLabel.setColor(Color.BLACK);
        final Slider slider =
            new Slider(
                SLIDER_MIN,
                SLIDER_MAX,
                SLIDER_STEP,
                false,
                skin
            );
        slider.addListener(new EventListener() {
            @Override
            public boolean handle(final Event event) {
                sliderLabel.setText("" + (int) slider.getValue());
                return true;
            }
        });
        slider.setValue(SLIDER_MIN);

        TextButton resetButton =
            new TextButton(
                "New game",
                skin
            );

        Table toolbar = new Table().pad(TOOLBAR_PADDING);
        toolbar.left().add(resetButton).space(TOOLBAR_CELL_SPACING);
        toolbar.add(sliderLabel).space(TOOLBAR_CELL_SPACING);
        toolbar.add(slider);
        toolbar.setHeight(resetButton.getPrefHeight() + 2 * TOOLBAR_PADDING);


        Rectangle gameBounds =
            new Rectangle(
                ROOT_PADDING,
                ROOT_PADDING,
                stage.getViewport().getWorldWidth() - 2 * ROOT_PADDING,
                stage.getViewport().getWorldHeight()
                - 2 * ROOT_PADDING
                - toolbar.getHeight()
                - ROOT_ROW_SPACING
            );
        Actor gameBoard = new MyGameBoard(new MyGameState(gameBounds), renderer);
        gameBoard.setBounds(
            gameBounds.getX(),
            gameBounds.getY(),
            gameBounds.getWidth(),
            gameBounds.getHeight()
        );

        VerticalGroup rootLayout =
            new VerticalGroup()
                .center()
                .space(ROOT_ROW_SPACING)
                .pad(ROOT_PADDING);
        rootLayout.setFillParent(true);
        rootLayout.grow().addActor(toolbar);
        rootLayout.addActor(gameBoard);

        stage.addActor(rootLayout);

        Gdx.input.setInputProcessor(stage);
    }
    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        stage.getViewport().apply();
        stage.act(Gdx.graphics.getDeltaTime());
        stage.draw();
    }
    @Override
    public void resize(int width, int height) {
        stage.getViewport().update(width, height, true);
    }
}

MyGameBoard.java

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.scenes.scene2d.Actor;

public final class MyGameBoard extends Actor {
    private static final float BORDER_THICKNESS = 5f;
    private static final Color DEFAULT_BACKGROUND_COLOR =
        new Color(204 / 255f, 229 / 255f, 1, 1);

    private final MyGameState gameState;
    private final ShapeRenderer renderer;

    public MyGameBoard(
        final MyGameState gameState,
        final ShapeRenderer renderer
    ) {
        this.gameState = gameState;
        this.renderer = renderer;
    }

    @Override
    public void draw(final Batch batch, final float parentAlpha) {
        batch.end();

        Gdx.gl.glLineWidth(BORDER_THICKNESS);
        renderer.setProjectionMatrix(batch.getProjectionMatrix());
        renderer.begin(ShapeType.Line);
        renderer.setColor(DEFAULT_BACKGROUND_COLOR);
        renderer.rect(getX(), getY(), getWidth(), getHeight());

        // draw my game board state (a single point in the middle of the board)
        renderer.circle(
            gameState.gameBounds.width / 2,
            gameState.gameBounds.height / 2,
            BORDER_THICKNESS
        );
        renderer.end();

        batch.begin();
    }
}

MyGameState.java (sample game board state)

import com.badlogic.gdx.math.Rectangle;

public class MyGameState {
    public Rectangle gameBounds;

    public MyGameState(Rectangle gameBounds) {
        this.gameBounds = gameBounds;
    }
}

I've looked at the following similar questions:

but I simply wasn't able to piece the information provided in each of those questions together into a working solution for my problem.

Janez Kuhar
  • 3,705
  • 4
  • 22
  • 45

0 Answers0