I am working on a text-based videogame and created a GUI for it, however, I am having issues getting my two classes to work together. I have a text field in my GUI class that receives input from the user, which I want to send to my Player class so that it can compare it and execute the appropriate methods. I am still very new to programming, so I hope that some of you out there might be able to help me. Excuse the horrendous code.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class GUI extends Player {
String input;
JLabel message;
public GUI() {
JFrame frame = new JFrame("SPACE GAME");
ImageIcon image = new ImageIcon("rocket.png");
frame.setIconImage(image.getImage());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1200, 800);
frame.setLocationRelativeTo(null);
frame.setFocusable(true);
frame.getContentPane();
JPanel panel = new JPanel();
JTextField commandLine = new JTextField(30);
JLabel message = new JLabel();
frame.add(panel);
panel.setBackground(Color.black);
panel.setLayout(null);
panel.setBorder(BorderFactory.createEmptyBorder(1000, 1000 ,1000 ,1000));
commandLine.setBackground(Color.WHITE);
commandLine.setBounds(5, 730, 300, 30);
commandLine.setBorder(BorderFactory.createLineBorder(Color.GRAY, 3));
commandLine.setFont(new Font("Zig", Font.PLAIN, 18));
commandLine.setForeground(Color.GREEN);
commandLine.setBackground(Color.BLACK);
commandLine.setCaretColor(Color.GREEN);
commandLine.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String input = commandLine.getText();
}
});
commandLine.setVisible(false);
message.setForeground(Color.GREEN);
message.setText("Welcome to SPACE GAME! Press any key to start.");
message.setBounds(5,665, 1000, 100);
message.setFont(new Font("Courier", Font.PLAIN, 18));
panel.add(message);
panel.add(commandLine);
frame.setVisible(true);
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
commandLine.setVisible(true);
commandLine.requestFocusInWindow();
message.setText("Type \"help\" for help");
}
#Override
public void keyReleased(KeyEvent e) {
}
});
}
public JLabel getMessage(JLabel message) {
return message;
}
}
import javax.swing.*;
import java.util.Scanner;
public class Player {
//Attributes
private Room currentRoom;
Player player;
JLabel message;
// Handles player movement and commands
public void move() {
player = new Player();
Map map = new Map();
GUI gui = new GUI();
player.currentRoom = map.room1;
Scanner input = new Scanner(System.in);
System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription());
System.out.println("Type \"help\" to get help");
//Commands
boolean isGameRunning = true;
while (isGameRunning) {
String goMessage = input.nextLine();
goMessage = goMessage.toLowerCase();
switch (goMessage) {
case "go north", "north", "go n", "n": goNorth(); break;
case "go east", "east", "go e", "e": goEast(); break;
case "go south", "south", "go s", "s": goSouth(); break;
case "go west", "west", "go w", "w": goWest(); break;
case "exit": isGameRunning = false; break;
case "look":
System.out.println(player.currentRoom.getRoomDescription());
break;
case "help":
System.out.println("""
"go (north, south, east, west)" to choose a direction to go.
"look" gives you a description of the room.
"exit" stops the game.""");
break;
default:
System.out.println("Unknown command");
break;
}
}
}
public void goNorth() {
if (player.currentRoom.getNorthRoom() != null) {
player.currentRoom = player.currentRoom.getNorthRoom();
System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription());
} else {
System.out.println("You cannot go that way");
}
}
public void goEast() {
if (player.currentRoom.getEastRoom() != null) {
player.currentRoom = player.currentRoom.getEastRoom();
System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription());
} else {
System.out.println("You cannot go that way");
}
}
public void goSouth() {
if (player.currentRoom.getSouthRoom() != null) {
player.currentRoom = player.currentRoom.getSouthRoom();
System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription());
} else {
System.out.println("You cannot go that way");
}
}
public void goWest() {
if (player.currentRoom.getWestRoom() != null) {
player.currentRoom = player.currentRoom.getWestRoom();
System.out.println("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription());
} else {
System.out.println("You cannot go that way");
}
}
}
There are some good things in your code as well as several major issues that may hamper your ability to mash a GUI with your current console-program code, and these include:
You do have some separation of the your program logic into separate classes such as Player, Map, and Room classes, and that is a very good thing, but you still could do more separation of the logic, and doing so can simplify your code making it easier to debug and enhance.
The Player class contains a Player instance, I'm not sure why. The outer main class should probably be re-named from Player to the Game class, and the Player class should be a totally separate class, one that holds the state of a single player instance, and shouldn't create instances of itself as you're doing. The key is to strive to "oop-ify" your code, especially the "model" portion of the code, the code that holds the non-user interface program logic, to make it more object-oriented, since this will make it much easier to allow you to connect a GUI or any other interface, to your model.
Your model (again, named Player) has user interface (UI) code within it: it has Scanner input code within it and println statements. This is problematic, especially now since you want to enhance the program and make it work with a GUI. The key thing that you must do, first and foremost, is to separate the user interface code (agian the code that gets input from the user and that displays output to the user) outside of model class, which is, again, class that holds the program logic.
Your Player class has a lot of non-Player code cluttering it. A basic OOPs principle is that a class should have a single responsibility (called the "single responsibility rule"), and so your Player class should only have code that is needed to encapsulate Player state (such as name, Room, perhaps other properties such as strength, weapons, health, intelligence....) and Player behavior (methods that allow Player to interact with other classes), and nothihg more or less.
Going on the same vein, it appears that all the program data and behaviors seem to be hard-wired into this single main Player class that drives the program. This is very limiting and will hinder your ability to create a rich playing environment with many rooms, items in the rooms, and things like this. Divide and conquer -- get that information out of the main program and put it into OOP-compliant classes, including a Room class that has fields for items it might contain, that has knowledge of its own connections to other rooms.
Your GUI program extends the Player object. This is a major problem since this program structure does not pass the basic test of inheritance, the "is-a" test: logically is a GUI a more specific type of player (much like a Dog is a more specific type of Animal, a structure which passes the "is-a" test)? No, it is not, and while this distinction may seem pendantic, you may try to use Player fields within the GUI class because of this inheritance, but if you try to do this, the code will not work correctly. Remove this inheritance, and instead try to connect classes through "composition" where one class holds an instance of another, rather than extends another. So for instance, the GUI could possibly hold a Player variable rather than extend from Player.
Bottom line:
You will want to fully "OOP-ify" your code before trying to add a GUI
In order for the program to be able to well-meshed with a GUI, I would suggest making your underlying logical code (the non-user interface logic code) much more object-oriented with classes that all have a single responsibility, classes that are easily testable in isolation (away from GUI or any user interface). Start with first principles and the program will be much easier to build.
So, for example, classes that could be considered for an adventure game include:
A Direction class, or better a Direction enum.
This would encapsulate the idea of "direction" and would be used for objects in the game to know which direction they are going and to be able to communicate this to other objects using constants, rather than Strings. The user of an enum would allow the compiler to check that directions are being used correctly, since if you used Strings, say, "West", the compiler would not automatically know if you mis-typed the String and used "Best" instead.
Something simple like this, for example:
public enum Direction {
NORTH, EAST, SOUTH, WEST
}
A GameRoom class
This would hold information telling it its location in the grid of rooms, Strings for both name and description properties, and a game player field or a List of game players if more than one are allowed, perhaps something that contains code like this:....
public class GameRoom {
// map with connections to other game rooms
private Map<Direction, GameRoom> connections = new HashMap<Direction, GameRoom>();
// location information
private int x;
private int y;
// identifying information
private String roomName;
private String description;
// holds any player objects that may be in the room
private List<GamePlayer> playersInRoom = new ArrayList<>();
The class will have appropriate constructors as well as getter and setter methods and also:
An addPlayer(...) method that allows the game to add a player into the room:
public void addPlayer(GamePlayer gamePlayer) {
playersInRoom.add(gamePlayer);
gamePlayer.setCurrentRoom(this); // we'll get to this later
}
A public boolean move(...) method that moves a player contained by the room to a connecting room. It first checks that the room actually contains the player being moved, and then next checks if the room has a connection to another room in the requested direction. If either are false, then the method returns false to let the calling code that the attempted move failed. Otherwise, if the move is allowed, it returns true:
public boolean move(GamePlayer gamePlayer, Direction direction) {
// if the room doesn't currently hold this player
if (!playersInRoom.contains(gamePlayer)) {
return false; // invalid move request
}
// if the room doesn't have a connecting room in the requested direction
if (!connections.containsKey(direction)) {
return false; // invalid move request
}
// otherwise, we're good
playersInRoom.remove(gamePlayer);
connections.get(direction).addPlayer(gamePlayer);
return true;
}
A GamePlayer class
This class will hold several properties such as a String for name and a GameRoom field for the current room, constructors, getter and setter methods. It's declaration and fields could look something like:
public class GamePlayer {
private String name;
private GameRoom currentRoom;
//.... other properties
It too should have a move method that calls the currentRoom GameRoom's move method and returns the same boolean, a result that tells the calling code if the move was valid and successful:
public boolean move(Direction direction) {
return currentRoom.move(this, direction);
}
A GameModel class:
This will hold the state of the current game, fields for players, a data structure to hold all the rooms. It can have its own move method.....
Only after creating your OOP-compliant classes should you move to the next step: the creation of your GUI or "view" class/classes. This class could hold a GameModel instance, and would be responsible for 1) displaying the state of the game (a visual representation of the rooms and the items that they hold), and 2) getting input from the user, and passing that input to the game model for processing.
I like to use an M-V-C program structure, which stands for "Model-View-Controller" where the program logic and GUI are kept as separate as possible, and there may be a controller class or classes that help tie the model and view together. Another principle that I try to follow is to keep the GUI as "dumb" as possible. By dumb, I mean that it should get input from the user, perhaps do the most basic of input verifications, and should display the state of the model, but that's it. Almost all the "brains" of the program should be held by the model itself and not the view.
Proof-of-concept Example:
An incomplete but running "proof-of-concept" example of what I am describing above is shown below. You should copy the program whole, paste it into your IDE into a single file named VideoGame.java, and then should be able to run it. It uses some concepts that you may not yet be familiar with, including the use of Key Bindings to get user input from the GUI and the use of PropertyChangeListeners and PropertyChangeSupport objects to allow clean communication between objects (to notify listeners if the state of one of the model object has changed). This program should respond to pressing of the arrow keys:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class VideoGame {
private static final int[][] ROOM_GRID_KEY = {
{ 1, 0, 0, 2, 0, 0, 3, 4 },
{ 5, 6, 7, 8, 9, 10, 11, 0 },
{ 0, 12, 0, 13, 0, 0, 0, 0 },
{ 0, 14, 0, 0, 0, 0, 15, 16 },
{ 17, 18, 0, 19, 0, 0, 20, 0 },
{ 21, 22, 23, 24, 25, 26, 27, 28 }
};
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
GameModel gameModel = new GameModel(ROOM_GRID_KEY);
GameView gameView = new GameView();
new GameController(gameModel, gameView);
JFrame frame = new JFrame("Video Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(gameView);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class GameController {
private GameModel gameModel;
private GameView gameView;
public GameController(GameModel gameModel, GameView gameView) {
this.gameModel = gameModel;
this.gameView = gameView;
ModelListener modelListener = new ModelListener();
gameView.setModel(gameModel);
gameModel.addPropertyChangeListener(modelListener);
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
gameView.modelChange(evt);
}
}
}
#SuppressWarnings("serial")
class DisplayPanel extends JPanel {
private JPanel[][] panelGrid;
private int gridCellSize;
private GameModel gameModel;
private GameRoom[][] roomGrid;
public DisplayPanel(int gridCellSize) {
this.gridCellSize = gridCellSize;
}
public void setModel(GameModel gameModel) {
this.gameModel = gameModel;
this.roomGrid = gameModel.getRoomGrid();
int rows = roomGrid.length;
int cols = roomGrid[0].length;
setBackground(Color.BLACK);
setLayout(new GridLayout(rows, cols, 1, 1));
setBorder(BorderFactory.createLineBorder(Color.BLACK));
panelGrid = new JPanel[rows][cols];
for (int r = 0; r < panelGrid.length; r++) {
for (int c = 0; c < panelGrid[r].length; c++) {
JPanel panel = new JPanel(new GridBagLayout());
panelGrid[r][c] = panel;
panel.setPreferredSize(new Dimension(gridCellSize, gridCellSize));
if (roomGrid[r][c] == null) {
panel.setBackground(Color.BLACK);
} else {
panel.setBackground(Color.PINK);
if (roomGrid[r][c].getPlayersInRoom().size() > 0) {
GamePlayer gamePlayer = roomGrid[r][c].getPlayersInRoom().get(0);
String name = gamePlayer.getName();
JLabel label = new JLabel(name);
panel.add(label);
}
}
add(panel);
}
}
// key bindings code
addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), Direction.SOUTH);
addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), Direction.NORTH);
addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), Direction.WEST);
addBindings(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), Direction.EAST);
}
private void addBindings(KeyStroke keyStroke, Direction direction) {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
Action action = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
gameModel.move(direction);
}
};
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), action);
}
public void modelChange(PropertyChangeEvent evt) {
for (int r = 0; r < panelGrid.length; r++) {
for (int c = 0; c < panelGrid[r].length; c++) {
JPanel panel = panelGrid[r][c];
if (roomGrid[r][c] != null) {
if (roomGrid[r][c].getPlayersInRoom().size() > 0) {
GamePlayer gamePlayer = roomGrid[r][c].getPlayersInRoom().get(0);
String name = gamePlayer.getName();
JLabel label = new JLabel(name);
panel.add(label);
} else {
panel.removeAll();
}
}
}
}
revalidate();
repaint();
}
public GameModel getGameModel() {
return gameModel;
}
}
class GameView extends JPanel {
private static final int CELL_SIZE = 80;
private DisplayPanel displayPanel = new DisplayPanel(CELL_SIZE);
private GameModel gameModel;
// private JTextField textField = new JTextField();
public GameView() {
setLayout(new BorderLayout());
add(displayPanel);
// add(textField, BorderLayout.PAGE_END);
}
public void setModel(GameModel gameModel) {
this.gameModel = gameModel;
displayPanel.setModel(gameModel);
}
public void modelChange(PropertyChangeEvent evt) {
displayPanel.modelChange(evt);
}
public GameModel getGameModel() {
return gameModel;
}
}
class GameModel {
public static final String GAME_MODEL = "game model";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private GameRoom[][] roomGrid;
private GamePlayer player = new GamePlayer("Fred");
public GameModel(int[][] roomGridKey) {
roomGrid = new GameRoom[roomGridKey.length][roomGridKey[0].length];
// fill room grid with rooms if 1 in grid key array, with null if 0
for (int y = 0; y < roomGridKey.length; y++) {
for (int x = 0; x < roomGridKey[0].length; x++) {
roomGrid[y][x] = roomGridKey[y][x] != 0 ? new GameRoom("Some Room", "Some Description", x, y) : null;
}
}
// make room connections:
for (int y = 0; y < roomGrid.length; y++) {
for (int x = 0; x < roomGrid[0].length; x++) {
GameRoom thisRoom = roomGrid[y][x];
// if no room present, don't
if (thisRoom == null) {
continue;
}
if (x > 0) {
GameRoom otherGameRoom = roomGrid[y][x - 1];
if (otherGameRoom != null) {
thisRoom.putConnection(Direction.WEST, otherGameRoom);
}
}
if (x < roomGrid[0].length - 1) {
GameRoom otherGameRoom = roomGrid[y][x + 1];
if (otherGameRoom != null) {
thisRoom.putConnection(Direction.EAST, otherGameRoom);
}
}
if (y > 0) {
GameRoom otherGameRoom = roomGrid[y - 1][x];
if (otherGameRoom != null) {
thisRoom.putConnection(Direction.NORTH, otherGameRoom);
}
}
if (y < roomGrid.length - 1) {
GameRoom otherGameRoom = roomGrid[y + 1][x];
if (otherGameRoom != null) {
thisRoom.putConnection(Direction.SOUTH, otherGameRoom);
}
}
}
}
// put player in top left room
GameRoom currentRoom = roomGrid[0][0];
if (currentRoom == null) {
// some big error occurred
System.err.println("Current room at 0, 0 is null. Exiting");
System.exit(-1);
}
player.setCurrentRoom(currentRoom);
currentRoom.addPlayer(player);
player.addPropertyChangeListener(pce -> pcSupport.firePropertyChange(GAME_MODEL, null, player));
}
public boolean move(Direction direction) {
boolean success = player.move(direction);
return success;
}
public GamePlayer getPlayer() {
return player;
}
public GameRoom[][] getRoomGrid() {
return roomGrid;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(GAME_MODEL, listener);
}
}
class GamePlayer {
public static final String GAME_PLAYER = "game player";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private String name;
private GameRoom currentRoom;
public GamePlayer(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public boolean move(Direction direction) {
boolean success = currentRoom.move(this, direction);
return success;
}
public void setCurrentRoom(GameRoom currentRoom) {
GameRoom oldValue = this.currentRoom;
GameRoom newValue = currentRoom;
this.currentRoom = currentRoom;
pcSupport.firePropertyChange(GAME_PLAYER, oldValue, newValue);
}
public GameRoom getCurrentRoom() {
return currentRoom;
}
#Override
public String toString() {
return "GamePlayer [name=" + name + ", currentRoom=" + currentRoom + "]";
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(GAME_PLAYER, listener);
}
}
class GameRoom {
// map with connections to other game rooms
private Map<Direction, GameRoom> connections = new HashMap<Direction, GameRoom>();
// location information
private int x;
private int y;
// identifying information
private String roomName;
private String description;
// holds any player objects that may be in the room
private List<GamePlayer> playersInRoom = new ArrayList<>();
public GameRoom(String roomName, String description, int x, int y) {
this.roomName = roomName;
this.description = description;
this.x = x;
this.y = y;
}
public boolean move(GamePlayer gamePlayer, Direction direction) {
// if the room doesn't currently hold this player
if (!playersInRoom.contains(gamePlayer)) {
return false; // invalid move request
}
// if the room doesn't have a connecting room in the requested direction
if (!connections.containsKey(direction)) {
return false; // invalid move request
}
// otherwise, we're good
playersInRoom.remove(gamePlayer);
connections.get(direction).addPlayer(gamePlayer);
return true;
}
public void addPlayer(GamePlayer gamePlayer) {
playersInRoom.add(gamePlayer);
gamePlayer.setCurrentRoom(this);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String getRoomName() {
return roomName;
}
public String getDescription() {
return description;
}
public List<GamePlayer> getPlayersInRoom() {
return playersInRoom;
}
public Map<Direction, GameRoom> getConnections() {
return connections;
}
public void putConnection(Direction direction, GameRoom otherGameRoom) {
connections.put(direction, otherGameRoom);
}
#Override
public String toString() {
return "GameRoom [x=" + x + ", y=" + y + "]";
}
}
enum Direction {
NORTH, EAST, SOUTH, WEST
}
Again, compile and run this code and use the arrow keys to move "Fred" around the grid.
A more complete program would separate the data completely out of the code and allow for file I/O for reading in the room grid into the program, possibly by creating a CSV file or files to hold the room information, or if the data is expected to grow and be more complex, then a relational database such as one that uses one of the flavors of SQL.
Since you've made the GUI extend the player, all you need to do is call the desired function from the ActionListener on the input field.
commandLine.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String input = commandLine.getText();
if (input == "go north") {
GoNorth();
}
// .....
}
});
However, things are pretty messy. There's no real reason for the GUI to extend the player, for starters. I'd advise you to separate the GUI class from Player. That way, you won't need to use the Scanner or Print, and instead collect all your GUI functionality in the GUI class.
For instance:
public void goNorth() {
if (player.currentRoom.getNorthRoom() != null) {
player.currentRoom = player.currentRoom.getNorthRoom();
gui.print("You are in " + player.currentRoom.getName() + ". " + player.currentRoom.getRoomDescription())
} else {
gui.print("You cannot go that way");
}
}
You would need to implement a print function in the GUI class, that added a line of text to the message box, but I'm sure you were planning on that to begin with.
In the GUI class, you would then have a reference to the player, so reading the input field would look something like this:
commandLine.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String input = commandLine.getText();
switch(input) {
case "go north":
player.goNorth();
break;
...
}
}
});
Related
So I've been looking around and couldn't find an answer that matches my question. Apologies if this question has been answered.
Here are my two classes:
Game Class
package sample;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.awt.*;
public class Game extends Application{
Player Player;
Map Map;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Game");
Group root = new Group();
double x =0;
double y = 0;
for (Tile[] out: Map.Tiles_Array) {
for (Tile in : out) {
Rectangle rectangle = new Rectangle();
rectangle.setHeight(10);
rectangle.setWidth(10);
rectangle.setX(x);
rectangle.setY(y);
root.getChildren().add(rectangle);
x = x + 10;
}
y = y+ 10;
x = 0;
}
VBox box = new VBox();
root.getChildren().add(box);
final Scene scene = new Scene(root);
scene.setFill(null);
primaryStage.setFullScreen(true);
primaryStage.setScene(scene);
primaryStage.show();
}
public Game(Map map , Player player) {
Map= map;
Player = player;
}
public static void main(String[] args) {
launch(args);
}
void update(){
}
}
`` MainMenu class
package sample;
import javafx.stage.Stage;
import java.util.Scanner;
public class MainMenu {
public static void main(String[] args) {
Print("***************************************************");
Print("* Welcome to the best Object Orientated game ever *");
Print("***************************************************");
Print("What is your Name?");
var Name = User_Input();
Print("Are you Male Or Female (M/F)");
char Gender = Check_Gender(User_Input().charAt(0));
Print("Whats is your age");
int Age = Exception_Catcher();
Print("Great, Now you need to choose your avatar that you will play with......");
Print("Your choice is between Archer(A), Knight(K) or Wizard(W)");
Characters Chosen_Character = Check_Choice(User_Input().charAt(0));
Print(Name + " " + Gender +" "+ Age + " " + Chosen_Character.Name); // debugging
Player player1 = new Player(Name,Age,Gender,Chosen_Character);
var Map = new Map(25,25 , player1);
Game My_Game = new Game(Map,player1);
}
private static void Print(String input){ // Prints string it is given
System.out.println(input);
}
private static String User_Input(){ // returns Users response
Scanner s = new Scanner(System.in);
return s.nextLine();
}
private static char Check_Gender(char Letter){
Letter = Character.toUpperCase(Letter);
if(Letter == 'M'){return Letter;}
else if(Letter == 'F'){return Letter;}
else{
Print("Choose M or F");
return Check_Gender(User_Input().charAt(0));
}
}
private static Characters Check_Choice(char Letter){
Letter = Character.toUpperCase(Letter);
if(Letter == 'A'){
return new Archer(50,2,10);
}
else if(Letter == ('K')){
return new Knight(100,1,20);
}
else if(Letter ==('W')){
return new Wizard(25,5,5);
}
else{
Print("Choose one of the correct options:Archer(A), Knight(K) or Wizard(W)");
return Check_Choice(User_Input().charAt(0));
}
}
private static int Exception_Catcher(){ //exception handling for when the user enters a non numerical value
try{
return Check_Age(Integer.parseInt(User_Input()));
}
catch (Exception e){
Print("That's not a correct number format, try again");
return Exception_Catcher();
}
}
private static int Check_Age(int Age){
if (Age <= 0) {
Print("You are not alive apparently");
return Exception_Catcher();
} else if (Age > 120) {
Print("You are not that old!! stop being silly");
return Exception_Catcher();
} else {
return Age;
}
}
}
Once my Map object and the Player object gets created, I want to pass it onto Game in order to procedurally create graphics for the game. I tried constructing the Game class to call the method Start however it requires a 'Stage' however when I create a default stage and pass it as an argument it throws multiple exceptions.
You need to start your game from Application.launch(args) so consider the following structure :
public class Game extends Application{
private Player player;
private Map map;
#Override
public void start(Stage primaryStage) throws Exception {
MainMenu mm = new MainMenu();
player = mm.getPlayer();
map = mm.getMap();
//rest of the code remains the same
}
public static void main(String[] args) {
launch(args);
}
}
MainMenu only receives user input and constructs a Player and a Map:
public class MainMenu {
private String name,age,gender,chosenCharacter;
private final Player player;
private final Map map;
public MainMenu() {
getUserInput();
player = new Player(name,age,gender,chosenCharacter);
map = new Map(25,25 , player);
}
private void getUserInput(){ // returns Users response
//todo
}
Player getPlayer() {
return player;
}
Map getMap() {
return map;
}
}
A better option is to get user input by using GUI:
public class GetUserInput {
private Player player;
private Map map;
private final TextField nameTf, ageTf, genderTf, characterTf;
GetUserInput() {
Dialog<Void> dialog = new Dialog<>();
dialog.setTitle("Game Setting");
dialog.setHeaderText("Please enter your prefrences");
// Set the button types.
ButtonType buttonType = new ButtonType("OK", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(buttonType, ButtonType.CANCEL);
//Add event handler to ok button
Button okButton = (Button) dialog.getDialogPane().lookupButton(buttonType);
okButton.setOnAction(e-> processInput());
//todo hanlde response when cancel button is pressed
// Create labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));
nameTf = new TextField();
nameTf.setPromptText("Name");
ageTf = new TextField();
ageTf.setPromptText("Age");
genderTf = new TextField();
genderTf.setPromptText("Gender");
characterTf = new TextField();
characterTf.setPromptText("Character");
//todo add validation to all text fields
grid.add(new Label("Name:"), 0, 0);
grid.add(nameTf, 1, 0);
grid.add(new Label("Age:"), 0, 1);
grid.add(ageTf, 1, 1);
grid.add(new Label("Gender:"), 0, 2);
grid.add(genderTf, 1, 2);
grid.add(new Label("Character:"), 0, 3);
grid.add(characterTf, 1, 3);
dialog.getDialogPane().setContent(grid);
// Request focus on the name field
Platform.runLater(() -> nameTf.requestFocus());
dialog.showAndWait();
}
private void processInput() {
String name = nameTf.getText();
String age = ageTf.getText();
String gender = genderTf.getText();
String chosenCharacter = characterTf.getText();
player = new Player(name,age,gender,chosenCharacter);
map = new Map(25,25 , player);
}
Player getPlayer() {
return player;
}
Map getMap() {
return map;
}
}
And use it by:
GetUserInput getUserInput = new GetUserInput();
player = getUserInput.getPlayer();
map = getUserInput.getMap();
One thing to remember is that your user interface isn't just a random collection of JavaFX components, but includes your code. Therefore you can put your code wherever you wish. Well, sort of.
I would create an object to store much of my state, perhaps calling this Game. Every Event subclass that exists might reference this to decide what the next state of the game should be.
If this is a zork-like game (many rooms, objects to pick up and discard, many exits) I would create an array of Room's, each Room would either be a Room object or a subclass of Room, depending on how difficult the room is. Objects would just be a base "Thing" class, with possible subclasses depending on what the thing was supposed to do. Each Room would describe its exits to the program, which would create the proper buttons. Some rooms would allow the player to do something inside the room ("wash hands in the pool" maybe) and there would be responses for some of them, and generic responses for others ("I don't know how to do that, Dave.").
Most of the buttons would be for exits. The EventHandler for the button would contain information from the Room about which new room to enter.
This is probably the nth time you've received a newbie question regarding calculators, but I just can't figure it out, been working on it for two to three days. The way I have built my calculator at the moment does not suffice and I know I have to start calculating at the time I press the '=' button, but I simply can't figure out how to do so. Due to this reason I have reverted back to my original calculator code, in which it calculates when I press an operation button (like '+') which didn't work, but I was hoping that that would allow me to properly build on it. Here's the code:
package rekenmachine;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;
public class Rekenmachine extends JFrame
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(300,500);
frame.setLocation(800,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Rekenmachine");
RekenPaneel rekenpaneel = new RekenPaneel();
frame.setContentPane(rekenpaneel);
frame.setVisible(true);
}
private static int getal, totaalGetal;
private boolean optellen, aftrekken, vermenigvuldigen, delen;
public int Optellen(int getal)
{
reset();
optellen = true;
totaalGetal += getal;
getal = 0;
return totaalGetal;
}
public int Aftrekken(int getal)
{
reset();
aftrekken = true;
totaalGetal -= getal;
getal = 0;
return totaalGetal;
}
public int Delen(int getal)
{
reset();
delen = true;
totaalGetal /= getal;
getal = 0;
return totaalGetal;
}
public int Vermenigvuldigen(int getal)
{
reset();
vermenigvuldigen = true;
totaalGetal *= getal;
getal = 0;
return totaalGetal;
}
public int getGetal()
{
return getal;
}
public int getTotaalGetal()
{
return totaalGetal;
}
public void reset()
{
optellen = false;
aftrekken = false;
delen = false;
vermenigvuldigen = false;
getal = 0;
totaalGetal = 0;
}
}
class RekenPaneel extends JPanel
{
JButton knop0, knop1, knop2, knop3, knop4, knop5, knop6, knop7, knop8, knop9,
knopOptel, knopAftrek, knopVermenigvuldigen, knopDelen, knopUitkomst,
knopWissen;
JTextField invoerVak;
JPanel textPaneel, knopPaneel, logoPaneel;
Rekenmachine rekenmachine;
public RekenPaneel()
{
rekenmachine = new Rekenmachine();
setLayout(new BorderLayout());
textPaneel = new JPanel();
knopPaneel = new JPanel();
logoPaneel = new JPanel();
textPaneel.setLayout(new FlowLayout());
knopPaneel.setLayout(new GridLayout(4,4));
logoPaneel.setLayout(new FlowLayout());
Border rand = BorderFactory.createEmptyBorder(10, 10, 10, 10);
knop0 = new JButton("0");
knop0.addActionListener(new knop0Handler());
knop1 = new JButton("1");
knop1.addActionListener(new knop1Handler());
knop2 = new JButton("2");
knop2.addActionListener(new knop2Handler());
knop3 = new JButton("3");
knop3.addActionListener(new knop3Handler());
knop4 = new JButton("4");
knop4.addActionListener(new knop4Handler());
knop5 = new JButton("5");
knop5.addActionListener(new knop5Handler());
knop6 = new JButton("6");
knop6.addActionListener(new knop6Handler());
knop7 = new JButton("7");
knop7.addActionListener(new knop7Handler());
knop8 = new JButton("8");
knop8.addActionListener(new knop8Handler());
knop9 = new JButton("9");
knop9.addActionListener(new knop9Handler());
knopOptel = new JButton("+");
knopOptel.addActionListener(new knopOptelHandler());
knopAftrek = new JButton("-");
knopAftrek.addActionListener(new knopAftrekHandler());
knopVermenigvuldigen = new JButton("*");
knopVermenigvuldigen.addActionListener(new knopVermenigvuldigenHandler());
knopDelen = new JButton("/");
knopDelen.addActionListener(new knopDelenHandler());
knopUitkomst = new JButton("=");
knopUitkomst.addActionListener(new knopUitkomstHandler());
knopWissen = new JButton("C");
knopWissen.addActionListener(new knopWissenHandler());
invoerVak = new JTextField(25);
invoerVak.setHorizontalAlignment(invoerVak.RIGHT);
invoerVak.setEditable(false);
invoerVak.setBackground(Color.WHITE);
textPaneel.add(invoerVak);
knopPaneel.add(knop7);
knopPaneel.add(knop8);
knopPaneel.add(knop9);
knopPaneel.add(knopDelen);
knopPaneel.add(knop4);
knopPaneel.add(knop5);
knopPaneel.add(knop6);
knopPaneel.add(knopVermenigvuldigen);
knopPaneel.add(knop1);
knopPaneel.add(knop2);
knopPaneel.add(knop3);
knopPaneel.add(knopOptel);
knopPaneel.add(knop0);
knopPaneel.add(knopWissen);
knopPaneel.add(knopUitkomst);
knopPaneel.add(knopAftrek);
add(textPaneel, BorderLayout.NORTH);
add(knopPaneel, BorderLayout.CENTER);
add(logoPaneel, BorderLayout.SOUTH);
}
class knop0Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "0");
}
}
class knop1Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "1");
}
}
class knop2Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "2");
}
}
class knop3Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "3");
}
}
class knop4Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "4");
}
}
class knop5Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "5");
}
}
class knop6Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "6");
}
}
class knop7Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "7");
}
}
class knop8Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "8");
}
}
class knop9Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText(invoerVak.getText() + "9");
}
}
class knopOptelHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String invoer = invoerVak.getText();
int invoerGetal = Integer.parseInt(invoer);
rekenmachine.Optellen(invoerGetal);
invoerVak.setText("");
}
}
class knopAftrekHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String invoer = invoerVak.getText();
int invoerGetal = Integer.parseInt(invoer);
rekenmachine.Aftrekken(invoerGetal);
invoerVak.setText("");
}
}
class knopVermenigvuldigenHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String invoer = invoerVak.getText();
int invoerGetal = Integer.parseInt(invoer);
rekenmachine.Vermenigvuldigen(invoerGetal);
invoerVak.setText("");
}
}
class knopDelenHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String invoer = invoerVak.getText();
int invoerGetal = Integer.parseInt(invoer);
rekenmachine.Delen(invoerGetal);
invoerVak.setText("");
}
}
class knopUitkomstHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
invoerVak.setText("" + rekenmachine.getTotaalGetal());
rekenmachine.reset();
}
}
class knopWissenHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
rekenmachine.reset();
invoerVak.setText("");
}
}
}
What it basically does is look like a calculator, all buttons work, yet the way it calculates is way off, if at all. I think what I need to do is save a number, when I press + it should add the next number, if I press - it should substract the next number, if I press * it should multiply by the next number and if I press / it should divide by the next number, then when I press = it should show the result, yet I have no idea how to do that.
Should it be done with an arraylist? If so, how could I properly save the result? I mean, using it with two numbers isn't that hard, you just save two numbers and do something with them, then show the result, but a person doesn't always use just two numbers.
To explain the problem I'm having more clearly: for example, when I enter '50' and then press '+' it SHOULD convert "50" to getal = 50 and start the Optellen method, then totaalGetal should become 50, it then empties the textfield. If I then add '3', it should say 53 when I press '=' yet it still shows 50 if I'm lucky. To solve that I assume I have to make the calculation WHEN I press '=' but I don't know how to save/calculate numbers before having done that.
Can anybody tell me what to do before I've lost all my hair? :P
When you click on the +, you're calling this:
knopOptel.addActionListener((ActionEvent e) ->
{
String invoer = invoerVak.getText();
int invoerGetal = Integer.parseInt(invoer);
rekenmachine.addition(invoerGetal);
invoerVak.setText("");
});
But when you click on +, you're not doing the calculation yet! What you should be doing is:
The user type a number
The user click on + (for example)
In your ActionListener, you read the number on the screen, you store it in getal, you clear the screen, and you set your boolean optel to true
The user types another number
The user click on equal
In your equal Listener, you read the number you read the number on the screen, and depending on the flag (optel in the example), you calculate the result
you display the result
So indeed, the calculation is done when you press equal.
A small code example:
knopOptel.addActionListener((ActionEvent e) ->
{
int invoerGetal = Integer.parseInt(invoerVak.getText()); // get the number
calculate(invoerGetal); //sets totalNumber to what it should be by looking at the flags
invoerVak.setText(totalNumber); // we write the temporary result
additionFlag = true; // next number must be added
});
And your calculate function should just be something like:
private void calculate(int aInvoerGetal) {
if (addition)
totalNumber += aInvoerGetal;
else if (substract)
totalNumber -= aInvoerGetal;
else if (divide)
totalNumber /= aInvoerGetal;
else if (multiply)
totalNumber *= aInvoerGetal;
resetFlags();
}
TO GO FURTHER:
Now, if you want to support multiple caculations (5+5+5+3), it's easy. When you click on +, -, *, /, you first call the equalActionListener.
This way, you get this kind of sequence:
5, + // ==> equal called ==> 5 (because the flags are all false) ==> flag + to true
10, + // ==> equal called ==> 15 because 5 in memory and + flag was on. + flag goes off, then on again (because you pressed + again)
4, = // ==> equal called ==> 19
When developing something, you have to think first how you want to solve a problem. Work from there by designing a solution. If you have a programmable solution, implement it. The UI may come later. That's a core skill that a developer should have.
1) You want to have a calculator that support +, -, / and *. The output should be shown if "=" is clicked.
2) Think with classes. That concept may be new for you, but you will discover later from. Your main class that does the calculations is Rekenmachine. (From a design perspective, it should be a stand alone class, but that's not important now). You need to separate it from your UI layer.
Your class supports the actions that you have implemented with the UI. That's good. But I also see things that shouldn't be there
public int Vermenigvuldigen(int getal)
{
reset(); // reset the calculator ?
vermenigvuldigen = true; // purpose ?
totaalGetal *= getal;
getal = 0; // resetting argument getal ?
return totaalGetal;
}
Here, I'm not sure why you're calling reset() because what it does is
public void reset()
{
optellen = false;
aftrekken = false;
delen = false;
vermenigvuldigen = false;
getal = 0;
totaalGetal = 0;
}
When reading the above method, you see that it resets the value that you tried to add on. Of course your calculation would go wrong because you're erasing previous data... resetting everything back to initial state. I also don't understand the setting to "true" or "false" on the actions. Perhaps for the UI? That is not required.
Make it simple:
When creating Rekenmachine, set the variable totaalGetal to 0 as default. That variable holds the value of your calculations performed so far. That's the start. When you have an addition, use
public void add(int getal) {
totaalGetal+= getal; // means totaalGetal = totaalGetal + getal.
}
Before calling add() you have to parse the string to an integer. This can be done in the button action:
class knop1Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
// get input
String input = invoerVak.getText();
// convert
int converted = convertToInt(input);
// instruct myRekenmachine to add the value
myRekenmachine.add(converted);
}
}
Important note ... use concise naming ... "knop1handler" is difficult to read. Use "addButtonHandler" to indicate that this class handles the add button.
convertToInt is a method that reads in a String and returns with an integer. You have to implement that yourself. myRekenmachine is an instance of your Rekenmachine class.
This above is for addition. Implement the same for other operands. If you want to adjust the UI, do that in the handler.
Now, when you press =, just return the totaalGetal value.
PS: Not sure, but ask if you are allowed to write names in English. My native language is Dutch, but during my CS courses, I am allowed to program completely in English. Please try to ask it because English is the main language in IT world if you're aiming for a career in IT.
Wesley, did you think about what you wanted the calculator to do before you started coding? e.g. would it support brackets, sin/cos, memory. Did you think about how logically these functions would work and then think of how they could be implemented in Java? A few flow charts and some pesudocode can go a long way when you're starting out in a new language if only to help you comprehend what it is you are trying to do.
BTW I know it's tempting to start with the GUI code and move into the logic of the application but it is usually better to start with the logic and then move onto the GUI. You can hard code the values for inputs and see if the functionaly behaves as expected and then introduce parameters with values passed in from else where.
EDIT
I think I know why your + key is not working. The reset() method is setting getal and totalGetal to 0 before adding them. 0 + 0 is 0.
knopOptel = new JButton("+");
knopOptel.addActionListener(new knopOptelHandler());
class knopOptelHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String invoer = invoerVak.getText();
int invoerGetal = Integer.parseInt(invoer);
rekenmachine.Optellen(invoerGetal);
invoerVak.setText("");
}
}
public int Optellen(int getal)
{
reset();
public void reset()
{
optellen = false;
aftrekken = false;
delen = false;
vermenigvuldigen = false;
getal = 0;
totaalGetal = 0;
}
optellen = true;
totaalGetal += getal;
getal = 0;
return totaalGetal;
}
So usually when I make mock-up programs (like this one) I look for things I can improve on in case the situation happens again.
Today I thought I'd brush up on basic OOP (I understand the concept of OOP, just haven't messed around with it for a bit and wanted to freshen my memory). So I decided to make a little game that just creates 3 monsters on a 10x10 plane and 1 player (you), you are able to move your player in any x/y direction. My program works but I can't help but feel that I'm doing something incorrectly.
So the basic layout of my program was to have 5 classes. A GUI class that shows the game and gives you directional buttons for movement control, a class that creates the monsters, a class that creates the players, a class that creates the 10x10 board and keeps track of monster/player locations, and of course a main class that creates all the objects and has the main game loop and whatnot.
I was having a bit of a hard time interacting with my main class and my GUI class. What I ended up doing was doing a while loop in my main class and waiting until the player presses the start button, and once the player presses it (via action listener) the GUI class sets a public variable (running) from false to true, and I am able to act accordingly once the variable is changed.
HERE is where I feel like I am doing something wrong: At first my while loop would not terminate unless I printed out to the console. I Googled the issue and apparently people have said that it's some sort of issue with threading or "active polling", which I did not understand. I went to my program and added a small 10ms thread sleep in my while loops and everything started working great.
My question to you guys is, what is active polling? Why is it bad? How/why/where was this going on in my program? And finally if there's a better way of interacting with a GUI class and a main class. Sorry for the giant wall of text but I like to be thorough when explaining a situation!
TL;DR: Am I interacting correctly with my GUI class and my main class? If not what is the proper way to do it?
My main class:
public class MainGame {
public static void main(String[] args) throws InterruptedException{
ShowGUI gui = new ShowGUI();
while(!gui.running){
Thread.sleep(10);
}
Board gameBoard = new Board();
gui.setLabelText(gameBoard.getBoard());
//Add Player
Player playerOne = new Player(1000, "Player 1");
//Add monsters
Monster monstMatt = new Monster(1000, "Matt");
Monster monstJon = new Monster(1000, "Jon");
Monster monstAbaad = new Monster(1000, "Abaad");
while(gui.running){
Thread.sleep(10);
int x, y;
x = playerOne.getX();
y = playerOne.getY();
if(gui.buttonPress != -1){
if(gui.buttonPress == 1){
playerOne.move(x, --y);
}else if(gui.buttonPress == 2){
playerOne.move(x, ++y);
}else if(gui.buttonPress == 3){
playerOne.move(--x, y);
}else if(gui.buttonPress == 4){
playerOne.move(++x, y);
}
gui.buttonPress = -1;
gui.setLabelText(gameBoard.getBoard());
}
}
}
}
My GUI Class:
public class ShowGUI{
private JTextArea board;
private JButton moveUp;
private JButton moveDown;
private JButton moveLeft;
private JButton moveRight;
public boolean running = false;
public int buttonPress = -1;
public ShowGUI(){
System.out.println("GUI Successfully Loaded");
createAndShow();
}
private void createAndShow(){
JFrame mainFrame = new JFrame("Bad Game");
addComponents(mainFrame.getContentPane());
mainFrame.setSize(500, 400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setLocationRelativeTo(null);
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
private void addComponents(Container pane){
pane.setLayout(null);
board = new JTextArea(1, JLabel.CENTER);
moveUp = new JButton("Up");
moveDown = new JButton("Down");
moveLeft = new JButton("Left");
moveRight = new JButton("Right");
moveUp.setBounds(185, 225, 130, 35);
moveLeft.setBounds(115, 280, 130, 35);
moveRight.setBounds(255, 280, 130, 35);
moveDown.setBounds(185, 335, 130, 35);
board.setEditable(false);
board.setBounds(115, 30, 270, 145);
board.setFont(new Font("Consolas", Font.BOLD, 12));
addActionListeners();
pane.add(board);
pane.add(moveUp);
pane.add(moveRight);
pane.add(moveLeft);
pane.add(moveDown);
}
private void addActionListeners(){
moveUp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
running = true;
buttonPress = 1;
}
});
moveDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 2;
}
});
moveLeft.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 3;
}
});
moveRight.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 4;
}
});
}
public void setLabelText(char[][] boardToShow){
board.setText(" ");
for(int i = 0; i < boardToShow.length; i++){
for(int j = 0; j < boardToShow[i].length; j++){
board.append(boardToShow[i][j] + " ");
}
board.append("\n ");
}
}
}
If you require my Board/Monster/Player classes I can post them, but I don't think the problem is with those classes.
Active polling (a.k.a. busy waiting) is when a program checks over and over whether some condition is true, and since we're talking about computers, this is usually done many times a second. It's bad because it means the process is constantly using up CPU to check for this condition, and, even worse, because it uses up so much of the CPU, it can prevent the condition from being able to become true in the first place (this is what is happening in your case).
I'll try to explain this with a metaphor, where your Main class (specifically your while(gui.running) loop) is the boss, and the GUI class is the employee. Imagine the boss comes to his employee every second and asks "Have you done what I asked you to do?". By doing so every second, not only is the boss wasting his time, but is actually preventing his employee from being able to do what he was asked to do.
That is exactly what is happening between your Main and GUI class. The value of buttonPress can not change when the while loop keeps running, and that's why sleeping (and printing to console, because that requires an IO operation which also blocks the thread for a while) makes it work, because the while loop stops executing for a short amount of time, giving your program the chance to change the value of buttonPress.
The solution
In order to solve this, let's go back to the boss/employee metaphor. Instead of checking every second, the boss should say, "When you are done doing this, come and tell me". That way he doesn't need to keep checking on the employee and the employee has time to do the task. Similarly, your GUI class should call a method in your Main class when the actionListeners are fired. Ultimately, as other people pointed out, your structure needs quite a bit of work and clean up, I would recommend you look into using the Model-View-Controller pattern.
However I will propose a solution which will work for the setup you currently have. Your Main class should look something like:
public class Main {
private Player playerOne;
private Monster monstMatt;
private Monster monstJon;
private Monster monstAbaad;
private ShowGUI gui;
private Board gameBoard;
public Main() {
//Add Player
playerOne = new Player(1000, "Player 1");
//Add monsters
monstMatt = new Monster(1000, "Matt");
monstJon = new Monster(1000, "Jon");
monstAbaad = new Monster(1000, "Abaad");
gui = new ShowGUI(this);
gameBoard = new Board();
gui.setLabelText(gameBoard.getBoard());
}
public movePlayerUp() {
movePlayer(0, -1);
}
public movePlayerDown() {
movePlayer(0, 1);
}
public movePlayerLeft() {
movePlayer(-1, 0);
}
public movePlayerRight() {
movePlayer(1, 0);
}
private movePlayer(x, y) {
playerOne.move(playerOne.getX() + x, playerOne.getY() + y);
}
}
And the GUI class:
public class ShowGUI {
private Main main;
public ShowGui(Main main) {
this.main = main;
createAndShow();
System.out.println("GUI Successfully Loaded");
}
private void addActionListeners(){
moveUp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerUp();
}
});
moveDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerDown();
}
});
moveLeft.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerLeft();
}
});
moveRight.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerRight();
}
});
}
/* All the other methods you have */
...
}
So instead of using flags (buttonPress=2), use methods which will be called when a certain action occurs. Again, this is not a perfect long-term solution, but it has the right gist and is the sort of pattern you should follow.
Having some problems updating a JTextField in a different class after reading a String from another JTextField. Here's the method in question:
public JTextField buyVowel(playerPlate player)
{
String get = input.getText();
String[] vowels = new String[]{"a","e","i","o","u"};
for(int i =0; i<vowels.length; i++)
{
if(get.equals(vowels[i]))
{
player.pMoney =- 250;
player.playerMoney.setText("$"+player.pMoney);
}
}
return player.playerMoney;
}
playerPlate is a separate class.
I'm using this method to determine what player the program should be modifying:
public playerPlate getCurrentPlayer()
{
if(currentPlayer == 1)
{
return player1;
}
else if(currentPlayer == 2)
{
return player2;
}
else
{
return player3;
}
}
player(s) 1, 2, and 3 are instances of playerPlate.
I want it to be modifying instance variables in this class:
package wheelOfFortune;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class playerPlate extends JPanel
implements ActionListener
{
public String pName;
public int pMoney = 500;
public int currentPlayer;
public JTextField playerMoney;
public playerPlate(String player, Color color, int currentPlayer)
{
setBorder(BorderFactory.createLineBorder(Color.BLACK,2));
setBackground(color);
pName = player;
JTextField playerNames = new JTextField(pName);
playerNames.setBorder(BorderFactory.createLineBorder(Color.BLACK,2));
playerNames.setEditable(false);
playerNames.setFont(new Font("Impact", Font.PLAIN, 24));
playerNames.setHorizontalAlignment(JTextField.CENTER);
playerNames.setBackground(Color.WHITE);
JTextField playerMoney = new JTextField("$"+pMoney);
playerMoney.setBorder(BorderFactory.createLineBorder(Color.BLACK,2));
playerMoney.setEditable(false);
playerMoney.setFont(new Font("Impact", Font.BOLD, 32));
playerMoney.setHorizontalAlignment(JTextField.CENTER);
playerMoney.setBackground(Color.WHITE);
Box b1 = Box.createVerticalBox();
b1.add(playerNames);
b1.add(Box.createVerticalStrut(5));
Box b2 = Box.createHorizontalBox();
b2.add(Box.createHorizontalStrut(60));
Box b3 = Box.createVerticalBox();
b3.add(playerMoney);
b3.add(Box.createVerticalStrut(8));
b2.add(b3);
b1.add(b2);
b1.add(Box.createVerticalStrut(5));
add(b1);
}
public void actionPerformed(ActionEvent e)
{
}
}
Here is the actionPerformed method within the main class:
public void actionPerformed(ActionEvent e)
{
JButton b = (JButton)e.getSource();
if(b==spin)
{
spinWheel(wheelStuff);
repaint();
}
if(b==next)
{
updatePlayer();
repaint();
}
if(b==reset)
{
letterBoard.reset();
updateCat();
repaint();
}
if(b==buyVowel)
{
buyVowel(getCurrentPlayer());
repaint();
}
}
The gist of what I want to happen, is when the user types a vowel into JTextField input, and clicks JButton buyVowel it subtracts $250 from their total money (pMoney). And displays the change on the GUI. After tinkering with this for a couple hours, I honestly have no idea why this isn't working. I keep receiving nullPointerExceptions when trying to use it. Thanks for your help.
Note: everything except for class playerPlate is in the same class. playerPlate is in a separate class.
You're shadowing the variable playerMoney in the constructor of playerPlate. The method buyVowel relies on playerPlate being instantiated when invoking setText, otherwise a NullPointerException will be thrown. Replace
JTextField playerMoney = new JTextField("$"+pMoney);
with
playerMoney = new JTextField("$"+pMoney);
Aside: Java naming conventions indicate that class names start with uppcase letters so use class names such as PlayerPlate.
I am trying to write a memory game. When we run app, "menu" appears first. The choices are one player and two player. When we click one player "level" selection part appears. Here choices are easy, medium, hard. Then one of them is clicked and game starts. But i am having trouble while implementing how to check selected cards equal or not. When a card is created, an id is assigned to it. And when two cards are clicked, I am checking ids. If ids are same, it returns "match", if it is not then "close" returns that means cards turn facedown. I am using MVC pattern. I have 9 classes. Card, Game, Level(enum), Menu, State(enum); views CardButton, GamePanel, LevelPanel, MenuPanel.
Class GamePanel:
package view;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import model.Game;
import model.State;
public class GamePanel extends JPanel {
private Game model;
ActionListener cardButtonActionListener;
private JFrame frame;
public GamePanel(Game gameModel, JFrame frame) {
int width=gameModel.getRowSize();
int height=gameModel.getColumnSize();
setLayout(new GridLayout(width,height));
this.model = gameModel;
this.cardButtonActionListener = new CardButtonActionListener();
this.frame = frame;
show();
}
public void show() {
for(int row=0; row<model.getRowSize(); row++) {
for (int col=0; col<model.getColumnSize(); col++) {
CardButton cardBtn = new CardButton(model.getCard(row, col));
cardBtn.addActionListener(cardButtonActionListener);
add(cardBtn);
}
}
frame.repaint();
}
public class CardButtonActionListener implements ActionListener {
private int i = 0;
CardButton b ,b2;
State currentState;
#Override
public void actionPerformed(ActionEvent e) {
if(i==0){
b = (CardButton) e.getSource();
b.setFaceUp();
JOptionPane.showInputDialog("id "+b.getCard().getValue());
i++;
}
else{
b2 = (CardButton) e.getSource();
b2.setFaceUp();
i--;
}
currentState = model.compareCards(b, b2);
if(currentState == State.Match){
b.setVisible(false);
b2.setVisible(false);
}
if(currentState == State.Close){
b.setFaceDown();
b2.setFaceDown();
}
if(currentState == State.Continue){
}
}
}
}
Class Game:
package model;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.JPanel;
import view.CardButton;
public class Game extends JPanel {
private Level level;
private ArrayList<ArrayList<Card>> board;
private int rowSize;
private int colSize;
private ArrayList<Card> cardList;
public Game() {
}
public void setLevel(Level level,int x) {
this.level = level;
initBoard(x);
}
private void initBoard(int x) {
// Create board according to level variable
int s = x;
rowSize = s;
colSize = s;
int a=rowSize*colSize/2;
int b=0;
this.board = new ArrayList<ArrayList<Card>>();
for(int row=0; row<s; row++) {
this.cardList = new ArrayList<Card>();
for (int col=0; col<s/2; col++) {
cardList.add(new Card(b));
cardList.add(new Card(b));
b++;
}
board.add(getCardList());
}
Collections.shuffle(board);
}
public ArrayList<Card> getCardList(){
Collections.shuffle(cardList);
return cardList;
}
public int getRowSize() {
return rowSize;
}
public int getColumnSize() {
return colSize;
}
public Card getCard(int row, int col) {
return board.get(row).get(col);
}
public State compareCards(CardButton b, CardButton b2) {
int v1, v2;
v1 = b.getCard().getValue();
v2 = b2.getCard().getValue();
if(b.getCard()!= null && b2.getCard()!= null){
return State.Continue;
}else{
if(v1 == v2){
return State.Match;
}
else{
return State.Close;
}
}
}
}
GamePanel takes state info and decides card which position card should stay: faceup, facedown. But I couldn t implement action performed method(in GamePanel) and compareCards method(in Game) correctly. When I run code, I get null pointer exeption. Because I can not take 2 buttons info. One of them always stays null. I probably need to change this part:
if(i==0){
b = (CardButton) e.getSource();
b.setFaceUp();
JOptionPane.showInputDialog("id "+b.getCard().getValue());
i++;
}
else{
b2 = (CardButton) e.getSource();
b2.setFaceUp();
i--;
}
But I don t know how I can correct. Thank you.
Edit: Whole project is here http://cnv.as/21qoh
The b and b2 variables are null when the CardButtonActionListener is created. So the first time actionPerformed() is called and you fall through your if(i==0) conditional statement, only one of b or b2 gets assigned the return value from e.getSource(). So when you call currentState = model.compareCards(b, b2); either b or b2 will still be null which as you found out will cause that method to throw a null pointer exception.
It looks like this isn't so much a coding error as it is that your design needs some additions. The main reason is each card has its own instance of CardButtonActionListener and when clicked, this listener class isn't aware of any other cards that have already been clicked. For a quick remedy to this you could add a public static Card lastClicked; member variable to your Game class (disclaimer: note I said 'quick', not 'good' as this violates good OOP design and in a multithreaded app would give you tons of trouble... but for a single threaded app like yours if you just want it to work this may be ok for you - but be warned that using public static variables like this is definitely not a good habit to get into). Then you could modify your CardButtonActionListener.actionPerformed() like this (note that I did away with the 'i' variable):
#Override
public void actionPerformed(ActionEvent e) {
CardButton justClickedButton = e.getSource();
justClickedButton.setFaceUp();
CardButton previouslyClickedButton = Game.lastClicked;
if(previouslyClickedButton == null){
JOptionPane.showInputDialog("id "+justClickedButton.getCard().getValue());
previouslyClickedButton = justClickedButton;
}
else{
currentState = model.compareCards(justClickedButton, previouslyClickedButton);
if(currentState == State.Match){
justClickedButton.setVisible(false);
previouslyClickedButton.setVisible(false);
}
if(currentState == State.Close){
justClickedButton.setFaceDown();
previouslyClickedButton.setFaceDown();
}
if(currentState == State.Continue){
}
previouslyClickedButton = null;
}
}