Creating a basic menu screen using tables

Until now, this chapter's recipes have been oriented to cover an enormous cut in the Scene2D API. From now on, you will find real cases to put the already explained concepts into practice with a few additional extras.

Almost every game has a main menu screen to allow the player to flow across the application. As it works as a facade, it must be neat so as not to give a bad first impression.

A very simple menu example will be explained in this recipe, but it can really serve you as a quick template to customize your own menu in a few minutes. To maintain chapter cohesion, it will make use of actions and some selected widgets.

Getting ready

This recipe is not an exception, so please import the sample projects into your Eclipse workspace. The source code for this recipe lives in the MainMenuSample.java file. It is a good idea to tweak that code at the same time that you learn the upcoming new concepts.

How to do it…

The content of this sample is now described step by step:

  1. First of all, decide which components will appear in your menu screen. This example will contain the game title, some pictures of the main character, three buttons, and a slider to change the volume, as shown in the following screenshot:
    How to do it…
  2. Once you have your mental (or drawn) distribution of the screen space, declare your actors:
    private Image gameTitle, hamsty1, hamsty2;
    private TextButton btnPlay, btnSettings, btnExit;
    private Slider slider;
  3. Apart from visual components, you will also need some structural ones to organize and encapsulate your actors:
    private Table table;
    private Stage stage;
  4. Within your onCreate() method, place the typical viewport stuff:
    viewport = new FitViewport(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
  5. Select your preferred font type:
    BitmapFont font = new BitmapFont(Gdx.files.internal("data/font.fnt"));
  6. Choose the images for the title and the main character. Note that an animation might be more suitable to get the player's attention, but for simplicity reasons, we will just use static images. Remember to dispose them all when the application exits:
    Texture gameTitleTex = new Texture (Gdx.files.internal("data/gameTitle.png"));
    Texture hamsty1Tex = new Texture (Gdx.files.internal("data/hamsty.png"));
    Texture hamsty2Tex = new Texture (Gdx.files.internal("data/hamsty2.png"));
    
    gameTitle = new Image(new TextureRegionDrawable(new TextureRegion(gameTitleTex)));
    hamsty1 = new Image(new TextureRegionDrawable(new TextureRegion(hamsty1Tex)));
    hamsty2 = new Image(new TextureRegionDrawable(new TextureRegion(hamsty2Tex)));
  7. Define a common TextButtonStyle for all three buttons and instantiate them:
    buttonUpTex = new Texture( Gdx.files.internal("data/scene2d/myactor.png"));
    buttonOverTex = new Texture( Gdx.files.internal("data/scene2d/myactorOver.png"));
    buttonDownTex = new Texture( Gdx.files.internal("data/scene2d/myactorDown.png"));
    
    TextButton.TextButtonStyle tbs = new TextButton.TextButtonStyle();
    tbs.font = font;
    tbs.up = new TextureRegionDrawable(new TextureRegion(buttonUpTex));
    tbs.over = new TextureRegionDrawable(new TextureRegion(buttonOverTex));
    tbs.down = new TextureRegionDrawable(new TextureRegion(buttonDownTex));
    
    btnPlay = new TextButton("PLAY", tbs);
    btnSettings = new TextButton("SETTINGS", tbs);
    btnExit = new TextButton("EXIT", tbs);
  8. Repeat the process with Slider:
    Slider.SliderStyle ss = new Slider.SliderStyle();
    ss.background = new TextureRegionDrawable(new TextureRegion(new Texture(Gdx.files.internal("data/slider_background.png"))));
    ss.knob = new TextureRegionDrawable(new TextureRegion(new Texture(Gdx.files.internal("data/slider_knob.png"))));
    
    slider = new Slider(0f, 100f, 1f, false, ss);
  9. Instantiate the Table actor, which plays a key role in this recipe:
    table = new Table();
  10. Insert your first row, which will contain the game title. Don't worry about the details of the code because they will be explained in the upcoming How it works… section:
    table.row();
    table.add(gameTitle).padTop(30f).colspan(2).expand();
  11. Add new rows to insert the remaining content. Pay special attention to the difference between add() and addActor(), as when using the former, the actors' position, size, and so on will be ignored when it is put inside a table:
    table.row();
    table.add(hamsty).padTop(10f).expandY().uniform();
    table.add(hamsty2).padTop(10f).expandY().uniform();
    table.row();
    table.add(btnPlay).padTop(10f).colspan(2);
    table.row();
    table.add(btnSettings).padTop(10f).colspan(2);
    table.row();
    table.add(btnExit).padTop(10f).colspan(2);
    table.row();
    table.add(slider).bottom().colspan(2).expandY();
    table.padBottom(30f);
  12. Size the table to the stage:
    table.setFillParent(true);
  13. Pack our structural component to fit the bounds of its content:
    table.pack();
  14. One advantage of Tables is that they work as a group so that you can apply any Action over the whole set. Let's try a fadeIn animation, so the first thing is to hide the table by setting its transparency to 0:
    table.getColor().a = 0f;
    
    table.addAction(fadeIn(2f));
  15. You can handle the user's interaction with the buttons as follows:
    Gdx.input.setInputProcessor(stage);
    
    // Play button listener
    btnPlay.addListener( new ClickListener() {
      @Override
      public void clicked(InputEvent event, float x, float y) {
        Gdx.app.log(TAG, "PLAY");
      };
    });

    There are many types of InputEvent. Take a look at the Libgdx official documentation at http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.Type.html to explore them.

  16. Do the same with Slider:
    slider.addListener(new InputListener() {
      @Override
      public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
        Gdx.app.log(TAG, "slider changed to: " + slider.getValue());
        // Set volume to slider.getValue();
      }
      @Override
      public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
        return true;
      };
    });

    If touchDown returns true, the listener is ready to receive touchUp and touchDragged events until the first one is executed. It will also prevent the event from feeding other listeners outside the stage.

  17. To complete the onCreate() method, add the table to the stage:
    stage.addActor(table);
  18. Finally, update our stage and draw it on the render method:
    stage.act(Gdx.graphics.getDeltaTime());
    stage.draw();
  19. Last but not least, remember to dispose the stage within the dispose() method:
    public void dispose() {
      batch.dispose();
      font.dispose();
      stage.dispose();
    }

How it works…

As mentioned earlier, Table becomes a really powerful and essential actor in the Scene2D ecosystem. It is even present internally on several of the other actors. It is mainly responsible for organizing the screen space to fit your needs without too much hardcoding. In this section, you will learn how to deal with this old enemy of UIs through the Libgdx interface.

Table is composed of Cells, which have a lot of interesting and tweakable properties related to space treatment.

Expand

The first way for a cell to occupy the space is by expanding its logical table's bounds up to the main table's bounds (stage). You will understand this quickly with the following screenshot:

Expand

You can carry this out using these methods: expand(), expandX(), and expandY(), which only differ on the axis they affect.

In case we decide to avoid the expansion, we will get the following screenshot:

Expand

As you can see, the cells of the table are not expanded to the viewport limits but to the widest column, which is the one that contains the game title.

Note

If you have Box2DDebugRenderer enabled, you will see red, blue, and green lines. The red ones are the cell bounds, the blue ones are the stage limits, and the green ones delimit the space that a widget takes up.

Fill

We might want the content of a cell to fill its parent so the buttons become bigger over the x axis, just like the following screenshot:

Fill

Similar to the expand action, we can achieve this through fill(), fillX(), and fillY().

Note

Note that stretching looks best when you use the NinePatch class.

Take into account that fill methods are limited to the widget's maximum size. See the upcoming Sizing section in order to query and modify that size.

Uniform

In order to keep cells the same size, make use of the uniform() method. For instance, the two cells that contain a hamster image can be written as:

table.add(hamsty1).padTop(10f).expandY().uniform();
table.add(hamsty2).padTop(10f).expandY().uniform();

Sizing

You can also change a cell's width through minWidth(), prefWidth(), and maxWidth(), whose combined effect can be achieved with the width() method.

By default, a table takes the preferred width of a widget. But if it does not fit to a cell bound, they are scaled down to a value between its preferred size and its minimum size. If the widget does not fit yet with its minimum size, it might overlap with some other elements of the scene.

A widget will never take up a bigger space than its maximum width, not even through fill methods. This is the same with the height. You can factor width and height functions with the size() method. Cells or parent widgets may determine the size of child widgets according to the available space.

Padding and spacing

Both padding and spacing allow the designer to insert extra space around the edges of a cell, but the particularity is that padding will join adjacent spaces while spacing will take the biggest value. The following diagram will help to make things clearer:

Padding and spacing

Some of the methods are space(), spaceTop(), padBottom(), padLeft(), and so on.

Alignment

You can align a widget within the cell itself by calling the bottom(), right(), left(), and top() methods.

Columns and rows

Whenever you want to have multiple columns, make use of the colspan() method. It receives the number of columns that a cell will take up as the argument.

The next screenshot shows a practical case:

Columns and rows

As you can see, each hamster's cell occupies one column, whereas the game title's cell occupies just one. However, the game title column takes up the space of two columns. So, all we have to do is order the game title cell to span two columns:

table.add(gameTitle).colspan(2);

The next step is to add a new row. This can be carried out through the row() method:

table.row();

Finally, set the hamsters' cells to span just one column:

table.add(hamsty1).colspan(1);
table.add(hamsty2).colspan(1);

Or simply, do not specify any colspan because it will take one column by default.

Note

You might need a parallel method to get the same behavior in rows. It is not included, but you can still get it using nested tables.

Defaults

It is possible to set common attributes to all cells by default through the table.defaults() method. This gives you the chance of instantly applying any height, width, alignment, expansion, and more.

You can also do it only over columns with table.columnDefault(numCol) or over rows with table.row().

There's more…

The Using ShapeRenderer for debug graphics recipe in Chapter 2, Working with 2D Graphics, explains some really powerful and useful debug tools. However, Scene2D comes with its own debug system that really helps to get the desired UI.

All you need to do is make use of the following function call after instantiating a Table instance:

table.debug(); //Enables debug

Note

The debug() method can be applied generically (as shown in the previous example) or over tables, cells, or all widgets contained in the table itself.

Apart from debug utilities, there are some more features to enrich your table and consequently your UI, so I recommend that you take a walk through the table API.

See also

  • Right now, you are very close to a real implementation of a UI in Libgdx, but it still lacks skin customization, so feel free to carry on and feed your neurons with tasty features in the Skin customization recipe.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.51.228