Thread errors when running Java game - java

I am getting some strange errors when running my Java game. I've been working on this game for weeks as a project for my computer science class and it is due on 6/4/2015, so I appreciate any help. Initially I wrote the game in a class called Game and ran it from a static method called run. Later, I decided to add a GUI menu in a class called Control. To run the game now, I call the main method in the Control class. This menu has a button with an action listener. When the button is clicked, the run method in the Game class is called. If I click and run the run method directly the game works fine. But if I click the button which calls the run method it draws a frame, but not the actual game.
Here is my code for the GUI:
public class Control extends JFrame implements ActionListener {
// JPanel
public JPanel pnlButton = new JPanel();
// Buttons
public JButton btnAddFlight = new JButton("Multiplayer");
public JButton single = new JButton("Singleplayer");
public Control() throws IOException,InterruptedException {
super("Bouncy Ball");
//Set button size
btnAddFlight.setBounds(150, 400, 220, 30);
single.setBounds(150,350,220,30);
// JPanel bounds
pnlButton.setBounds(0, 0, 500, 500);
pnlButton.setBackground(Color.WHITE);
// Adding the Bouncy Ball logo to JPanel
String path = "gg.jpg";
File file = new File(path);
BufferedImage image = ImageIO.read(file);
JLabel label = new JLabel(new ImageIcon(image));
label.setBounds(179,50,150,150);
//Action Listener setup
single.addActionListener(this);
//add buttons and title logo to JPanel
pnlButton.add(btnAddFlight);
pnlButton.add(label);
pnlButton.add(single);
//Set up and add the instructions to the JPanel
JLabel gg = new JLabel("Welcome to Bouncy Ball, a game where you have to manipulate");
gg.setFont(new Font("Serif", Font.PLAIN, 18));
gg.setBounds(0,10,500,500);
pnlButton.add(gg);
JLabel f = new JLabel("the window size with WASD to win! Click the buttons to start.");
f.setFont(new Font("Serif", Font.PLAIN, 18));
f.setBounds(0,28,500,500);
pnlButton.add(f);
pnlButton.setLayout(null);
//Add JPanel to JFrame
this.add(pnlButton);
// JFrame properties
setSize(500, 500);
setTitle("Bouncy Ball");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);;
}
#Override
public void actionPerformed(ActionEvent submitClicked) {
try{
isClicked = true;
Game.run();
}
catch(InterruptedException a){}
}
public static void main(String[] args) throws IOException,InterruptedException{
new Control();
}
}
and here is the run method in my game class:
public static void run() throws InterruptedException {
//Setting up JFrame
frame = new KeyFrame("Bouncy Ball");
frame.setSize(1000, 1000);
//Setting up Scanner and get user level input
Scanner scan = new Scanner (System.in);
System.out.println("Level one, two, three, or four?(must be an int, four is multiplayer)");
f = scan.nextInt();
//Display JFrame
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.toFront();
//Add Game JPanel
game = new Game();
frame.add(game);
//Setting up game basics
b = new Ball(game,0,0,30,30);
setUpLevel();
objList.add(0,b);
//Main Game Loop
while (true) {
//Request Focus
game.requestFocusInWindow();
//Update current Time
lastStartTime = System.currentTimeMillis();
//Move the ball and the player
p.move();
b.move();
//Refresh JPanel
game.repaint();
Thread.sleep(10);
//Check if the player wins or loses
checkWin();
checkLoss();
}
}
If I call the run method directly it works, but not if I click the button in the GUI. I have a feeling that it is a thread issue, because the main thread ends once run is called. I'm not a 100% percent sure as to the cause though. I'd appreciate any responses, I've been working on this game for weeks as a project for my computer science class and it is due on 6/4/2015.

Swing is single threaded - all painting, events, etc...occur on this thread (named the EDT). The Game.run method is called on the EDT, which in turn executing a long running task (eg while (true), Thread.sleep) - this prevents the EDT from doing anything else. To perform animation, consider using a Thread, or better yet a Swing Timer.

Related

Java Swing open form on button click

I am developing a card flip flop game in java Swing(using java swing for 1st time). I am using netbeans, I have a menu like new game.. I want that when the user clicks new game button then the game starts. But i dont know how to do this, like when user clicks button , then in event handling action function,is it like this?
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
JFrame myframe = new JFrame();
//and the game functionality here
}
You are doing the right thing if you want to have a new window open upon click a button. In your sample code, you need to make the new frame visible.
public class NewGame {
public static void main(String[] args) {
JFrame frame = new JFrame("Start up frame");
JButton newGameButton = new JButton("New Game");
frame.setLayout(new FlowLayout());
frame.add(newGameButton);
frame.setVisible(true);
newGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JFrame newGameWindow = new JFrame("A new game!");
newGameWindow.setVisible(true);
newGameWindow.add(new JLabel("Customize your game ui in the new window!"));
newGameWindow.pack();
}
});
frame.pack();
}
}

Moving a JLabel for a game

I am coding a Backgammon game that plays by itself. While the backend code is mostly done, me and my colleague have pretty much no experience in GUI coding. We used the Designer given by a plugin for Eclipse and most of the code was generated.
So here's the thing. Right now there is a JFrame which has a JPanel in which are 2 JLabels, one for the background and one for 1 playing piece (when I figure this out, the rest of them will be added). These are all in an initialize() method which is run through EventQueue.invokeLater(new Runnable()). After the board and the piece are drawn, the startGame() method is called, which finally comes to a method called movePiece(), with the sole purpose of moving that JLabel piece I talked about before.
I have tried mostly everything I can think of, I have scoured the forums and only found people talking about layouts (which I may or may not be using, as I said, generated code) and the darn JLabel won't move when I call setLocation() inside the movePiece() method. It always throws a NullPointerException at that line which is bizarre because the image is already drawn. I will include part of the code below.
I know this will probably look like some of the worse codes ever, but please bear with my lack of skill and help me make this better. Any insights?
public class Main{
public JLabel image1;
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main window = new Main();
window.frame.setVisible(true);
new Main(1);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Main() {
initialize();
}
public Main(int i){
startGame();
}
public void startGame() {
//game code
movePiece(Move.getPos(), Move.getDest());
//rest of code
}
public void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 1023, 617);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(800, 700));
JPanel panel_1 = new JPanel();
panel_1.setPreferredSize(new Dimension(200, 700));
frame.getContentPane().add(panel_1, BorderLayout.EAST);
JButton btnNewButton = new JButton("Start Game");
/* ACTION LISTENER FOR THE BUTTON */
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
});
btnNewButton.setFocusable(false);
btnNewButton.setForeground(SystemColor.windowBorder);
btnNewButton.setBackground(SystemColor.menu);
btnNewButton.setVerifyInputWhenFocusTarget(false);
btnNewButton.setRequestFocusEnabled(false);
btnNewButton.setRolloverEnabled(false);
btnNewButton.setFont(new Font("Verdana", Font.PLAIN, 11));
btnNewButton.setDebugGraphicsOptions(DebugGraphics.NONE_OPTION);
/* ADDING THE BTN TO PANEL 1 TO THE RIGHT */
panel_1.add(btnNewButton);
JPanel parentPanel = new JPanel();
parentPanel.setFocusable(false);
parentPanel.setEnabled(false);
parentPanel.setDoubleBuffered(false);
parentPanel.setDebugGraphicsOptions(DebugGraphics.NONE_OPTION);
parentPanel.setIgnoreRepaint(true);
parentPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
parentPanel.setBorder(null);
parentPanel.setPreferredSize(new Dimension(800, 700));
parentPanel.setBackground(SystemColor.menu);
frame.getContentPane().add(parentPanel, BorderLayout.WEST);
/* BACKGAMMON BOARD LABEL-->PANEL */
ImageIcon imageIcon = new ImageIcon(
new ImageIcon("images/bg.png").getImage()
.getScaledInstance(800, 551, Image.SCALE_SMOOTH));
parentPanel.setLayout(null);
ImageIcon imageIcon1 = new ImageIcon(
new ImageIcon("images/pl.png").getImage()
.getScaledInstance(50, 50, Image.SCALE_SMOOTH));
image1 = new JLabel("");
image1.setBounds(37, 36, 50, 50);
parentPanel.add(image1);
image1.setIcon(imageIcon1);
JLabel bg = new JLabel("");
bg.setBounds(10, 11, 800, 551);
bg.setIcon(imageIcon);
parentPanel.add(bg);
}
void movePiece(int pos, int dest) {
//null exception happens here, ignore the arguments
image1.setLocation(37 + 50,36);
}
You create an instance of Main and make it visible...
Main window = new Main();
window.frame.setVisible(true);
You then create a new instance of Main, which, through it's constructor, calls your moviePiece method...
new Main(1);
But the new instance of Main has nothing to do with the previous instance or any of the components which they created.
Instead, trying doing...
window.movePiece(Move.getPos(), Move.getDest());
instead of new Main(1);

How to display one Jframe at a time? [duplicate]

I'm trying to make a little game that will first show the player a simple login screen where they can enter their name (I will need it later to store their game state info), let them pick a difficulty level etc, and will only show the main game screen once the player has clicked the play button. I'd also like to allow the player to navigate to a (hopefully for them rather large) trophy collection, likewise in what will appear to them to be a new screen.
So far I have a main game window with a grid layout and a game in it that works (Yay for me!). Now I want to add the above functionality.
How do I go about doing this? I don't think I want to go the multiple JFrame route as I only want one icon visible in the taskbar at a time (or would setting their visibility to false effect the icon too?) Do I instead make and destroy layouts or panels or something like that?
What are my options? How can I control what content is being displayed? Especially given my newbie skills?
A simple modal dialog such as a JDialog should work well here. The main GUI which will likely be a JFrame can be invisible when the dialog is called, and then set to visible (assuming that the log-on was successful) once the dialog completes. If the dialog is modal, you'll know exactly when the user has closed the dialog as the code will continue right after the line where you call setVisible(true) on the dialog. Note that the GUI held by a JDialog can be every bit as complex and rich as that held by a JFrame.
Another option is to use one GUI/JFrame but swap views (JPanels) in the main GUI via a CardLayout. This could work quite well and is easy to implement. Check out the CardLayout tutorial for more.
Oh, and welcome to stackoverflow.com!
Here is an example of a Login Dialog as #HovercraftFullOfEels suggested.
Username: stackoverflow Password: stackoverflow
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import javax.swing.*;
public class TestFrame extends JFrame {
private PassWordDialog passDialog;
public TestFrame() {
passDialog = new PassWordDialog(this, true);
passDialog.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new TestFrame();
frame.getContentPane().setBackground(Color.BLACK);
frame.setTitle("Logged In");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
});
}
}
class PassWordDialog extends JDialog {
private final JLabel jlblUsername = new JLabel("Username");
private final JLabel jlblPassword = new JLabel("Password");
private final JTextField jtfUsername = new JTextField(15);
private final JPasswordField jpfPassword = new JPasswordField();
private final JButton jbtOk = new JButton("Login");
private final JButton jbtCancel = new JButton("Cancel");
private final JLabel jlblStatus = new JLabel(" ");
public PassWordDialog() {
this(null, true);
}
public PassWordDialog(final JFrame parent, boolean modal) {
super(parent, modal);
JPanel p3 = new JPanel(new GridLayout(2, 1));
p3.add(jlblUsername);
p3.add(jlblPassword);
JPanel p4 = new JPanel(new GridLayout(2, 1));
p4.add(jtfUsername);
p4.add(jpfPassword);
JPanel p1 = new JPanel();
p1.add(p3);
p1.add(p4);
JPanel p2 = new JPanel();
p2.add(jbtOk);
p2.add(jbtCancel);
JPanel p5 = new JPanel(new BorderLayout());
p5.add(p2, BorderLayout.CENTER);
p5.add(jlblStatus, BorderLayout.NORTH);
jlblStatus.setForeground(Color.RED);
jlblStatus.setHorizontalAlignment(SwingConstants.CENTER);
setLayout(new BorderLayout());
add(p1, BorderLayout.CENTER);
add(p5, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jbtOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (Arrays.equals("stackoverflow".toCharArray(), jpfPassword.getPassword())
&& "stackoverflow".equals(jtfUsername.getText())) {
parent.setVisible(true);
setVisible(false);
} else {
jlblStatus.setText("Invalid username or password");
}
}
});
jbtCancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
parent.dispose();
System.exit(0);
}
});
}
}
I suggest you insert the following code:
JFrame f = new JFrame();
JTextField text = new JTextField(15); //the 15 sets the size of the text field
JPanel p = new JPanel();
JButton b = new JButton("Login");
f.add(p); //so you can add more stuff to the JFrame
f.setSize(250,150);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Insert that when you want to add the stuff in. Next we will add all the stuff to the JPanel:
p.add(text);
p.add(b);
Now we add the ActionListeners to make the JButtons to work:
b.addActionListener(this);
public void actionPerforemed(ActionEvent e)
{
//Get the text of the JTextField
String TEXT = text.getText();
}
Don't forget to import the following if you haven't already:
import java.awt.event*;
import java.awt.*; //Just in case we need it
import java.x.swing.*;
I hope everything i said makes sense, because sometimes i don't (especially when I'm talking coding/Java) All the importing (if you didn't know) goes at the top of your code.
Instead of adding the game directly to JFrame, you can add your content to JPanel (let's call it GamePanel) and add this panel to the frame. Do the same thing for login screen: add all content to JPanel (LoginPanel) and add it to frame. When your game will start, you should do the following:
Add LoginPanel to frame
Get user input and load it's details
Add GamePanel and destroy LoginPanel (since it will be quite fast to re-create new one, so you don't need to keep it memory).

Changing layout/frame in response to a button press

I'm trying to make a little game on Java using the Swing components (Boggle-type game).
The way I have it set up right now, it basically opens up to the game right away - but I want to have a start up window with two buttons - "Tutorial" and "Play". I already have the functionality (my Tutorial button just opens a new Window with all the things on it) I'm just not sure how to create a second JFrame and then switch to it when I press Play (or rather, create a JFrame, then switch to the one I've already created when the JButton is pressed). I guess I could cause a new JFrame to open on the same location and the old one to become non-visible - but I was hoping for a simpler solution.
I also want to do this on completion of the game, switching again automatically to a little stat page - so any info will be appreciated.
This is what I have so far in case you guys want to see my code (I haven't yet hooked up the Enter key send the userWord to be validated and scored in my other classes, or filled in the tileGrid with Tile Objects, or the timer.... but that will all come later!)
public class Game implements Runnable {
public void run(){
final JFrame frame = new JFrame("Boggle");
frame.setLocation(500,200);
// Input - holds typing box
final JLetterField typingArea = new JLetterField(1);
typingArea.setFocusTraversalKeysEnabled(false);
typingArea.setEditable(true);
typingArea.setFocusable(true);
typingArea.requestFocusInWindow(); //also this request isn't being granted..
//if anyone could explain why i would love you
// I want the focus on the TextField on startup
frame.add(typingArea, BorderLayout.SOUTH);
typingArea.addKeyListener(new KeyAdapter() {
public void keyPressed (KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) { // enter key is pressed
String userWord = typingArea.getText().toLowerCase();
typingArea.setText("");
}
}
});
final JLabel status = new JLabel("Running...");
// Main playing area
GridLayout tileGrid = new GridLayout(4,4);
final JPanel grid = new JPanel(tileGrid);
frame.add(grid, BorderLayout.CENTER);
// Reset button
final JPanel control_panel = new JPanel();
frame.add(control_panel, BorderLayout.NORTH);
final ImageIcon img = new ImageIcon("Instructions.png", "My Instructions...");
final JButton info = new JButton("Help");
info.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final JFrame infoFrame = new JFrame("Tutorial");
infoFrame.setLocation(500,50);
JLabel tutorialImg = new JLabel(img);
int w = img.getIconWidth();
int h = img.getIconHeight();
infoFrame.setSize(w, h);
infoFrame.add(tutorialImg);
infoFrame.setVisible(true);
}
});
control_panel.add(info);
// Put the frame on the screen
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Game());
}
}
use CardLayout instead of second JFrame, your concept is heading to OutOfMemory
use JFrame.pack(after switch betweens Cards in CardLayout) if you want to change JFrames bounds on runtime,

JFrame not updating when I call repaint()

I have a chess game I'm trying to make, and I'm trying to call the repaint() method on one of my JFrames in the game loop. This particular JFrame shows the total kills for each player. I'm pretty sure repaint() is actually being called, but for some reason it doesn't seem to be updating my JLabels correctly, which are supposed to hold the number of kills values for each player.
Here's my code for the custom JFrame extension class which houses the JLabels representing the kills.
private ChessGame game;
private JPanel killsPanel;
private String p1kills;
private String p2kills;
private JLabel kills;
private JLabel p1, p2;
private JLabel p1NumKills = new JLabel();
private JLabel p2NumKills = new JLabel();
//the player's kill values are increasing and registering, just not within the jlabels representing them
public KillsFrame(ChessGame game){
this.game = game;
killsPanel = new JPanel(new MigLayout("", "[center][right][left][c]", "[top][center][b]"));
kills = new JLabel("KILLS");
p1 = new JLabel(game.getCurrentPlayer().getName() + " - ");
p2 = new JLabel(game.getOtherPlayer().getName() + " - ");
//this is the part that should be working but isn't.
//p1kills and p2kills aren't being updated or something.
p1kills = "" + game.getCurrentPlayer().getKills();
p2kills = "" + game.getOtherPlayer().getKills();
p1NumKills.setText(p1kills);
p1NumKills.setText(p2kills);
killsPanel.add(kills, "span");
killsPanel.add(p1);
killsPanel.add(p1NumKills, "wrap");
killsPanel.add(p2);
killsPanel.add(p2NumKills, "wrap");
killsPanel.setBackground(Color.lightGray);
add(killsPanel);
pack();
setTitle("Scoreboard");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setResizable(true);
setVisible(true);
setLocationRelativeTo(null);
}
Then I just call this frame's repaint() in a main method in a different class:
public static void main(String[] args) throws InterruptedException {
JFrame gameFrame = new ChessMain();
gameFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
gameFrame.pack();
gameFrame.setResizable(true);
gameFrame.setLocationRelativeTo(null);
gameFrame.setVisible(true);
gameFrame.setTitle("Chess X");
LoginFrame loginFrame = new LoginFrame(((ChessMain) gameFrame).getGame());
System.out.println(((ChessMain) gameFrame).getGame().toString());
System.out.println(((ChessMain) gameFrame).getGame().currentPlayer.getName()+ ", it's your turn.");
//this is what creates the kills frame.
KillsFrame kf = new KillsFrame(((ChessMain) gameFrame).getGame());
while(true){
((ChessMain) gameFrame).getGame().run();
kf.repaint();//*************************************
Thread.sleep(1);
}
}
Any and all help is greatly appreciated.
Your error lies when you are trying to "sleep" your main thread. This is bound to cause errors if not make your GUI non-responsive. Your best bet is to use the swing.Timer class (tutorial here http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html). Fire events at regular intervals to repaint(). This will result in a smooth animation.
if for some reason you Have to sleep a thread in a swing program, create a new thread using the SwingWorker class (And not otherwise). see http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html#process(java.util.List)

Categories