Jak zrobić ekranowy HUD w libgdx?

23

Jestem nowy w libgdx i odkrywam, że ogłuszają mnie najprostsze rzeczy. Wydaje się, że chcę, abym robił rzeczy w określony sposób, ale dokumentacja nie mówi mi, co to jest.

Chcę stworzyć bardzo prostą grę 2D, w której gracz kontroluje statek kosmiczny. Kółko myszy będzie powiększać i pomniejszać, a informacje i elementy sterujące są wyświetlane na ekranie.

Ale nie mogę sprawić, by kółko myszy NIE powiększało interfejsu użytkownika. Próbowałem daremnie z matrycami projekcyjnymi pomiędzy nimi

Oto mój (aktualny) kod:

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 ustawia się za pomocą przewijania (liczba całkowita).

Próbowałem około tuzina wariantów zmiany matrycy projekcji kamery po narysowaniu przycisku, ale przed wypłynięciem statku, ale bez względu na to, co robię, to samo dzieje się zarówno z przyciskiem, jak i ze statkiem. Więc:

Jaki jest typowy sposób implementacji interfejsu użytkownika libgdx na ekranie, który nie jest transformowany przez matrycę / powiększenie kamery?

Devin Carless
źródło
Możesz użyć camera.project(Vector3 screenCoords)do projekcji czegoś od współrzędnych świata do ekranów przewodów.
JDSweetBeat

Odpowiedzi:

17

Możesz po prostu użyć innego SpriteBatch bez ustawiania macierzy projekcji, aby narysować 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();
lookx2
źródło
1
Dla tego, co robiłem, uznałem, że jest to najprostsze podejście, dla podstawowego interfejsu użytkownika, który powinien zawsze pojawiać się w tym samym miejscu na ekranie (np. Partytury, koncerty itp.)
Richard
Posiadanie spritebatch bez matrycy projekcji doprowadziło do różnych pozycjonowania na różnych rozdzielczościach ekranu, kiedy próbowałem tego
codeulike
8

Myślę, że najłatwiejszym sposobem na stworzenie mapy hud byłoby użycie drugiej kamery i nałożenie ich na siebie. Ten problem był omawiany na forach libgdx jakiś czas temu i pamiętam, że ktoś tam opublikował swój kod. Możesz zajrzeć tam i zobaczyć, co wymyślisz.

Chuck D.
źródło
2
Tak, to właściwa odpowiedź. Lub użyj osobnego stołu montażowego do rysowania i narysuj go po zakończeniu gry.
Matsemann
3
Dziękuję Ci. Spróbuję obu. Nie zdawałem sobie sprawy, że powszechną praktyką jest posiadanie więcej niż jednego stołu montażowego lub kamery na ekran.
Devin Carless
4
Czy możesz dodać przykładowy kod, a może @DevinCarless?
ashes999
6

Oto metoda, której używam do pracy z jedną partią:

Najpierw narysuj wszystko inne, a następnie narysuj HUD na końcu:

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

Jeśli chcesz narysować więcej po, pamiętaj, aby zresetować matrycę projekcji do tego, co musi być.

talloaktrees
źródło
Ostrzegamy, że bardzo szybko skończy się wiele macierzy w pamięci (ponieważ .cpu()za każdym razem tworzy nową macierz). Lepiej mieć stałą macierz buforową i ustawiać jej wartości co klatkę.
Denis Kniazhev
1

Pamiętaj, że pierwsza kamera jest tam, gdzie ustalone jest tło. Drugi to ruch ziemi. Jeśli zastanawiasz się, dlaczego duszek porusza się na scenie, ponieważ sekretem jest ziemia, która się porusza. Duszek po prostu chodzi w miejscu, ponieważ używasz trzeciego aparatu. Możesz przewijać ziemię drugiej kamery, ale HUD jest nadal na miejscu na trzeciej. Obietnica, to prawda! Trzecia kamera wyświetla miejsce, w którym wyświetla duszka w środku kierunku, niezależnie od tego, czy porusza się w lewo, czy w prawo za pomocą małego kodu animacji, a także HUD (tj. Wynik, pozostałe życia, użyty przedmiot). Jeśli czegoś potrzebujesz, zapytaj mnie.

David Dimalanta
źródło
Jak mogę odłączyć duszka od drugiego aparatu? Chcę narysować tekst, ale jest za duży ... Potrzebuję innej kamery, ale kiedy to robię, tekst idzie ze mną .. z odtwarzaczem, z ekranem ... porusza się trochę jak wróg na ekranie, ale wciąż pozostaje ze mną ... Czy możesz mi doradzić?
Nimitack
1

Zrobiłem to w ten sposób:

Najpierw stworzyłem nową klasę o nazwie Hud. Implementuje Disposableze względu na zarządzanie zasobami. Następnie musisz zdefiniować a Stagedla swojej zawartości i nową, viewportponieważ zostanie użyta nowa kamera. Musisz zdefiniować nowy Table, który możesz dodać do Stage. Możesz dodawać swoje treści do tablezwykłej wersji. Reszta kodu, który przedstawię, aby pokazać, jak to działa, jest specyficzna dla gry, więc po prostu zignoruj ​​ją.

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;
}

}

a następnie na ekranie odtwarzania w następujący sposób:

przede wszystkim potrzebujesz zmiennej, aby odwoływać się do twojego hudu:

private Hud hud; 

a następnie w konstruktorze tworzysz nową instancję swojej klasy:

hud= new Hud(); 

w metodzie aktualizacji musisz umieścić następujące wiersze kodu, ponieważ myślę, że chcesz wyświetlić niektóre informacje o grze, takie jak punkty lub pozostałe życia:

hud.update();

w metodzie renderowania zrób to:

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

i na koniec nie zapomnij pozbyć się hudu metodą usuwania

Mam nadzieję że to pomogło

Bexx
źródło