3
\$\begingroup\$

I have made a Java clone of the popular 2048 game. It works, but I drew the UI in the correct way.

Images so you know what we're discussing:

enter image description hereenter image description here

I'm basically not sure how to do this, whether I should use Skin, Sprite, Actors like Button and Label, or simply draw textures to the screen.

For the left picture, I did the top half of the screen, the header, like this:

public class GameHeader extends Group {
private BitmapFont bigFont;
private BitmapFont smallFont, smallBoltFont;
private int GAME_MARGIN = (TotallyNot2048.SCREEN_WIDTH - WIDTH) / 2;
private GlyphLayout layout = new GlyphLayout();
private Texture scoreBackground;
private int score, best;
private Preferences prefs;
private Game game;
private Rectangle menuRect, newRect;

public GameHeader(Game game, AssetManager assetManager) {
    setBounds(GAME_MARGIN, HEIGHT + GAME_MARGIN, WIDTH, SCREEN_HEIGHT - HEIGHT);
    this.game = game;

    score = 0;

    prefs = Gdx.app.getPreferences("HighScore");
    prefs.flush();

    best = prefs.getInteger("highScore");

    bigFont = assetManager.get("bigFont.ttf", BitmapFont.class);
    smallFont = assetManager.get("smallFont.ttf", BitmapFont.class);
    smallBoltFont = assetManager.get("smallBoltFont.ttf", BitmapFont.class);

    scoreBackground = Helper.createRoundedRectangleTexture(WIDTH / 4.2f, smallFont.getLineHeight() * 2, new Color(187f / 256f, 173f / 256f, 160f / 256f, 1f), 3);

    menuRect = new Rectangle(GAME_MARGIN + WIDTH - 80, HEIGHT + GAME_MARGIN * 2, 80, 40);
    newRect = new Rectangle(GAME_MARGIN + WIDTH - 170, HEIGHT + GAME_MARGIN * 2, 80, 40);

    listenForInput();
}

private void listenForInput() {
    addListener(new InputListener() {

        @Override
        public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
            if (pointer == Input.Buttons.LEFT) {
                if (menuRect.contains(TotallyNot2048.getMousePosInGame().x, TotallyNot2048.getMousePosInGame().y)) {
                    ((GameScreen) game.getScreen()).toTitleScreen();
                }

                if (newRect.contains(TotallyNot2048.getMousePosInGame().x, TotallyNot2048.getMousePosInGame().y)) {
                    ((GameScreen) game.getScreen()).restartGame();
                }
            }
            return true;
        }
    });
}

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

    layout.setText(bigFont, "2048");
    bigFont.setColor(new Color(120f / 256f, 110f / 256f, 100f / 256f, 1f));
    bigFont.draw(batch, "2048", 0, SCREEN_HEIGHT - layout.height - GAME_MARGIN);

    layout.setText(smallFont, "Join the numbers and get to the ");
    smallFont.setColor(new Color(120f / 256f, 110f / 256f, 100f / 256f, 1f));
    smallFont.draw(batch, "Join the numbers and get to the ", 0, HEIGHT + GAME_MARGIN);

    smallBoltFont.setColor(new Color(120f / 256f, 110f / 256f, 100f / 256f, 1f));
    smallBoltFont.draw(batch, "2048 tile!", layout.width, HEIGHT + GAME_MARGIN);

    batch.draw(scoreBackground, WIDTH - scoreBackground.getWidth() * 2.05f, SCREEN_HEIGHT - GAME_MARGIN * 2.5f);
    batch.draw(scoreBackground, WIDTH - scoreBackground.getWidth(), SCREEN_HEIGHT - GAME_MARGIN * 2.5f);

    layout.setText(smallBoltFont, "SCORE");
    smallBoltFont.setColor(new Color(242f / 256f, 228f / 256f, 217f / 256f, 1f));
    smallBoltFont.draw(batch, "SCORE", WIDTH - scoreBackground.getWidth() * 2.05f + scoreBackground.getWidth() / 2 - layout.width / 2, SCREEN_HEIGHT - GAME_MARGIN * 2.5f + scoreBackground.getHeight() * .8f);
    layout.setText(smallBoltFont, "BEST");
    smallBoltFont.draw(batch, "BEST",  WIDTH - scoreBackground.getWidth() + scoreBackground.getWidth() / 2 - layout.width / 2, SCREEN_HEIGHT - GAME_MARGIN * 2.5f + scoreBackground.getHeight() * .8f);

    layout.setText(smallBoltFont, Integer.toString(score));
    smallBoltFont.setColor(Color.WHITE);
    smallBoltFont.draw(batch, Integer.toString(score), WIDTH - scoreBackground.getWidth() * 2.05f + scoreBackground.getWidth() / 2 - layout.width / 2, SCREEN_HEIGHT - GAME_MARGIN * 2.5f + scoreBackground.getHeight() * .4f);
    layout.setText(smallBoltFont, Integer.toString(best));
    smallBoltFont.draw(batch, Integer.toString(best), WIDTH - scoreBackground.getWidth() + scoreBackground.getWidth() / 2 - layout.width / 2, SCREEN_HEIGHT - GAME_MARGIN * 2.5f + scoreBackground.getHeight() * .4f);


    batch.draw(scoreBackground, WIDTH - 80, HEIGHT + GAME_MARGIN, 80, 40);

    layout.setText(smallBoltFont, "Menu");
    smallBoltFont.draw(batch, "Menu", WIDTH - 80 / 2 - layout.width / 2, HEIGHT + GAME_MARGIN + 40 / 2 + layout.height / 2);

    batch.draw(scoreBackground, WIDTH - 170, HEIGHT + GAME_MARGIN, 80, 40);

    layout.setText(smallBoltFont, "New");
    smallBoltFont.draw(batch, "New", WIDTH - 80 / 2 - 90 - layout.width / 2, HEIGHT + GAME_MARGIN + 40 / 2 + layout.height / 2);

}
}

I do a lot of calculations in the draw method here, and I don't think that's a good idea. I also use a lot of "magic numbers", but I think that would be fixed too if I used something else instead of just drawing everything like this. The menu and new buttons are clickable.

On the title screen (right image) I draw in a completely different way:

package com.thecodingwombat.screens;

public class TitleScreen implements Screen {
private Stage stage;
private GameScreen[] gameScreens;
private TitleScreen titleScreen;
private Game game;
private AssetManager assetManager;
private Image board;
private GlyphLayout layout = new GlyphLayout();
private String[] boardDescriptions;
private Texture[] boardPreviews;
private int currentBoardIndex = 1;
private int[] boardSizes;
private BitmapFont font;
private Label boardDescriptionLabel;

public TitleScreen(Game game, AssetManager assetManager) {
    this.game = game;
    this.assetManager = assetManager;
    this.titleScreen = this;
    stage = new Stage(new ScreenViewport());

    font = assetManager.get("smallerRegularFont.ttf", BitmapFont.class);
    Texture board3 = assetManager.get("board_preview_3.png", Texture.class);
    Texture board4 = assetManager.get("board_preview_4.png", Texture.class);
    Texture board5 = assetManager.get("board_preview_5.png", Texture.class);
    Texture board6 = assetManager.get("board_preview_6.png", Texture.class);
    Texture board8 = assetManager.get("board_preview_8.png", Texture.class);

    createTitle();
    createButtons();

    boardDescriptions = new String[] {"Small - 3x3", "Classic - 4x4", "Big - 5x5", "Bigger - 6x6", "Huge - 8x8"};
    boardSizes = new int[] {3, 4, 5, 6, 8};
    gameScreens = new GameScreen[5];
    boardPreviews = new Texture[] {board3, board4, board5, board6, board8};

    board = new Image(boardPreviews[1]);
    board.setBounds(TotallyNot2048.SCREEN_WIDTH / 2 - 300 / 2, TotallyNot2048.SCREEN_HEIGHT / 3 + layout.height * 1.5f, 300, 300);
    stage.addActor(board);

    boardDescriptionLabel = new Label(boardDescriptions[1], new Label.LabelStyle(font, new Color(.2f, .2f, .2f, 1f)));
    boardDescriptionLabel.setBounds(TotallyNot2048.SCREEN_WIDTH / 2 - 150, TotallyNot2048.SCREEN_HEIGHT / 3, 300, font.getCapHeight());
    boardDescriptionLabel.setAlignment(Align.center);

    stage.addActor(boardDescriptionLabel);
}

private void createButtons() {
    TextButton.TextButtonStyle button1Style = new TextButton.TextButtonStyle();
    button1Style.font = font;
    button1Style.fontColor = new Color(.2f, .2f, .2f, 1f);

    layout.setText(font, "Start Game");

    TextButton play = new TextButton("Start Game", button1Style);
    Image leftButton = new Image(assetManager.get("leftButton.png", Texture.class));
    Image rightButton = new Image(assetManager.get("rightButton.png", Texture.class));

    play.setBounds(TotallyNot2048.SCREEN_WIDTH / 2 - layout.width * .75f, TotallyNot2048.SCREEN_HEIGHT / 3 - layout.height * 3, layout.width * 1.5f, layout.height * 2);
    leftButton.setBounds(TotallyNot2048.SCREEN_WIDTH / 2 - 300 / 2 - layout.height * 1.5f, TotallyNot2048.SCREEN_HEIGHT / 3 - layout.height * .2f, layout.height * 1.5f, layout.height * 1.5f);
    rightButton.setBounds(TotallyNot2048.SCREEN_WIDTH / 2 + 300 / 2, TotallyNot2048.SCREEN_HEIGHT / 3 - layout.height * .2f, layout.height * 1.5f, layout.height * 1.5f);

    stage.addActor(play);
    stage.addActor(leftButton);
    stage.addActor(rightButton);

    play.addListener(new InputListener() {
        @Override
        public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
            if (gameScreens[currentBoardIndex] == null)
                gameScreens[currentBoardIndex] = new GameScreen(game, titleScreen, assetManager, boardSizes[currentBoardIndex]);
            game.setScreen(gameScreens[currentBoardIndex]);
            gameScreens[currentBoardIndex].setFontSize();
            return true;
        }
    });

    leftButton.addListener(new InputListener() {
        @Override
        public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
            if (currentBoardIndex == 0)
                currentBoardIndex = 4;
            else
                currentBoardIndex--;
            boardDescriptionLabel.setText(boardDescriptions[currentBoardIndex]);
            board.setDrawable(new TextureRegionDrawable(new TextureRegion(boardPreviews[currentBoardIndex])));
            return true;
        }
    });

    rightButton.addListener(new InputListener() {
        @Override
        public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
            if (currentBoardIndex == 4)
                currentBoardIndex = 0;
            else
                currentBoardIndex++;
            boardDescriptionLabel.setText(boardDescriptions[currentBoardIndex]);
            board.setDrawable(new TextureRegionDrawable(new TextureRegion(boardPreviews[currentBoardIndex])));
            return true;
        }
    });
}

private void createTitle() {
    Label.LabelStyle label1Style = new Label.LabelStyle();
    label1Style.font = assetManager.get("bigFont.ttf", BitmapFont.class);
    label1Style.fontColor = new Color(.2f, .2f, .2f, 1f);
    Label title = new Label("2048", label1Style);
    title.setAlignment(Align.center);
    title.setBounds(0, Gdx.graphics.getHeight() * .85f, Gdx.graphics.getWidth(), 0);
    stage.addActor(title);
}

@Override
public void show() {
    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.act();
    stage.draw();
}
}

Here I use Button and Label, is this better? And did I implement it correctly? I don't yet know how to add styling to those, like backgrounds yet though. In the title screen (the latter code block) I also do user input in a different way because I use the build in listeners for the Buttons. Is the latter also better for handling clickable things?

I added fonts in my main class in an AssetManager and gave the manager to all other classes. Am I supposed to do that?

\$\endgroup\$
  • \$\begingroup\$ Mr Wombat what is your question: What are you looking to improve here - it wasn't altogether clear when reading it? \$\endgroup\$ – BKSpurgeon Apr 25 '17 at 9:12
  • \$\begingroup\$ I'd like to know what libgdx drawing/rendering features to use in different situations, among which the situations I illustrated. So should I use the Button and Label classes for example, and use Skin? So for my situations, drawing a simple UI for the title screen and the header of the game, what libgdx functionalities should I use and how to implement them? \$\endgroup\$ – The Coding Wombat Apr 25 '17 at 11:41
  • \$\begingroup\$ Mr Wombat given my current state of knowledge of the libgdx (which is zilch atm) - i'm afraid i can't offer (any) useful suggestions. \$\endgroup\$ – BKSpurgeon Apr 25 '17 at 13:57
  • \$\begingroup\$ BKSpurgeon, do you maybe know people who do? \$\endgroup\$ – The Coding Wombat Apr 25 '17 at 18:07
  • \$\begingroup\$ try here: badlogicgames.com/forum \$\endgroup\$ – BKSpurgeon Apr 25 '17 at 22:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.