25
\$\begingroup\$

I'm new to libgdx, and I am finding I am getting stumped by the simplest of things. It seems to want me to do things a specific way, but the documentation won't tell me what that is.

I want to make a very simple 2d game in which the player controls a spaceship. The mouse wheel will zoom in and out, and information and controls are displayed on the screen.

But I can't seem to make the mouse wheel NOT zoom the UI. I've tried futzing with the projection matrices in between

Here's my (current) code:

public class PlayStage extends Stage {
...
    public void draw() {
    // tell the camera to update its matrices.
    camera.update();

    // tell the SpriteBatch to render in the
    // coordinate system specified by the camera.
    spriteBatch.setProjectionMatrix(camera.combined);

    spriteBatch.begin();

    aButton.draw(spriteBatch, 1F);

    playerShip.draw(spriteBatch, 1F);

    spriteBatch.end();
}
}

camera.zoom is set by scrolled(int amount).

I've tried about a dozen variations on the theme of changing the camera's projection matrix after the button is drawn but before the ship is, but no matter what I do, the same things happen to both the button and the ship. So:

What is the usual libgdx way of implementing an on-screen UI that isn't transformed by the camera's projection matrix/zoom?

\$\endgroup\$
1
  • \$\begingroup\$ You could use the camera.project(Vector3 screenCoords) to project something from world coordinates to screen cords. \$\endgroup\$
    – Jax
    Commented Sep 8, 2015 at 20:15

5 Answers 5

19
\$\begingroup\$

You can just use another SpriteBatch without setting projection matrix to draw the HUD,

camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
spriteBatch.begin();
aButton.draw(spriteBatch, 1F);
playerShip.draw(spriteBatch, 1F);
spriteBatch.end();

hudBatch.begin();
//Draw using hudBatch
hudBatch.end();
\$\endgroup\$
2
  • 1
    \$\begingroup\$ For what I was doing, I found this to be the simplest approach, for basic UI which should always appear at the same spot on the screen (like scores, lives, etc) \$\endgroup\$
    – Richard
    Commented Aug 26, 2013 at 20:37
  • \$\begingroup\$ Having the spritebatch without projection matrix led to different positioning on different screen resolutions when I tried this \$\endgroup\$
    – codeulike
    Commented Mar 11, 2014 at 0:36
8
\$\begingroup\$

I think the easiest way to accomplish a hud map would be to use a second camera and overlay them. This issue has been discussed on the libgdx forums a while back and I remember someone over there posting their hud code. You may want to poke around over there and see what you come up with.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Yes, this is the right answer. Or use a separate Stage for hud and draw it after game stuff. \$\endgroup\$
    – Matsemann
    Commented Jun 30, 2012 at 4:40
  • 3
    \$\begingroup\$ Thank you. I'll try both. I didn't realize it was common practice to have more than one Stage or Camera per Screen. \$\endgroup\$ Commented Jun 30, 2012 at 17:34
  • 4
    \$\begingroup\$ Can you please add some sample code, or maybe @DevinCarless? \$\endgroup\$
    – ashes999
    Commented Jan 3, 2014 at 22:28
6
\$\begingroup\$

Here is a method I use for working with a single batch:

First draw everything else, then draw your HUD last:

Matrix4 uiMatrix = cam.combined.cpy();
uiMatrix.setToOrtho2D(0, 0, WIDTH, HEIGHT);
batch.setProjectionMatrix(uiMatrix);
batch.begin();

If you want to draw more after, make sure you reset the projection matrix to whatever it needs to be.

\$\endgroup\$
1
  • \$\begingroup\$ Be warned that you'll end up with lot of matrixes in memory very quickly (because .cpu() creates a new matrix every time). Better have a permanent buffer matrix and set its values every frame. \$\endgroup\$ Commented Jun 29, 2017 at 13:26
6
\$\begingroup\$

I have done it like this:

First i have created a new Class called Hud. It implements Disposable because of the resource management. Then you need to define a Stage for your content and a new viewport because a new camera will be used. You need to define a new Tablethat you can add to the Stage. You can add your content to the table like usual. The rest of the code i will put to show how it works is game specific so just ignore it.

public class Hud implements Disposable{

public Stage stage;
private Viewport viewport;

//score && time tracking variables
private Integer worldTimer;
private float timeCount;
private static Integer score;
private boolean timeUp;

//Scene2D Widgets
private Label countdownLabel, timeLabel, linkLabel;
private static Label scoreLabel;

public Hud (SpriteBatch sb){
    //define tracking variables
    worldTimer = 250;
    timeCount = 0;
    score = 0;

    //setup the HUD viewport using a new camera seperate from gamecam
    //define stage using that viewport and games spritebatch
    viewport = new FitViewport(GetTheTriforce.V_WIDTH, GetTheTriforce.V_HEIGHT, new OrthographicCamera());
    stage = new Stage(viewport, sb);

    //define labels using the String, and a Label style consisting of a font and color
    countdownLabel = new Label(String.format("%03d", worldTimer), new Label.LabelStyle(new BitmapFont(), Color.WHITE));
    scoreLabel =new Label(String.format("%06d", score), new Label.LabelStyle(new BitmapFont(), Color.WHITE));
    timeLabel = new Label("LEFTOVER TIME", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
    linkLabel = new Label("POINTS", new Label.LabelStyle(new BitmapFont(), Color.WHITE));

    //define a table used to organize hud's labels
    Table table = new Table();
    table.top();
    table.setFillParent(true);

    //add labels to table, padding the top, and giving them all equal width with expandX
    table.add(linkLabel).expandX().padTop(10);
    table.add(timeLabel).expandX().padTop(10);
    table.row();
    table.add(scoreLabel).expandX();
    table.add(countdownLabel).expandX();

    //add table to the stage
    stage.addActor(table);

}

public void update(float dt){
    timeCount += dt;
    if(timeCount >= 1){
        if (worldTimer > 0) {
            worldTimer--;
        } else {
            timeUp = true;
        }
        countdownLabel.setText(String.format("%03d", worldTimer));
        timeCount = 0;
    }
}

public static void addScore(int value){
    score += value;
    scoreLabel.setText(String.format("%06d", score));
}

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

public boolean isTimeUp() { return timeUp; }


public static Label getScoreLabel() {
    return scoreLabel;
}

public static Integer getScore() {
    return score;
}

}

and then in the Playscreen like this:

first of all you need a variable to reference your hud like:

private Hud hud; 

and then in the constructor you create an new instance of your class:

hud= new Hud(); 

in the update- method you need to put these lines of code since i think you want to display some game information like points or the lives left:

hud.update();

in the render- method do this:

//Set batch to now draw what the Hud camera sees.
game.batch.setProjectionMatrix(hud.stage.getCamera().combined);
hud.stage.draw();

and at the end don't forget to dispose your hud in the dispose- method

i hope that helps

\$\endgroup\$
0
1
\$\begingroup\$

Remember, the first camera is where the background is fixed. The second one is where the ground moves. If you're wondering why the sprite moves in the stage because the secret it the ground that moves. The sprite is just walking in place since you're using a third camera. You might scrolled the second camera's ground but the HUD is still in place on third. Promise, it's true! The third camera is where the it displays the sprite in center for direction, whether he goes left or right by a little animation code, and the HUD (i.e. score, remaining lives, item used). If you need something, ask me.

\$\endgroup\$
1
  • \$\begingroup\$ How can i detach sprite from the second camera? What i want to do is to draw text, but it's to big... So i need another camera, but when i do that the text goes with me.. with the player, with my screen... it does move a bit like the enemy on the screen but it is still stay with me... Can you please advise ? \$\endgroup\$
    – adam
    Commented Jun 22, 2017 at 20:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .