I'm creating a plugin for an event, this event is just: when you stop moving, you loose. I created everything, but my check to know if a player moves or not doesn't work:
I tried to make an array and when a player move event it's added, but it doesn't work,
#EventHandler
public static void OnPlayerMoov(PlayerMoveEvent e) {
if(!playerHaveMooved.contains(e.getPlayer())) {
playerHaveMooved.add(e.getPlayer());
}
}
public static boolean isMoving(Player p){
System.out.println(Respawn.playerHaveMooved.contains(p));
return Respawn.playerHaveMooved.contains(p);
}
I tried to use velocity; it doesn't work,
public static boolean isMoving(Player p){
return (p.getVelocity().length() == 0);
}
It doesn't work. It kills me when I am moving.
Do you have a solution?
The first don't work because you just check if the player already move a time, which will always be true.
The second about velocity check only if player get knockbacked for example.
First solution
Save last time of move, and check each few time if it stop move.
In this example, I will force player to move each seconds.
private final HashMap<Player, Long> timeMove = new HashMap<>(); // map for data
#Eventhandler
public void onMove(PlayerMoveEvent e) {
timeMove.put(e.getPlayer(), System.currentTimeMillis()); // update last move
}
public void startChecking(JavaPlugin pl) {
pl.getServer().getScheduler().runTaskTimer(pl, () -> {
long current = System.currentTimeMillis();
for(Player p : Bukkit.getOnlinePlayers()) {
long last = timeMove.getOrDefault(p, 0);
long diff = current - last;
if(diff > 1000) { // 1000 = second
// player don't move
}
}
}, 20, 20);
}
Second solution
Check each X time from last and current location
private final HashMap<Player, Location> lastLoc = new HashMap<>(); // map for data
public void startChecking(JavaPlugin pl) {
pl.getServer().getScheduler().runTaskTimer(pl, () -> {
for(Player p : Bukkit.getOnlinePlayers()) {
Location lastPlayerLoc = lastLoc.get(p); // get loc, or keep null if not registered
if(lastPlayerLoc != null) {
double distance = lastPlayerLoc.distance(p.getLocation());
if(distance == 0.0) {
// here the player don't move
continue; // go to next player
}
}
lastLoc.put(p, p.getLocation()); // update loc
}
}, 20, 20);
}
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;
...
}
}
});
I'm trying to understand how BufferStrategy is working. I've made a simple app that is drawing some Sprite objects over and over again every 60 frames per second. I can see the images on the canvas but they are flickering for some reason. Can you tell me why? If you don't want to read all the code, just focus on the Paint method and the main game loop.
public abstract class Frame extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;
//------------------------Variables------------------------//
private boolean initialized = false;
private boolean fullScreen = false;
public boolean running = true;
private GraphicsDevice vc;
private BufferStrategy strategy;
private Graphics2D g2d;
private int timer = 0;
//------------------------Variables------------------------//
public final void __init__() {
this.addKeyListener(this); //Adding key listener.
this.setVisible(true);
this.setIgnoreRepaint(true);
this.createBufferStrategy(2);
this.strategy = this.getBufferStrategy();
this.setResizable(false);
this.initialized = true; //Initialized.
}
//Create a window.
public final void set_mode(int width, int height, boolean fullScreen) throws NotInitializedException {
//Frame not initialized.
if (!this.initialized) {
throw new NotInitializedException("Frame not initialized!");
} else {
//--------------------Variables--------------------//
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
//--------------------Variables--------------------//
//Setting vc equal to the default graphics device of the system.
this.vc = env.getDefaultScreenDevice();
//Full Screen.
if (fullScreen) {
this.fullScreen = fullScreen;
//Creating the display mode.
DisplayMode mode = new DisplayMode(width, height, 32, DisplayMode.REFRESH_RATE_UNKNOWN);
//If display settings are allow to change display mode.
if (this.vc.isDisplayChangeSupported()) {
this.vc.setDisplayMode(mode); //Change to the new mode.
}
//Set the screen to full screen.
this.vc.setFullScreenWindow(this);
} //Not full screen.
else {
this.setSize(width, height);
this.addWindowListener(new WindowHandler(this));
}
}
}
//This mehod is been called from Sprite.draw() method.
public void paint(Sprite sprite) {
do {
do {
this.g2d = (Graphics2D) this.strategy.getDrawGraphics();
g2d.drawImage(sprite.getImage(), sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight(), null);
this.g2d.dispose();
} while (this.strategy.contentsRestored());
this.strategy.show();
} while (this.strategy.contentsLost());
}
public final int tick(int fps) {
int ms = 1000 / fps;
timer += 1;
//Try to sleep.
try {
Thread.sleep(ms);
} //Thread interrupted.
catch (Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
return timer;
}
public class MyApp extends Frame {
public static String BG_PATH = "C:/Users/admin/Desktop/game/bg.jpg";
public static String PL_PATH = "C:/Users/admin/Desktop/game/player.png";
public static String EN_PATH = "C:/Users/admin/Desktop/game/enemy.png";
private int speed = 20;
private boolean left = false;
private boolean right = false;
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
this.close();
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = false;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = false;
}
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void onWindowClose() {
// TODO Auto-generated method stub
}
//This method starts the game.
public void startApp() {
this.__init__(); //initialize the frame.
Sprite bg = new Sprite(this, Picture.load(BG_PATH), "bg"); //Create a new sprite obj
this.set_mode(bg.getWidth() - 500, bg.getHeight() - 100, false); //Create the window.
Sprite player = new Sprite(this, Picture.load(PL_PATH), "player");
player.setX(bg.getWidth() / 3);
player.setY(bg.getHeight() / 2);
//Game Main Loop
while (this.running) {
bg.draw();
player.draw();
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
}
}
}
}
You have a few issues that I can clearly spot.
First off, you must understand that drawing in Swing/Awt is not known for it's speed, it's actually known for the exact opposite. The fact is, even though you're telling your game to run at 60fps, it probably can't do it. Thus the flickering. Essentially, your application is caught within a "drawing-data race", and it's always slightly behind. Try something real quick; set Thead.Sleep() to 10 or 30. I feel as though that might solve your problem entirely.
If not, consider the second problem. You're calling this.strategy.show(); inside the player.Draw(); function, when it needs to be the last thing that you do in your draw call. In other words:
//Game Main Loop
while (this.running) {
bg.draw(); // DON'T SWAP BUFFERS!
player.draw(); // DON'T SWAP BUFFERS!
// draw other entities
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
this.strategy.show(); // This needs to be the very last thing you do. You're swapping buffers here, which only needs to be done once per frame.
}
My guess is you're also swapping your buffer during the bg.Draw(); function as well, and that is actually why your screen is flickering. So those are two things right there. Try lowering the frames per second down to something that Java can actually handle, and don't swap your buffer until the VERY end of your drawing routine.
Some other recommendations:
Use direct variable access instead of getters and setters. There is overhead when calling "player.getX()". When you could just call "player.x".
There is no future in Java 2D game development. Swing/AWT(which you're using) is entirely dead. If you want to make a game, and be serious about it, take the time to learn OpenGL(In your case it would be Lwjgl as a wrapper).
I'm trying to design a game like BeJeweled using java language
This is where I've reached so far :
public class Game {
public static void main(String[] args) throws InterruptedException {
final JFrame window = new JFrame();
window.setSize(508,669);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Grid g=new Grid();
Game.obj(g);
window.add(g);
window.setVisible(true);
window.setResizable(false);
window.repaint(2);
window.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent e) {
int x=e.getX();
int y=e.getY();
for(int i=0;i<10;i++) {
for(int j=0;j<16;j++){
if(x>i*50+5 && x<i*50+54 && y>j*40+26 && y<j*40+26+39){
g.b[i][j]=g.a[i][j];
int q = x;
int w=y;
int r =x;
int t =y;
q-=50;
w-=40;
if( i>0&&g.a[i-1][j]==g.b[i][j]){
g.a[i][j]=0;
g.a[i-1][j]=0;
}
if( j>0&&g.a[i][j-1]==g.b[i][j]){
g.a[i][j]=0;
g.a[i][j-1]=0;
}
r+=50;
t+=40;
if(i<9&&g.a[i+1][j]==g.b[i][j]){
g.a[i][j]=0;
g.a[i+1][j]=0;
}
if(j<15&&g.a[i][j+1]==g.b[i][j]){
g.a[i][j]=0;
g.a[i][j+1]=0;
}
}
}
}
SwingUtilities.updateComponentTreeUI(window);
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
}
public static void obj(Grid g){
Random r =new Random();
for(int k=0;k<10;k++)
for(int l=0;l<16;l++)
g.a[k][l]=1+r.nextInt(4);
}
}
class Grid extends JPanel {
private Graphics Graphics;
#Override
public void paint(Graphics g) {
final Graphics g1 = g ;
this.setGraphics(g);
for(int i=0;i<600;i+=50)
for(int j=0;j<400;j+=40)
g.drawRect(i, j, i+50, j+40);
for(int i=0;i<10;i++)
for(int j=0;j<16;j++){
if(a[i][j]== 0) g.setColor(Color.WHITE);
if(a[i][j] == 1) g.setColor(Color.ORANGE);
if(a[i][j] == 2) g.setColor(Color.red);
if(a[i][j] == 3) g.setColor(Color.GREEN);
if(a[i][j] == 4) g.setColor(Color.cyan);
g.fillRect(i*50+1, j*40+1, 49, 39);
}
// Mouselis mouseptr = new Mouselis(g);
// this.addMouseListener(mouseptr);
// this.addMouseMotionListener(mouseptr);
}
public void setGraphics(Graphics Graphics) {
this.Graphics = Graphics;
}
int [][] a= new int[10][16];
int [][] b= new int[10][16];
}
In this design only the up down left right of rectangles are being checked for same color in the method mouse clicked. How can i make it check all the near rectangles for the one having same color ?
Please help Thanks
You have four if statements that check the adjacent rectangles for the same color as the one that was clicked. Here is the last one:
if(j<15&&g.a[i][j+1]==g.b[i][j]){
g.a[i][j]=0;
g.a[i][j+1]=0;
}
j, the vertical grid coordinate, is compared with 15 to be sure that j+1 is within bounds. Also, a rectangle in the 2d-array a is checked to see if it is equal to the rectangle in the 2d-array b that got clicked. If it is, you set it to 0 for Color.WHITE. Because the indices are i and j+1 this checks the rectangle directly below the clicked one.
To check a rectangle that is diagonally adjacent, change both indices by one.
The rectangle to the bottom right is g.a[i+1][j+1]. To access this rectangle without an error you need to be sure that both i+1 and j+1 are within the bounds of the array to avoid an error.
So, the if statement is
if(j<15 && i < 9 && g.a[i+1][j+1]==g.b[i][j]){ ...
You can figure out the rest.
Java is a strong Object-Oriented programming language - especially before Java8: I think that you should really work with more Objects that would hold their own responsibilities and behaviours.
Once you'll have a class that represents a Jewel, just implement some recursive method to know if it's part of some "Jewel streak". For example:
public class Jewel {
private final JewelType type; // Could be an Enum for example
// Many other stuff to add (like a constructor at least!)
public Collection<Jewel> getStreak() {
final Set<Jewel> res = new HashSet<>();
this._getStreak(res);
return res;
}
private void _getStreak(final Set<Jewel> streak) {
// Just leave the method if we've already visited this Jewel
if(!streak.add(this)) {
return;
}
// Assuming that this method returns a Collection of the Jewels that are stored West, North, South and East from this
for(final Jewel neighbour : Grid.getInstance().getJewelsAround(this)) {
if(neighbour.getJewelType().equals(this.getJewelType())) {
neighbour._getStreak(streak);
}
}
}
}
When trying to see if some Jewel is part of a streak that is long enough to be "destroyed", just check something like this:
Jewel b = Grid.getInstance().getJewelForCoordinates(x, y);
final Collection<Jewel> streak = b.getStreak();
if(streak.size() >= Game.MINIMUM_STREAK_SIZE) {
Grid.getInstance().destroyJewels(streak);
}
I just don't even have access to any Java editor/compiler right now so please excuse me, would my code present any mistyping. Even though I recommend to do many changes, I hope it can help you :)
By the way, if you don't want to override all the methods described by the MouseListener interface, use the following:
window.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent event) {
// Code to be executed on mouseClicked().
}
});
MouseAdapter is an abstract class that implements MouseListener and does nothing when receiving any event. You just have to override the methods that handle the events you're interested in ;)