我正在開發一個基于文本的視頻游戲并為其創建了一個 GUI,但是,我在讓我的兩個類協同作業時遇到了問題。我的 GUI 類中有一個文本欄位,用于接收來自用戶的輸入,我想將其發送到我的 Player 類,以便它可以比較它并執行適當的方法。我對編程還是很陌生,所以我希望你們中的一些人能夠幫助我。請原諒可怕的代碼。
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");
}
}
}
uj5u.com熱心網友回復:
您的代碼中有一些好東西以及幾個可能妨礙您將 GUI 與當前控制臺程式代碼混合的主要問題,其中包括:
- 您確實將您的程式邏輯分離為單獨的類,例如 Player、Map 和 Room 類,這是一件非常好的事情,但您仍然可以對邏輯進行更多分離,這樣做可以簡化您的代碼制作更容易除錯和增強。
- Player 類包含一個 Player 實體,我不知道為什么。外主類也許應該重新命名來自
Player于Game類和Player類應該是一個完全獨立的類,一個是擁有的狀態,單播放器實體,而不應造成的自身情況為你做. 關鍵是要努力“oop-ify”你的代碼,尤其是代碼的“模型”部分,即保存非用戶界面程式邏輯的代碼,使其更加面向物件,因為這會讓它變得更更容易讓您將 GUI 或任何其他界面連接到您的模型。 - 您的模型(再次命名為 Player)在其中包含用戶界面 (UI) 代碼:其中包含 Scanner 輸入代碼和 println 陳述句。這是有問題的,尤其是現在您想要增強程式并使其與 GUI 一起作業。首先,您必須做的關鍵事情是將用戶界面代碼(再次從用戶獲取輸入并將輸出顯示給用戶的代碼)與模型類分開,模型類同樣是保存程式邏輯。
- 您的 Player 類有很多非 Player 代碼將其弄亂。一個基本的 OOP 原則是一個類應該有一個單一的職責(稱為“單一職責規則”),所以你的 Player 類應該只包含封裝 Player 狀態所需的代碼(例如名稱、房間,也許其他屬性,例如如力量、武器、健康、智力……)和玩家行為(允許玩家與其他職業互動的方法),或多或少。
- 按照同樣的思路,似乎所有程式資料和行為似乎都被硬連接到驅動程式的這個單一的主要 Player 類中。這是非常有限的,并且會阻礙您創建具有許多房間、房間內物品以及諸如此類的豐富游戲環境的能力。分而治之——從主程式中獲取該資訊并將其放入符合 OOP 的類中,包括具有可能包含的專案欄位的 Room 類,該類知道自己與其他房間的連接。
- 您的 GUI 程式擴展了 Player 物件。這是一個主要問題,因為該程式結構沒有通過基本的繼承測驗,即“is-a”測驗:邏輯上 GUI 是更具體的玩家型別(就像狗是更具體的動物型別,通過“is-a”測驗的結構)?不,它不是,雖然這種區別可能看起來是懸而未決的,但由于這種繼承,您可能會嘗試在 GUI 類中使用 Player 欄位,但如果您嘗試這樣做,代碼將無法正常作業。洗掉此繼承,而是嘗試通過“組合”連接類,其中一個類包含另一個類的實體,而不是擴展另一個類。因此,例如,GUI 可能保存一個 Player 變數而不是從 Player 擴展。
底線:在嘗試添加 GUI之前,您需要完全“OOP 化”您的代碼
為了使程式能夠與 GUI 很好地結合,我建議使您的底層邏輯代碼(非用戶界面邏輯代碼)更加面向物件,所有類都具有單一職責,類是易于隔離測驗(遠離 GUI 或任何用戶界面)。先從第一的原則,程式將多打造更簡單。
因此,例如,可以考慮用于冒險游戲的類包括:
- 一個 Direction 類,或者更好的一個 Direction enum。
這將封裝“方向”的概念,并將用于游戲中的物件,以了解它們的方向,并能夠使用常量而不是字串將其傳達給其他物件。列舉的用戶將允許編譯器檢查方向是否被正確使用,因為如果您使用字串,例如“West”,編譯器將不會自動知道您是否輸入錯誤字串并使用“Best”代替。
像這樣簡單的事情,例如:
public enum Direction {
NORTH, EAST, SOUTH, WEST
}
- GameRoom 類
這將包含告訴它在房間網格中的位置的資訊,名稱和描述屬性的字串,以及一個玩家欄位或一個List游戲玩家(如果允許多個),可能包含這樣的代碼:... .
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.
一個更完整的程式會將資料完全從代碼中分離出來,并允許檔案 I/O 將房間網格讀入程式,可能通過創建一個或多個 CSV 檔案來保存房間資訊,或者如果資料是預期的要增長并變得更復雜,然后是關系資料庫,例如使用其中一種 SQL 風格的資料庫。
uj5u.com熱心網友回復:
由于您已經使 GUI 擴展了播放器,因此您需要做的就是從輸入欄位上的 ActionListener 呼叫所需的函式。
commandLine.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String input = commandLine.getText();
if (input == "go north") {
GoNorth();
}
// .....
}
});
然而,事情相當混亂。對于初學者來說,GUI 沒有真正的理由來擴展播放器。我建議您將 GUI 類與 Player 分開。這樣,您就不需要使用 Scanner 或 Print,而是在 GUI 類中收集所有 GUI 功能。
例如:
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");
}
}
您需要在 GUI 類中實作一個列印功能,在訊息框中添加一行文本,但我相信您一開始就打算這樣做。
在 GUI 類中,您將擁有對播放器的參考,因此讀取輸入欄位將如下所示:
commandLine.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String input = commandLine.getText();
switch(input) {
case "go north":
player.goNorth();
break;
...
}
}
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/323106.html
