6

I have made an alpha version of my rpg and now I want to implement a menu with multiple tabs. When you are in game and press tab or escape I want to show a user interface that will have a bar top with 3 buttons like main menu, skill tree and inventory. The problem is that when you switch tab I need to stop drawing the previously selected tab and disable the actors from it so when you click something from the current tab it doesn't also click a button on a tab that isn't being drawn but it is there... How can I disable actors, buttons so I can draw a new tab without worrying about overlapping the current tab with the previous one?

EDIT: I want to avoid making multiple stages. I would like to have a single stage on which every tab is defined.

Guillaume
  • 5,535
  • 1
  • 24
  • 30
Andrew
  • 1,109
  • 1
  • 15
  • 27

2 Answers2

14

Invisible actors don't receive touch events, making the easiest way to accomplish a tabbed interface simply setting the visibility of the tab content actors appropriately.

Here's how I would implement a tabbed interface in libgdx:

package com.badlogic.gdx.tests.lwjgl;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.ButtonGroup;
import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Stack;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

class Game3 extends ApplicationAdapter {

    Skin skin;
    Stage stage;

    @Override
    public void create () {
        skin = new Skin(Gdx.files.internal("data/uiskin.json"));
        stage = new Stage(new ScreenViewport());

        Table main = new Table();
        main.setFillParent(true);

        // Create the tab buttons
        HorizontalGroup group = new HorizontalGroup();      
        final Button tab1 = new TextButton("Tab1", skin, "toggle");
        final Button tab2 = new TextButton("Tab2", skin, "toggle");
        final Button tab3 = new TextButton("Tab3", skin, "toggle");
        group.addActor(tab1);
        group.addActor(tab2);
        group.addActor(tab3);
        main.add(group);
        main.row();

        // Create the tab content. Just using images here for simplicity.
        Stack content = new Stack();
        final Image content1 = new Image(skin.newDrawable("white", 1,0,0,1)); 
        final Image content2 = new Image(skin.newDrawable("white", 0,1,0,1));
        final Image content3 = new Image(skin.newDrawable("white", 0,0,1,1));   
        content.addActor(content1);
        content.addActor(content2);
        content.addActor(content3);

        main.add(content).expand().fill();

        // Listen to changes in the tab button checked states
        // Set visibility of the tab content to match the checked state
        ChangeListener tab_listener = new ChangeListener(){
            @Override
            public void changed (ChangeEvent event, Actor actor) {
                content1.setVisible(tab1.isChecked());
                content2.setVisible(tab2.isChecked());
                content3.setVisible(tab3.isChecked());
            }
        };
        tab1.addListener(tab_listener);
        tab2.addListener(tab_listener);
        tab3.addListener(tab_listener);

        // Let only one tab button be checked at a time
        ButtonGroup tabs = new ButtonGroup();
        tabs.setMinCheckCount(1);
        tabs.setMaxCheckCount(1);
        tabs.add(tab1);
        tabs.add(tab2);
        tabs.add(tab3);

        stage.addActor(main);       
        Gdx.input.setInputProcessor(stage);
    }

    @Override
    public void render () {
        stage.act();
        stage.draw();
    }

    @Override
    public void dispose () {
        stage.dispose();
        skin.dispose();
    }

    public static void main (String[] args) {
        new LwjglApplication(new Game3(), new LwjglApplicationConfiguration());
    }
}
nEx.Software
  • 6,782
  • 1
  • 26
  • 34
0
  • The best approach for handling menu is to use different Screen.
    1. Easy to manage. Separate classes. Separation of concerns.
    2. Simplified lifecycle management. Pause one screen when another is running.
    3. Other screen is hidden :(

  • If you want to keep same screen, simplest way would be different stages.
    1. Just disable inputlistener for previous stage and stop calling update on frozen stage.
    2. Manual management :(

  • If you want to use same stage, entire logic has to be implemented by you as you denied use of framework defined classes.
    1. Fully customizable.
    2. Difficult to manage. (Unnecessary testing required. Libgdx library classes are tested.)
    3. Maybe fun to implement.

How to do it:

  • Use different parent top level groups for menu and game objects.
  • Manually disable event handling for one group at a time and stop updating it.
  • May require overriding few methods in Stage class. (Not recommended).

Usually HUD's and pause menus are implemented in different stages.

I want to avoid making multiple stages

This must have come from "stages are heavy".

On the contrary, stages are not heavy but spritebatches are heavy.

If you make multiple stages which share a single spritebatch, you can easily create multiple stages without much overhead.

Hope this helps.
Good luck.

Tanmay Patil
  • 6,882
  • 2
  • 25
  • 45
  • I was thinking about using different screens, but then when I implement the skill tree I have to link the player object so I can update the stats from the skills and after I also need to change screen every time you want to see the skill tree. About using multiple stages and same sprite batch that might be a good idea, but if I want to add a batch argument I also need to provide a view-port argument which isn't something that I want... Thanks for the ideas though. Maybe I will try to implement both one stage and multiple stages and see which one is more suited for my game. – Andrew Jun 09 '14 at 15:16