Libgdx how to display monster name above their head

Hello,

It has been days for trying and failing to pull this off. Also asked for help, but seems none can help me either.

I were able to create HUD for the player, that was really easy. But then i got stuck about creating HUD for the monsters. Such as i want to draw their name above the head and also maybe add a health bar aswell.

http://3.1m.yt/Jq-JVZQ.png

As you see, the player hud works perfect, but when i move the monster name moves with me. Instead to get attached to the monster.


public void update(float deltaTime) {
        // update creatures
        world.getPlayer().onThink(deltaTime);
        for (Monster monster : world.getMonsters()) {
            monster.onThink(deltaTime);
        }

        // update camera to the player
        camera.position.set(world.getPlayer().getPosition(), 0);
        camera.update();
    }

    @Override
    public void render(float deltaTime) {
        // clear screen
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        // seprate update logic
        update(deltaTime);

        // set camera to what we see
        worldRenderer.renderer.setView(camera);
        worldRenderer.renderer.render();

        // draw world
        game.spriteBatch.setProjectionMatrix(camera.combined);
        worldRenderer.render();

        // monster UI
        game.spriteBatch.setProjectionMatrix(uiCam.combined);
        for (Monster monster : world.getMonsters()) {
            uiCam.project(uiPosition.set(monster.getPosition().x, monster.getPosition().y, 0));
        }

        game.spriteBatch.begin();
        Assets.font.draw(game.spriteBatch, "Firefox", uiPosition.x / 2, uiPosition.y / 2);
        game.spriteBatch.end();

        // Player Hud
        game.spriteBatch.setProjectionMatrix(hud.stage.getCamera().combined);
        hud.stage.draw();
    }

Thanks!

What exactly is happening? I can’t tell by the picture. Is that mushroom the monster?

Haha nope, the fox is the monster. When i scale down the font, it does not display whole text. The mushroom does not overlap the text, if you may wonder xD

Would you mind providing the code which has the instance of Sprite Batch you’re using there?

Sure:

package com.game.newproejctx.World;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.game.newproejctx.Entity.Monster;

public class GameRenderer {

    private GameWorld world;
    public OrthogonalTiledMapRenderer renderer;
    private SpriteBatch spriteBatch;

    public GameRenderer(SpriteBatch spriteBatch, GameWorld world) {
        this.spriteBatch = spriteBatch;
        this.world = world;
        this.renderer = new OrthogonalTiledMapRenderer(world.getMap(), world.unitScale);
    }

    public void render() {
        spriteBatch.begin();
        world.getPlayer().render(spriteBatch);
        for (Monster monster : world.getMonsters()) {
            monster.render(spriteBatch);
        }
        spriteBatch.end();
    }
}

I wonder if it’s possible to use Scene2D to display monsters name and maybe later on i will add healthbar by using progress bar or something.

Yes it is possible. Is the projection matrix of that spritebatch you’re using set to the camera’s combined one?

I had this similar problem before a long time ago, I am trying to narrow down what it could possibly be.

Are you doing anything special in some overriden draw methods of custom actors? That could be the case.

Well i found out why my font is way to big, i should use a seprate camera to display UI stuff and do not use the same one as the game cam.

But you said it’s possible to use Scen2d to display monsters name, here is my Hud and GameScreen:

package com.game.newproejctx.Scenes;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.game.newproejctx.Entity.Player;
import com.game.newproejctx.Main;

public class Hud implements Disposable {
    public Stage stage;
    private Viewport viewport;

    // Labels
    private static Label nameLabel;

    public Hud(SpriteBatch spriteBatch, Player player) {
        viewport = new FitViewport(Main.VIRTUAL_WIDTH, Main.VIRTUAL_HEIGHT, new OrthographicCamera());
        stage = new Stage(viewport, spriteBatch);

        nameLabel = new Label(player.getName(), new Label.LabelStyle(new BitmapFont(), Color.GREEN));
        nameLabel.setPosition(Main.VIRTUAL_WIDTH / 2 + player.getName().length(), Main.VIRTUAL_HEIGHT / 2 + player.getHeight(), Align.center);

        stage.addActor(nameLabel);
    }

    @Override
    public void dispose() { stage.dispose(); }
}
package com.game.newproejctx.Screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.game.newproejctx.Entity.Monster;
import com.game.newproejctx.Main;
import com.game.newproejctx.Scenes.Hud;
import com.game.newproejctx.World.GameRenderer;
import com.game.newproejctx.World.GameWorld;

public class GameScreen extends AbstractScreen {
    // World
    private GameWorld world;
    private GameRenderer worldRenderer;

    // Camera
    private OrthographicCamera camera;
    private Viewport viewport;

    // Hud
    private Hud hud;

    public GameScreen(Main game) {
        super(game);
    }

    @Override
    public void show() {
        world = new GameWorld("map.tmx");
        worldRenderer = new GameRenderer(game.spriteBatch, world);

        camera = new OrthographicCamera();
        viewport = new FitViewport(Main.VIRTUAL_WIDTH * world.unitScale, Main.VIRTUAL_HEIGHT * world.unitScale, camera);
        viewport.apply();

        camera.position.set(viewport.getWorldWidth() / 2, viewport.getWorldHeight() / 2, 0);

        hud = new Hud(game.spriteBatch, world.getPlayer());
    }

    public void update(float deltaTime) {
        world.getPlayer().onThink(deltaTime);

        camera.position.set(world.getPlayer().getPosition(), 0);
        camera.update();
    }

    @Override
    public void render(float deltaTime) {
        GL20 gl = Gdx.gl;
        gl.glClearColor(0, 0, 0, 1);
        gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        update(deltaTime);

        worldRenderer.renderer.setView(camera);
        worldRenderer.renderer.render();

        game.spriteBatch.setProjectionMatrix(camera.combined);
        worldRenderer.render();

        game.spriteBatch.setProjectionMatrix(hud.stage.getCamera().combined);
        hud.stage.draw();
    }

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

    @Override
    public void hide() {
    }
}

gl.glClearColor only needs to be set once, not per frame. gl.glClear(GL_COLOR_BUFFER_BIT) flushes the pixels to the clear color. Disabling the glClear gives a cool effect if motion goes on.

There are a few ways you can put names above heads.

One way would be making a function to convert world coordinates to screen coordination’s and utilizing this data, draw and offset text appropriately. That is if you use coordinate systems. I am unfamiliar with such function in libgdx, but it shouldn’t be too different then the ones I used when scripting on Roblox. Of course I remember nothing from that in specific - that was a few years ago. If need-be I will look it up.

The other way would be taking your modelview matrix of the entity, and translating from there - explicitly or implicitly ignoring any Z depth (into the screen). Using a base Orthogonal matrix of course. Then you would take this position and translate the text the object’s height in pixels in the Y axis.

Thank you for your answer. Can you provide any examples from what i’ve posted.

Well i think i’m on the right way. I’m using a second camera and font gets drawn right. But problem is that, if player moves, the text moves with him and do not stand still above monster head.

I’m gonna assume once again that you have not set the projection matrix as it sounds like its moving with the screen. So do this with the camera.

yourSpriteBatch.setProjectionMatrix(secondcamera.combined);

do this where you first created the second camera and began drawing your entities

Quick crash course:

Orthogonal matrix is a projection matrix. It maps the screen coordinates you give (X 0-800, Y 0-600) to normalized unit coordinates (X 0-1, Y 0-1). This is for the OpenGL backend of libgdx. It makes it so 100 pixels is 100, not 100/800 or 100/600. (Assuming 800x600 screen)

Model-View matrix is a transformation matrix. Its a matrix made from the position, rotation, and the scale(size, 0 being virtually invisible). This is the base point of where the model would be - in this case a 2d square (two triangles). This doesn’t account for the size. If you were to draw all points of the square here, they would be in one corner of the triangle (the origin).

That is why you then transform from the origin to wherever the point needs to be. This is done per vertex (point). You multiply these 3 together in this order. OrthoMatrix * ModelMatrix * Vec3(offsetx,offsety,offsetz).

The Camera matrix, or whatever people want to call it, fits inbetween model and vertex offset. This is a further offset from the Model matrix. It allows to emulate the objects by offsetting them, for the fact there is not such thing as a camera in game programming. “move the world around instead of player”

… bottom line …

Make sure you have an ortho matrix, camera matrix, and a model matrix generated from the position, rotation, and scale. libgdx may do most of the work for you, so you should research more on libgdx in these areas - not necessarily matrix portion, but make sure something is happening to get the disired result.

Thank you everyone, for trying to help me. I have updated the first post. Since i know im near to the solution, but it does not work :frowning:

I newer Used Libgdx ^^ - but i can show how it must be in layout


	@Override
	public void render(float deltaTime) {
		// clear screen
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		// seprate update logic
		update(deltaTime);

		// set camera to what we see
		worldRenderer.renderer.setView(camera);
		worldRenderer.renderer.render();
		//Why? dublicate render 
--		worldRenderer.render();

		//this is painted in 2D - screen space - like GUI (inventory button etc)
		//don't need change projection matrix you already in orthographics projection 
		//maybe need reset matrix to base - if Libgdx have same worlds matrix and GUI
--		Assets.font.draw(game.spriteBatch, "Firefox", uiPosition.x / 2, uiPosition.y / 2);
		// Game GUI
++		GUI.draw();
	}
	//////
	public worldRenderer.renderer.render(){
		world.getPlayer().render();
			for (Monster monster : world.getMonsters()) {
				monster.render();
			}
		///Render Text - HP bars DMG numbers etc
		//Don't need change camera matrix 
		world.getPlayer().render_Text(); // + y offset to be over Player head - "Player"
		for (Monster monster : world.getMonsters()) {
			monster.render_Text();// + y offset - "Firefox" (x,y = monster Position)
		}
	}

Because for some unknown reasons ppl that use Libgdx can’t answer :stuck_out_tongue:

p.s Libgdx is not simple for beginners - maybe try RPG Maker, or Unity, Unreal

Hello,

Thank you for your respone. About duplicate render, one is render the tiled map and another is rendrering creatures.
Without setting a new projection matrix, then the GUI stuff i render gets unit scaled, which is not ideal.

Just use one camera and use batch.setProjectionMatrix to that camera.
set it to null when drawing gui

I tried:

game.spriteBatch.setProjectionMatrix(null);

Got NullPointerException error.

Hhhm, you could also use 2 batches, spriteBatch/hudBatch.