user7887989
user7887989

Reputation:

Select buttons with a mouse rather then keyboard

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

Answers (2)

Vince
Vince

Reputation: 15146

First, you must get the bounds (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.

Next, you must maintain the bounds for each menu item.

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)
        //     ...
        });
    }
}

Then, you must determine if the mouse position is within the label's position.

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)) {
            //...
        }
    });
}

Finally, you must determine which label was clicked

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

Robo Mop
Robo Mop

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

Related Questions