Reputation:
Hi I made a pause menu for my game, and you navigate through it with the arrow keys on the keyboard. My question is how do I make it so I can navigate with my mouse, and click the buttons rather then having to use the arrow keys?
here is the code:
public class InGameMenu implements KeyListener {
private String[] string = { "Resume Game", "Options", "Save Game", "Load Game", "Exit Game" };
private String[] optionStrings = { "Help", "Back" };
public static int selected = 0;
private int space = 25;
public int width = 800;
public int height = 600;
public static boolean isInMenu = true;
public static boolean isInOptions = false;
public static boolean saving = false;
public static boolean loading = false;
public InGameMenu() {
}
public void tick() {
}
public void render(Graphics g) {
g.setColor(new Color(0, 0, 0, 90));
g.fillRect(0, 0, Component.width, Component.height);
if (isInMenu) {
g.setColor(Color.LIGHT_GRAY);
if (saving) {
g.drawString("Saving", Component.width / 2 / Component.pixelSize - (int) (Component.width / 25), 35);
}
if (loading) {
g.drawString("Loading", Component.width / 2 / Component.pixelSize - (int) (Component.width / 25), 35);
}
for (int i = 0; i < string.length; i++) {
if (selected == i) {
g.setColor(Color.RED);
} else {
g.setColor(Color.WHITE);
}
g.drawString(string[i], Component.width / 2 / Component.pixelSize - (int) (Component.width / 17.5), Component.height / 8 + (i * space));
}
} else if (isInOptions) {
for (int i = 0; i < optionStrings.length; i++) {
if (selected == i) {
g.setColor(Color.RED);
} else {
g.setColor(Color.WHITE);
}
g.drawString(optionStrings[i], Component.width / 2 / Component.pixelSize - (int) (Component.width / 17.5), Component.height / 8 + (i * space));
}
}
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (isInMenu) {
if (key == KeyEvent.VK_UP) {
selected--;
if (selected < 0) {
selected = string.length - 1;
}
}
if (key == KeyEvent.VK_DOWN) {
selected++;
if (selected > string.length - 1) {
selected = 0;
}
}
if (key == KeyEvent.VK_ENTER) {
if (selected == 0) {
Component.isInMenu = false;
} else if (selected == 1) {
isInMenu = false;
isInOptions = true;
selected = 0;
} else if (selected == 2) {
saving = true;
SaveLoad.save();
saving = false;
} else if (selected == 3) {
loading = true;
SaveLoad.load();
loading = false;
} else if (selected == 4) {
System.exit(0);
}
}
} else if (isInOptions) {
if (key == KeyEvent.VK_UP) {
selected--;
if (selected < 0) {
selected = optionStrings.length - 1;
}
}
if (key == KeyEvent.VK_DOWN) {
selected++;
if (selected > optionStrings.length - 1) {
selected = 0;
}
}
if (key == KeyEvent.VK_ENTER) {
if (selected == 0) {
System.out.println("HELP");
} else if (selected == 1) {
isInOptions = false;
isInMenu = true;
}
}
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
Upvotes: 0
Views: 61
Reputation: 15146
x
, y
, width
and height
) of the text.You already have the x
and y
:
g.drawString(string[i], Component.width / 2 / Component.pixelSize - (int) (Component.width / 17.5), Component.height / 8 + (i * space));
// x = Component.width / 2 / Component.pixelSize - (int) (Component.width / 17.5)
// y = Component.height / 8 + (i * space)
You can determine the width
and height
via Font#getStringBounds(String, FontRenderContext)
:
FontRenderContext renderContext = new FontRenderContext(null, true, true);
Font font = new Font("Arial", Font.PLAIN, 14);
String labelText = "Start";
Rectangle2D labelBounds = font.getStringBounds(labelText, renderContext);
int labelWidth = (int) labelBounds.getWidth();
int labelHeight = (int) labelBounds.getHeight();
The bounds is needed to determine if the mouse is hovering over the text when the click occurs.
Right now, you're only maintaining the names in string
and optionStrings
. You'll need to maintain the x
, y
, width
and height
for every menu item.
Since every menu item each has their own name, size and position, it would be easier to create a new type composed of these properties:
public class Label {
private String name;
private Font font;
private int x, y;
private int width, height;
public Label(String name, Font font, int x, int y, int width, int height) {
this.name = name;
this.font = font;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
Instead of a String[]
, you could have a Label[]
. Although it's preferrable to use List
for it's higher-level functionality:
public class InGameMenu implements MouseListener {
private List<Label> labels;
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
labels.forEach(label -> {
// if (mouse hovering over label)
// ...
});
}
}
The formula is pretty simple:
// checks if mouse intersects with label on X axis
intersectsHorizontally := mouseX > labelX && mouseX < labelX + labelWidth;
// checks if mouse intersects with label on Y axis
intersectsVertically := mouseY > labelY && mouseY < labelY + labelHeight;
// if both conditions above are true, mouse is hovering over label
The easiest way to implement this would be to give your Label
objects a containsPoint(int x, int y)
behavior:
public class Label {
private int x, y;
private int width, height;
//...
public boolean containsPoint(int pointX, int pointY) {
boolean containsHorizontally = pointX > x && pointX < x + width;
boolean containsVertically = pointY > y && pointY < y + height;
return containsHorizontally && containsVertically;
}
}
Now you can easily determine whether the mouse is hovering over a specific label:
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
labels.forEach(label -> {
if(label.containsPoint(x, y)) {
//...
}
});
}
There are many ways to go about this. Instead of a List
, you could maintain the labels independently and check each one:
public class InGameMenu implements MouseListener {
private Label startLabel;
private Label loadLabel;
public InGameMenu(Label startLabel, Label loadLabel) {
this.startLabel = startLabel;
this.loadLabel = loadLabel;
}
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
if(startLabel.containsPoint(x, y)) {
//start game
} else if(loadLabel.containsPoint(x, y)) {
//load game
}
}
}
But this isn't dynamic, as InGameMenu
is forced to know about every label it has, and adding labels would be a pain.
A better approach is to create all the Label
objects outside of InGameMenu
, as the examples above have been doing:
FontRenderContext renderContext = ...;
Font font = ...;
// start label
String labelText = "Start";
Rectangle2D labelBounds = font.getStringBounds(labelText, renderContext);
int x = ...;
int y = ...;
int width = (int) labelBounds.getWidth();
int height = (int) labelBounds.getHeight();
Label label = new Label(labelText, font, labelX, labelY, labelWidth, labelHeight);
// list of all labels for menu
List<Label> labels = new ArrayList<>();
labels.add(label);
InGameMenu menu = new InGameMenu(labels);
Then have the label object tell us when it has been clicked. We can do this by giving Label
a click()
method for InGameMenu
to trigger when the label has been clicked:
public class InGameMenu implements MouseListener {
private List<Label> labels;
public InGameMenu(List<Label> labels) {
this.labels = labels;
}
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
labels.forEach(label -> {
if(label.containsPoint(x, y))
label.click();
});
}
}
Then allowing Label
to accept a callback function:
public class Label {
private String name;
private Font font;
private int x, y;
private int width, height;
private Runnable onClick;
public Label(String name, Font font, int x, int y, int width, int height, Runnable onClick) {
this.name = name;
this.font = font;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.onClick = onClick;
}
public void click() {
onClick.run();
}
//...
}
So when you create Label
objects, you can give them actions that make use of outside data (maybe a GameStateManager
or something):
Runnable onClick = () -> System.out.println("Start button was clicked!");
Label label = new Label("Start", ..., onClick);
Upvotes: 0
Reputation: 3553
You can implement MouseListener
too.
You can add these methods from MouseListener
:
public void mousePressed(MouseEvent e) {
if(e.getSource() == button1)
{
isInMenu = false;
isInOptions = true;
selected = 0;
}
if(e.getSource() == button2)
{
saving = true;
SaveLoad.save();
saving = false;
}
if(e.getSource() == button3)
{
loading = true;
SaveLoad.load();
loading = false;
}
if(e.getSource() == button4)
{
System.exit(0);
}
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
Upvotes: 1