HUD & UI Cookbook¶
This cookbook provides copy-paste ready implementations for common user interface elements in 2D games using LITIENGINE's GuiComponent and SpeechBubble frameworks.
1. Dynamic Health & Mana Bars¶
Draw a floating or fixed health bar that smoothly interpolates and changes color when damaged:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import de.gurkenlabs.litiengine.entities.CombatEntity;
import de.gurkenlabs.litiengine.gui.GuiComponent;
public class HealthBar extends GuiComponent {
private final CombatEntity entity;
private static final int BAR_WIDTH = 50;
private static final int BAR_HEIGHT = 6;
public HealthBar(CombatEntity entity) {
super(0, 0, BAR_WIDTH, BAR_HEIGHT);
this.entity = entity;
}
@Override
public void render(Graphics2D g) {
super.render(g);
if (this.entity == null || this.entity.isDead()) {
return;
}
// Position health bar 10 pixels above the entity
double screenX = this.entity.getX() - (BAR_WIDTH - this.entity.getWidth()) / 2.0;
double screenY = this.entity.getY() - 10;
double healthPercent = Math.max(0.0, (double) entity.getHitpoints() / entity.getMaxHitpoints());
// 1. Background Bar (Dark Gray)
g.setColor(new Color(40, 40, 40, 200));
g.fill(new Rectangle2D.Double(screenX, screenY, BAR_WIDTH, BAR_HEIGHT));
// 2. Health Fill (Green > Yellow > Red)
Color healthColor = healthPercent > 0.5 ? Color.GREEN : (healthPercent > 0.2 ? Color.YELLOW : Color.RED);
g.setColor(healthColor);
g.fill(new Rectangle2D.Double(screenX, screenY, BAR_WIDTH * healthPercent, BAR_HEIGHT));
// 3. Border Outline
g.setColor(Color.BLACK);
g.draw(new Rectangle2D.Double(screenX, screenY, BAR_WIDTH, BAR_HEIGHT));
}
}
2. Floating Combat Damage Numbers¶
Spawn animated damage text that floats upwards and fades out:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import de.gurkenlabs.litiengine.Game;
import de.gurkenlabs.litiengine.IUpdateable;
import de.gurkenlabs.litiengine.graphics.TextRenderer;
public class FloatingText implements IUpdateable {
private Point2D location;
private final String text;
private final Color color;
private int alpha = 255;
private int lifetime = 40; // 40 ticks = ~0.66s
public FloatingText(String text, Point2D startLocation, Color color) {
this.text = text;
this.location = new Point2D.Double(startLocation.getX(), startLocation.getY());
this.color = color;
Game.loop().attach(this);
}
@Override
public void update() {
this.location.setLocation(this.location.getX(), this.location.getY() - 0.7);
this.alpha = Math.max(0, this.alpha - (255 / lifetime));
if (this.alpha <= 0) {
Game.loop().detach(this);
}
}
public void render(Graphics2D g) {
if (this.alpha <= 0) return;
Point2D screenPos = Game.world().camera().getViewportLocation(this.location.getX(), this.location.getY());
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 14));
Color drawColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
TextRenderer.renderWithOutline(g, text, screenPos.getX(), screenPos.getY(), drawColor, Color.BLACK);
}
}
3. In-Game Speech Bubbles¶
Display dialogue bubbles over speaking entities:
import de.gurkenlabs.litiengine.gui.SpeechBubble;
// Display a speech bubble for 3000 milliseconds
SpeechBubble bubble = SpeechBubble.create(npcEntity, "Welcome to our village, traveler!");
4. Pause Menu Overlay¶
Create a toggleable pause overlay that pauses game loop logic while allowing menu interaction:
import java.awt.Color;
import java.awt.Graphics2D;
import de.gurkenlabs.litiengine.Game;
import de.gurkenlabs.litiengine.gui.Button;
import de.gurkenlabs.litiengine.gui.GuiComponent;
public class PauseOverlay extends GuiComponent {
private final Button resumeButton;
private final Button exitButton;
public PauseOverlay() {
super(0, 0, Game.window().getResolution().getWidth(), Game.window().getResolution().getHeight());
double centerX = getWidth() / 2.0;
double centerY = getHeight() / 2.0;
this.resumeButton = new Button(centerX - 100, centerY - 40, 200, 40, "Resume");
this.resumeButton.onClicked(e -> setVisible(false));
this.exitButton = new Button(centerX - 100, centerY + 20, 200, 40, "Quit to Title");
this.exitButton.onClicked(e -> Game.screens().display("TITLE"));
this.getComponents().add(resumeButton);
this.getComponents().add(exitButton);
this.setVisible(false);
}
@Override
public void render(Graphics2D g) {
if (!isVisible()) return;
// Semi-transparent backdrop
g.setColor(new Color(0, 0, 0, 180));
g.fillRect(0, 0, (int) getWidth(), (int) getHeight());
super.render(g);
}
}