Drag and Drop Inventory with LibGDX (Part II)

In this part II of our inventory tutorial series we are going to create the actual UI for our Inventory and Slots. LibGDX offers us some basic functionality for UIs, namely scene2d.ui. In case you are not familiar with it yet, you should probably read this article first.

Again: the full source code can be found here.

The SlotActor

In part I of this series, we have seen how a Slot looks like. Now we have to render this slot somehow. To do so, we are going to create a SlotActor which is an ImageButton itself, and which displays the TextureRegion of the Item.

public class SlotActor extends ImageButton implements SlotListener {

    private Slot slot;

    private Skin skin;

    public SlotActor(Skin skin, Slot slot) {
        super(createStyle(skin, slot));
        this.slot = slot;
        this.skin = skin;

        // this actor has to be notified when the slot itself changes
        slot.addListener(this);

        // ignore this for now, it will be explained in part IV
        SlotTooltip tooltip = new SlotTooltip(slot, skin);
        InventoryScreen.stage.addActor(tooltip);
        addListener(new TooltipListener(tooltip, true));
    }

    /**
     * This will create a new style for our image button, with the correct image for the item type.
     */
    private static ImageButtonStyle createStyle(Skin skin, Slot slot) {
        TextureAtlas icons = LibgdxUtils.assets.get("icons/icons.atlas", TextureAtlas.class);
        TextureRegion image;
        if (slot.getItem() != null) {
            image = icons.findRegion(slot.getItem().getTextureRegion());
        } else {
            // we have a special "empty" region in our atlas file, which is just black
            image = icons.findRegion("nothing");
        }
        ImageButtonStyle style = new ImageButtonStyle(skin.get(ButtonStyle.class));
        style.imageUp = new TextureRegionDrawable(image);
        style.imageDown = new TextureRegionDrawable(image);

        return style;
    }

    @Override
    public void hasChanged(Slot slot) {
        // when the slot changes, we switch the icon via a new style
        setStyle(createStyle(skin, slot));
    }

    public Slot getSlot() {
        return slot;
    }

}

As always, read the code carefully and also check the comments within. It should be pretty self-explanatory.

The InventoryActor

To display the Inventory, we are going to create a new Window which can be dragged around by default. For each Slot of the Inventory, we are going to create a new SlotActor, which we’ve just seen in the previous paragraph.

public class InventoryActor extends Window {

    public InventoryActor(Inventory inventory, DragAndDrop dragAndDrop, Skin skin) {
        super("Inventory", skin);

        // add an "X" button to the top right of the window, and make it hide the inventory
        TextButton closeButton = new TextButton("X", skin);
        closeButton.addListener(new HidingClickListener(this));
        getButtonTable().add(closeButton).height(getPadTop());

        // basic layout
        setPosition(400, 100);
        defaults().space(8);
        row().fill().expandX();

        // run through all slots and create SlotActors for each
        int i = 0;
        for (Slot slot : inventory.getSlots()) {
            SlotActor slotActor = new SlotActor(skin, slot);
            add(slotActor);

            // this can be ignored for now and will be explained in part III
            dragAndDrop.addSource(new SlotSource(slotActor));
            dragAndDrop.addTarget(new SlotTarget(slotActor));

            i++;
            // every 5 cells, we are going to jump to a new row
            if (i % 5 == 0) {
                row();
            }
        }

        pack();

        // it is hidden by default
        setVisible(false);
    }
}

Closing the inventory

This is actually pretty easy. In the previous code you could already see that we added a HidingClickListener to the button to close the window. Its only purpose is to hide a given actor on click.

public class HidingClickListener extends ClickListener {

    private Actor actor;

    public HidingClickListener(Actor actor) {
        this.actor = actor;
    }

    @Override
    public void clicked(InputEvent event, float x, float y) {
        actor.setVisible(false);
    }

}

The inventory screen

Now that we’ve got the nessary Actors to display our UI, we actually need a Screen with a Stage to attach those actors to.

public class InventoryScreen implements Screen {

    private InventoryActor inventoryActor;

    public static Stage stage;

    @Override
    public void show() {
        // create the stage and make it receive all input
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);

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

        DragAndDrop dragAndDrop = new DragAndDrop();
        inventoryActor = new InventoryActor(new Inventory(), dragAndDrop, skin);
        stage.addActor(inventoryActor);
    }

    @Override
    public void render(float delta) {
        // the screen will have a dark grey background colour
        Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 0f);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

        // show the inventory when any key is pressed
        if (Gdx.input.isKeyPressed(Input.Keys.ANY_KEY)) {
            inventoryActor.setVisible(true);
        }

        // handle all inputs and draw the whole UI
        stage.act(delta);
        stage.draw();
    }

    @Override
    public void resize(int width, int height) {
        stage.getViewport().update(width, height, true);
    }

    @Override
    public void hide() {
        Gdx.input.setInputProcessor(null);
        dispose();
    }

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

}

Next

In this part, we have created a basic UI for our inventory and slots. But we cannot do much with it yet. In the next part we will see how to make that inventory interactive by adding drag and drop functionality!

Leave a Reply

Your email address will not be published. Required fields are marked *