Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create-a-simple-hud #17

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Binary file added images/Healthbar.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
140 changes: 140 additions & 0 deletions user-interface/# Create a simple HUD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Create a simple HUD

A HUD (heads-up display) is used to render Graphics used for game information, like a health bar or a toolbar.
LITIENGINE provides a broad kit of Components for this with the [`GuiComponent`](https://litiengine.com/docs/user-interface/guicomponents-an-overview/) framework.

Lets implement a simple HUD that creates a healthbar for us.

* ![Healthbar](https://user-images.githubusercontent.com/105820458/173564612-9c57bf97-95b5-4c22-9058-8ed1ed3e28b2.PNG)


## 1. our HUD class

```java
package org.example;

import de.gurkenlabs.litiengine.Game;
import de.gurkenlabs.litiengine.graphics.ImageRenderer;
import de.gurkenlabs.litiengine.graphics.Spritesheet;
import de.gurkenlabs.litiengine.gui.GuiComponent;
import de.gurkenlabs.litiengine.resources.Resources;
import de.gurkenlabs.litiengine.util.Imaging;

import javax.tools.Tool;
import java.awt.image.BufferedImage;

public class HUD extends GuiComponent {
//create variables for our images that we want to render later, and scale them to the desired size
private static final BufferedImage HEART = Imaging.scale(Resources.images().get("src/main/resources/hud/life.png"), 5.0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the resources folder is configured correctly for your project, you only need to specify the file name, not the entire relative path.

private static final BufferedImage HEARTEMPTY = Imaging.scale(Resources.images().get("src/main/resources/hud/herzleer.png"), 5.0);

//optional / intialize a Padding variable, to change positioning easier later on
private static final int PADDING = 10;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could also use values that are relative to the screen resolution in order to make the UI more dynamic.


//call the super constructor, with the current game Windows Width and Hight to create a new HUD
protected HUD() {
super(0, 0, Game.window().getResolution().getWidth(), Game.window().getResolution().getHeight());
}

```
- It's important to know that the Image files for our `BufferedImage`s can't be received from the [game resource file](https://litiengine.com/docs/resource-management/), so be sure to provide the complete path in the file system.

## The render methods

now lets get to the visual part, the render methods of our `HUD` class:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
now lets get to the visual part, the render methods of our `HUD` class:
Now let's get to the visual part, the render methods of our `HUD` class:


```java
@Override
public void render(Graphics2D g) {
super.render(g);

//if there is no enviroment loaded we dont need to render anything
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//if there is no enviroment loaded we dont need to render anything
//if there is no environment loaded, we don't need to render anything

if (Game.world().environment() == null ) {
return;

}
this.renderHP(g);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.renderHP(g);
renderHP(g);

}

private void renderHP(Graphics2D g) {

//define the x and y coordinates of our Healtbar
double y = Game.window().getResolution().getHeight() - Game.window().getResolution().getHeight() + PADDING * 7 - HEART.getHeight();
double x = Game.window().getResolution().getWidth() - ((Player.instance().getHitPoints().getMax() * (HEART.getWidth() + PADDING) * 1.28) - PADDING);

//decides weather to render a full or empty heart depending on the players current Hitpoints
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//decides weather to render a full or empty heart depending on the players current Hitpoints
//decides whether to render a full or empty heart depending on the players current hitpoints

for (int i = 0; i < Player.instance().getHitPoints().getMax(); i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should explain all custom implementations.
For example, where does Player.instance() come from? -> A Creature controlled by the player.

BufferedImage img = i < Player.instance().getHitPoints().get() ? HEART : HEARTEMPTY;
ImageRenderer.render(g, img, x + i * img.getWidth() + PADDING, y);

}
}

```
- the x and y coorinates of this example place the bar in the upper left corner of the screen, but you can adjust these to your liking of course.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- the x and y coorinates of this example place the bar in the upper left corner of the screen, but you can adjust these to your liking of course.
- The x and y coordinates of this example place the bar in the upper left corner of the screen, but you can adjust these to your liking of course.




### you could also adjust how many Hitpoints a heart should represent by dividing it by 2 for example :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### you could also adjust how many Hitpoints a heart should represent by dividing it by 2 for example :
### You could also adjust how many hitpoints a heart should represent by dividing it by 2 for example :


```java
private void renderHP(Graphics2D g) {

//define the x and y coordinates of our Healtbar
double y = Game.window().getResolution().getHeight() - Game.window().getResolution().getHeight() + PADDING * 7 - HEART.getHeight();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain the magic numbers maybe?

double x = Game.window().getResolution().getWidth() - ((Player.instance().getHitPoints().getMax() * (HEART.getWidth() + PADDING) * 1.28) - PADDING);

//decides weather to render a full or empty heart depending on the players current Hitpoints
for (int i = 0; i < Player.instance().getHitPoints().getMax() /2; i++) {
BufferedImage img = i < Math.ceil( Player.instance().getHitPoints().get()/2) ? HEART : HEARTEMPTY;
ImageRenderer.render(g, img, x + i * img.getWidth() + PADDING, y);

}
}

```
- now one heart would represent 2 Hitpoints of the Player
- we use the `Math.ceil()` method to get the next bigger int and not a double

But now we have render methods, so why can't we see our precous health?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
But now we have render methods, so why can't we see our precous health?
But now we have render methods, so why can't we see our precious health?


## initializing a new HUD in the `GameScreen` class

Now to actually see our HUD, we first need to instantiate it in our `GameScreen` class


```java
package org.example;

import de.gurkenlabs.litiengine.Game;
import de.gurkenlabs.litiengine.gui.screens.GameScreen;


public class InGameScreen extends GameScreen {

private HUD hud;

public static final String NAME = "INGAME-SCREEN";

public InGameScreen() {
super(NAME);
}
@Override
protected void initializeComponents() {

//initialize a new HUD object, and add it to the GameScreen components
this.hud = new HUD();
getComponents().add(hud);

}
@Override
public void render(Graphics2D g) {
super.render(g);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to override the render method here.
This is exactly what the parent render method in the GameScreen class does.


if (Game.world().environment() != null) {
Game.world().environment().render(g);
}
}
}
```
And there you have it: your own Health bar, customizable as you wish :D