Java NullPointerException even though the variable has a value - java

I am currently working on a game written in Java.
For this I´m using JFrames, JPanels and JButtons.
To select the level you want to play, you have to select a world and then chose a level.
After launching the game, a new instance of my WorldSelection gets created, and after pressing a button, the chosen world gets set and a new instance of LevelSelection is created.
When pressing a Button there, the level gets set.
Here is my problem:
In the Game class, the update method checks the selected world and the level 60 times a second, and if both have selected values, it creates a new instance of a Stage/Level.
The moment I press a button to set the Level, I get a NullPointerException, even if the value it checks has a value.
This does not happen all of the time; half of the time it works (no Exception is thrown) and the other 50% an exception is thrown.
Exception in thread "main" java.lang.NullPointerException
at Game.update(Game.java:41)
at Game.run(Game.java:23)
at Game.start(Game.java:34)
at Game.<init>(Game.java:10)
at Game.main(Game.java:77)
Here is my Code:
Game class:
public class Game {
private boolean running;
private WorldSelection ws;
private boolean chosen = false;
public Game() {
ws = new WorldSelection();
start();
}
public void run() { // game loop
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
final double ns = 1000000000.0 / 60.0;
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
update();
delta--;
}
if (System.currentTimeMillis() - timer == 1000) {
timer += 1000;
}
}
}
public void start() {
running = true;
run();
}
private void update() {
if (!chosen) {
if (ws.getWorld() == 1) {
if (ws.ls.getLevel() == 1) {
chosen = true;
//creates new stage here
}
if (ws.ls.getLevel() == 2) {
chosen = true;
//creates new stage here
}
if (ws.ls.getLevel() == 3) {
chosen = true;
//creates new stage here
}
}
else if (ws.getWorld() == 2) {
if (ws.ls.getLevel() == 1) {
chosen = true;
//creates new stage here
}
if (ws.ls.getLevel() == 2) {
chosen = true;
//creates new stage here
}
if (ws.ls.getLevel() == 3) {
chosen = true;
//creates new stage here
}
}
}
else {
//game updates here
}
}
public static void main(String[] args) {
Game game = new Game();
}
}
WorldSelection class:
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class WorldSelection implements ActionListener {
public JFrame frame;
private int width, height;
private int world = 0;
private JButton button1;
public LevelSelection ls;
private JPanel panel;
public WorldSelection() {
this.width = 900;
this.height = 506;
frame = new JFrame();
frame.setSize(width, height);
frame.setResizable(false);
frame.setTitle("Quentins Adventure");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
panel = new JPanel();
panel.setBackground(Color.black);
button1 = new JButton("World 1");
button1.setBackground(Color.LIGHT_GRAY);
button1.setSize(120, 44);
button1.setLocation(80, 350);
button1.addActionListener(this);
panel.add(button1);
frame.add(panel);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == button1){
setWorld(1);
ls = new LevelSelection(235,268);
frame.setVisible(false);
}
}
public int getWorld() {
return world;
}
public void setWorld(int world) {
this.world = world;
}
}
LevelSelection class:
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LevelSelection implements ActionListener {
public JFrame frame;
private int width, height, buttonSize = 50;
private int level = 0;
private JButton button1;
private Font font;
private JPanel panel;
public LevelSelection(int bPosX,int bPosY) {
this.width = 900;
this.height = 506;
frame = new JFrame();
frame.setSize(width, height);
frame.setResizable(false);
frame.setTitle("Quentins Adventure - Wold Selection");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
panel = new JPanel();
panel.setBackground(Color.black);
button1 = new JButton("1");
button1.setBackground(Color.LIGHT_GRAY);
button1.setFont(font);
button1.setSize(buttonSize, buttonSize);
button1.setLocation(bPosX, bPosY);
button1.addActionListener(this);
panel.add(button1);
frame.add(panel);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == button1){
setLevel(1);
frame.dispose();
}
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
}

You setWorld and then do ls = new LevelSelection(235, 268);
It means that there is moment in time when ws.getWorld() == 1 is true, but ws.ls is null.
But it is not only problem with your code. You code is not thread safe which may lead to huge of bugs which is amount very difficult to find.

In your class WorldSelection, reference of class LevelSelection is created but
that is initialized in ActionPerformed(). Thus before that reference of
LevelSelection ls is pointing to null.

Related

JButton doesn't show up in JFrame

I'm currently working on a schoolproject, where I have to code the game snake. Now I`m finished with the biggest part and tryed to make the game menue. I tryed to place a JButton for starting the game (startPlay). However, the button won't show up and I can't figure out why. Can someone help? Thanks in advance!!
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;
public class Main extends JPanel implements ActionListener, KeyListener{
public static int field[][];
public static GenerateField genField;
public static Snake snake;
public static GenerateFood food;
public static GenerateBarrier barrier;
public int difficultness;
public static int widthField;
public static int heightField;
public static TimerTask move, genBarriers;
public static Timer snakeTimer, barrierTimer;
public JButton startPlay;
public static boolean gameStarted;
public Main ()
{
startPlay = new JButton("Starte Spiel");
startPlay.setBounds(0,0,300,200);
startPlay.addActionListener(this);
add(startPlay);
difficultness = 15;
gameStarted = false;
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
widthField = 150;
heightField = 95;
genField = new GenerateField();
snake = new Snake();
food = new GenerateFood();
barrier = new GenerateBarrier();
barrierTimer = new Timer("Timer");
snakeTimer = new Timer("Timer");
genBarriers = new TimerTask() {
#Override
public void run() {
barrier.clearBarrier();
barrier.multiSpawnBarrier(difficultness);
}
};
move = new TimerTask()
{
public void run()
{
if(GenerateField.inGame)
{
snake.moveSnake();
repaint();
}
}
};
}
private static void startGame()
{
genField.generateField();
field = genField.getField();
snake.generateSnake(40, 75);
food.spawnFood();
snakeTimer.schedule(move,0,50);
barrierTimer.schedule(genBarriers, 0, 25000);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(1520,1000);
frame.getContentPane().add(new Main());
frame.setLocationRelativeTo(null);
frame.setBackground(Color.LIGHT_GRAY);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void paint(Graphics g)
{
if(gameStarted) {
for (int iy = 0; iy < heightField; iy++) {
for (int ix = 0; ix < widthField; ix++) {
//Zeichnet schwarzen Hintergrund
if (genField.field[iy][ix] == 0) {
g.setColor(Color.BLACK);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichnet die Grenze an den Rändern
if (genField.field[iy][ix] == 1) {
g.setColor(Color.red);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichnet die Schlange
if (genField.field[iy][ix] == 2) {
g.setColor(Color.green);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichnet das "Futter"
if (genField.field[iy][ix] == 3) {
g.setColor(Color.orange);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichte die Hindernisse
if (genField.field[iy][ix] == 4) {
g.setColor(Color.blue);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
}
}
}
}
#Override
public void actionPerformed(ActionEvent e)
{
startPlay.setVisible(false);
startGame();
gameStarted = true;
}
#Override
public void keyPressed (KeyEvent e)
{
int code = e.getKeyCode();
if ( code == KeyEvent.VK_LEFT)
{
if (snake.mRight == false)
{
snake.mLeft = true;
snake.mRight = false;
snake.mUp = false;
snake.mDown = false;
}
}
if ( code == KeyEvent.VK_RIGHT)
{
if (snake.mLeft == false)
{
snake.mLeft = false;
snake.mRight = true;
snake.mUp = false;
snake.mDown = false;
}
}
if ( code == KeyEvent.VK_UP)
{
if (snake.mDown == false)
{
snake.mLeft = false;
snake.mRight = false;
snake.mUp = true;
snake.mDown = false;
}
}
if ( code == KeyEvent.VK_DOWN)
{
if (snake.mUp == false)
{
snake.mLeft = false;
snake.mRight = false;
snake.mUp = false;
snake.mDown = true;
}
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
Immediate Problem
The over use of static highlights issues with your design. static is not your friend, you should use it sparingly and wisely.
You're trying to put all your eggs in single basket. This is just going to make the state management harder to handle.
Instead, start by separating your menu and game into separate classes and managing them independently of each other.
This then allows you to use a CardLayout to manage the navigation between the different views.
The following is simple example to demonstrate how you might use CardLayout to perform decoupled navigation
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
CardLayout cardLayout = new CardLayout();
JPanel base = new JPanel(cardLayout);
NavigationController controller = new NavigationController() {
#Override
public void show(Screen screen) {
cardLayout.show(base, screen.name());
}
};
base.add(new MainMenuPane(controller), Screen.MENU.name());
base.add(new GamePane(controller), Screen.GAME.name());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(base);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Screen {
MENU, GAME;
}
public interface NavigationController {
public void show(Screen scren);
}
public class MainMenuPane extends JPanel {
public MainMenuPane(NavigationController controller) {
setLayout(new GridBagLayout());
JButton start = new JButton("Start");
add(start);
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
controller.show(Screen.GAME);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class GamePane extends JPanel {
private NavigationController controller;
public GamePane(NavigationController controller) {
this.controller = controller;
setLayout(new GridBagLayout());
add(new JLabel("Ready Player One"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Every thing else you'll need to fix
Don't use KeyListener, use Key bindings instead, they will fix the focus related issues in a more reliable way
You're violating the requirements of the paint chain, which is part of your immediate problem - See Performing Custom Painting and Painting in Swing for more details about how painting works and how you should work with it
Swing is single thread AND not thread safe - see Concurrency in Swing for more details. Essentially the use of java.util.Timer is running the risk of dirty read/writes which could lead to any number of weird and near impossible to diagnose issues. Instead, you should be using Swing Timer instead, which will ensure that any updates you make are made within the context of the Event Dispatching Thread. You should also be using a single Timer and scheduling everything in a simple update pass - this will generally improve performance

How to run the animation thread for a Java Swing game from a launcher?

I am a bit new to threading, so bear with me. All relevant classes will be below the text in one place for easier reference.
Backstory:
I created a simple pong-like game following this tutorial: http://www.edu4java.com/en/game/game1.html
Everything worked perfectly, then I made modifications to better understand how it all works. In the tutorial, there is a main method from which the animations are played continuously. According to the tutorial author, Thread.sleep(10) "...tells the processor that the thread which is being run must sleep for 10 ms, which allows the processor to execute other threads and in particular the AWT-EventQueue thread which calls the paint method."
Now, my question is this:
(Just for fun and to practice Java,) I have created a "launcher" for all the various small programs and games I make. I have yet to get the pong game to work inside the launcher. Without a main method inside the pong frame, the animation never runs. I left the main method in in the code below, so that it works. How would I go about launching the animation from somewhere other than main?
Here's the code:
The Frame and main method:
package pongGame;
import javax.swing.*;
public class PongMainGUI extends JFrame
{
private static final int WINDOW_WIDTH = 500;
private static final int WINDOW_HEIGHT = 800;
private static AnimationPanel panel;
public PongMainGUI()
{
//This line sets the title, and, since it calls the super constructor, it calls setTitle().
super("Pong!");
panel = new AnimationPanel(this);
//This method simply makes the screen appear in the center of whatever size screen you are using.
setLocationRelativeTo(null);
setSize(WINDOW_WIDTH,WINDOW_HEIGHT);
add(panel);
setVisible(true);
}
public static void main(String args[]) throws InterruptedException
{
new PongMainGUI();
while(true)
{
System.out.println("PongMainGUI");
panel.repaint();
panel.move();
Thread.sleep(10);
}
}
}
The Animation Panel:
package pongGame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;
#SuppressWarnings("serial")
public class AnimationPanel extends JPanel
{
PongMainGUI frame;
Ball ballClass;
Racquet racquetClass;
boolean bool = false;
public AnimationPanel(PongMainGUI frame)
{
this.frame = frame;
addMouseListener(new MouseListener()
{
#Override
public void mouseClicked(MouseEvent arg0)
{
}
#Override
public void mouseEntered(MouseEvent arg0)
{
}
#Override
public void mouseExited(MouseEvent arg0)
{
}
#Override
public void mousePressed(MouseEvent arg0)
{
}
#Override
public void mouseReleased(MouseEvent arg0)
{
}
});
addMouseMotionListener(new MouseMotionListener()
{
#Override
public void mouseDragged(MouseEvent e)
{
}
#Override
public void mouseMoved(MouseEvent e)
{
}
});
addKeyListener(new KeyListener()
{
#Override
public void keyPressed(KeyEvent e)
{
racquetClass.keyPressed(e);
}
#Override
public void keyReleased(KeyEvent e)
{
racquetClass.keyReleased(e);
}
#Override
public void keyTyped(KeyEvent e)
{
}
});
//This is needed to ensure that the keyboard will register properly and receive focus.
setFocusable(true);
ballClass = new Ball(this);
racquetClass = new Racquet(this);
}
public void move()
{
//ballClass.moveBall();
racquetClass.moveRacquet();
}
#Override
public void paint(Graphics g)
{
System.out.println("AnimationPanel paint method");
//This method clears the panel so it appears as if the circle is moving.
super.paint(g);
//Better version of Graphics.
Graphics2D g2d = (Graphics2D) g;
//This method turns antialiasing on, which cleans up the corners.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
ballClass.paint(g2d);
racquetClass.paint(g2d);
}
public void gameOver()
{
System.out.println("Game over method");
JOptionPane.showMessageDialog(null, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
System.exit(ABORT);
}
}
The Ball "sprite":
package pongGame;
import java.awt.Graphics2D;
import java.awt.Rectangle;
public class Ball
{
int xCoordinate = 0;
int yCoordinate = 0;
//1 = right movement, -1 = left
int xDirection = 1;
int yDirection = 1;
private final static byte ballWidth = 30;
private final static byte ballHeight = 30;
private AnimationPanel panel;
public Ball(AnimationPanel panel)
{
this.panel = panel;
}
public void paint(Graphics2D g2d)
{
//This creates the actual circle with a specified width and height.
//Because super.paint(g) is called at the start, a new circle is created each time.
g2d.fillOval(xCoordinate, yCoordinate, ballWidth, ballHeight);
System.out.println("Ball paint method");
moveBall();
}
//What this method does is add 1 to the x and y coordinates each time it's called. However, getWidth() and getHeight() are used to determine the current panel size, not the frame size.
//Then, whatever the width and/or height is is subtracted so the circle does not completely disappear from view.
public void moveBall()
{
if (xCoordinate + xDirection < 0)
{
xDirection = 1;
}
else if (xCoordinate + xDirection > panel.getWidth() - ballWidth)
{
xDirection = -1;
}
if (yCoordinate + yDirection < 0)
{
yDirection = 1;
}
else if (yCoordinate + yDirection > panel.getHeight() - ballHeight)
{
System.out.println("Ball moveBall method");
panel.gameOver();
}
if (collision() == true)
{
yDirection = -1;
yCoordinate = panel.racquetClass.getPaddleHeight() - ballHeight;
}
xCoordinate = xCoordinate + xDirection;
yCoordinate = yCoordinate + yDirection;
}
public Rectangle getBounds()
{
return new Rectangle(xCoordinate, yCoordinate, ballWidth, ballHeight);
}
private boolean collision()
{
return panel.racquetClass.getBounds().intersects(getBounds());
}
}
And finally, the Racquet "sprite":
package pongGame;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
public class Racquet
{
private AnimationPanel panel;
private int xCoordinate = 0;
//0 = no movement, 1 is right, -1 is left.
private byte direction = 0;
//All of the following values are in pixels.
private final static byte PADDLE_OFFSET = 100;
private final static byte PADDLE_WIDTH = 120;
private final static byte PADDLE_HEIGHT = 10;
public Racquet(AnimationPanel panel)
{
this.panel = panel;
}
public void moveRacquet()
{
if (xCoordinate + direction > 0 && xCoordinate + direction < panel.getWidth()-60)
xCoordinate = xCoordinate + direction;
}
public void paint(Graphics2D g)
{
g.fillRect(xCoordinate, getPaddleHeight(), PADDLE_WIDTH, PADDLE_HEIGHT);
//move();
}
public void keyReleased(KeyEvent e)
{
direction = 0;
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_LEFT)
direction = -1;
if (e.getKeyCode() == KeyEvent.VK_RIGHT)
direction = 1;
}
public Rectangle getBounds()
{
return new Rectangle(xCoordinate, getPaddleHeight(), PADDLE_WIDTH, PADDLE_HEIGHT);
}
public int getPaddleHeight()
{
return panel.getHeight() - PADDLE_OFFSET;
}
}
This may or may not help, but this is the code for the launcher I wanted to use to open the game:
This is the "main menu":
package GUI;
import javax.swing.*;
import painter.MainPainterGUI;
import java.awt.*;
import java.awt.event.*;
/**
* This class serves to create the launcher gui for the program.
* It extends JFrame.
* #author Jackson Murrell
*/
#SuppressWarnings("serial")
public class LauncherGUI extends JFrame implements ActionListener
{
//A couple constants that are used for sizing things.
private final short WINDOW_HEIGHT = 225;
private final short WINDOW_WIDTH = 550;
private final byte BLANK_SPACE = 25;
//Panels to use for adding in components.
JPanel textPanel, buttonPanel, mainPanel;
//Buttons for user input and selection.
JButton calculator, colorChooser, timer, exit, primeNumberTester, game, painter;
//A text label that will be used for giving the user
//instructions on the program.
JLabel textLabel;
//A constructor to create the GUI components when an object of this class is created.
public LauncherGUI()
{
//This call's the parent method's (JFrame) setTitle method.
super("Omni-program");
//These methods set various options for the JFrame.
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLocationRelativeTo(null);
textPanel = new JPanel();
buttonPanel = new JPanel();
mainPanel = new JPanel();
calculator = new JButton("Calculator");
colorChooser = new JButton("Color Chooser");
timer = new JButton("Timer");
primeNumberTester = new JButton("Prime Number Tester");
game = new JButton("Games");
exit = new JButton("Exit Launcher and Programs");
painter = new JButton("Painter");
calculator.addActionListener(this);
colorChooser.addActionListener(this);
timer.addActionListener(this);
exit.addActionListener(this);
primeNumberTester.addActionListener(this);
game.addActionListener(this);
painter.addActionListener(this);
textLabel = new JLabel("Welcome to the launcher! Click the button for the mini-program you would like to run.", 0);
textPanel.add(Box.createVerticalStrut(BLANK_SPACE));
textPanel.add(textLabel);
buttonPanel.add(calculator);
buttonPanel.add(colorChooser);
buttonPanel.add(timer);
buttonPanel.add(primeNumberTester);
buttonPanel.add(game);
buttonPanel.add(painter);
buttonPanel.add(exit);
mainPanel.setLayout(new GridLayout(2,1));
mainPanel.add(textPanel);
mainPanel.add(buttonPanel);
//mainPanel.add(Box.createVerticalStrut(BLANK_SPACE));
add(mainPanel);
//pack();
//Having this line at the end instead of the top ensures that once everything is added it is all set to be visible.
setVisible(true);
}
//This method is required since ActionListener is implemented.
//It will be used to process user input.
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == calculator)
{
new CalculatorGUI();
dispose();
}
else if (e.getSource() == colorChooser)
{
new ColorChooserGUI();
dispose();
}
else if(e.getSource() == timer)
{
new TimerGUI();
dispose();
}
else if (e.getSource() == primeNumberTester)
{
new PrimeNumberTesterGUI();
dispose();
}
else if(e.getSource() == exit)
{
System.exit(0);
}
else if(e.getSource() == painter)
{
new MainPainterGUI();
dispose();
}
else if(e.getSource() == game)
{
new GameLauncherGUI();
dispose();
}
}
}
Here's the actual game launcher:
package GUI;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import pongGame.PongMainGUI;
public class GameLauncherGUI extends JFrame implements ActionListener
{
//A couple constants that are used for sizing things.
private final short WINDOW_HEIGHT = 225;
private final short WINDOW_WIDTH = 550;
private JButton adventureGame, pong, back;
private JLabel label;
private JPanel mainPanel, buttonPanel, textPanel;
public GameLauncherGUI()
{
//This call's the parent method's (JFrame) setTitle method.
super("Omni-program");
//These methods set various options for the JFrame.
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLocationRelativeTo(null);
adventureGame = new JButton("Adventure Game (Broken)");
adventureGame.addActionListener(this);
pong = new JButton("Pong");
pong.addActionListener(this);
back = new JButton("Back");
back.addActionListener(this);
label = new JLabel("Click the button below for the game you wish to play,\nor click back to go to the previous screen.");
mainPanel = new JPanel();
buttonPanel = new JPanel();
textPanel = new JPanel();
textPanel.add(label);
buttonPanel.add(adventureGame);
buttonPanel.add(pong);
buttonPanel.add(back);
mainPanel.add(textPanel);
mainPanel.add(buttonPanel);
add(mainPanel);
//Having this line at the end instead of the top ensures that once everything is added it is all set to be visible.
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == back)
{
new LauncherGUI();
dispose();
}
else if(e.getSource() == pong)
{
new PongMainGUI();
dispose();
}
}
}
mainis a static method like others, so you can call it from your launcher:
PongMainGUI.main(null); // launch the pong game
However, note that, in order to avoid lots of trouble, Swing components must be created from the Event Dispatch Thread, as shown in this example. So you should wrap the content of your main method inside a Runnable and launch it with SwingUtilities.invokeLater().
However (again), by doing so, your Thread.sleep(10) will run on the EDT, blocking the GUI responsiveness again. Fortunately, Swing thought of that problem and created a utility called javax.swing.Timer that runs tasks periodically on the EDT (without blocking it):
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new PongMainGUI();
Timer timer = new Timer(10, new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println("PongMainGUI");
panel.repaint();
panel.move();
}
});
timer.start();
}
});
}
This main() method will run safely in standalone, or from your launcher.

Implementing long press by usng timer

The program is basically to drag and drop JPanel. I wanted to implement long press to select a JPanel and usage of timer is suggested in few tutorials. Therefore, I tried using timer for 1000ms to select a JPanel, but it works in the preferred way only once but with some flickering of a JPanel which I don't understand why. It fails to recognize mousepressed function later. Another problem is, when clicked on a JPanel, panels start to get removed unintentionally. Actually nothing should happen when a JPanel is clicked as I haven't written anything for Clicked function.
Please provide some suggestions to remove the above mentioned problems
Thanks in advance.
package swappaneleg;
import java.awt.Color;
import java.awt.Component;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.TimerTask;
import javax.swing.*;
public class SwapPanelEg extends JPanel{
private static final long serialVersionUID = 1594039652438249918L;
private static final int PREF_W = 400;
private static final int PREF_H = 400;
private static final int MAX_COLUMN_PANELS = 8;
private JPanel columnPanelsHolder = new JPanel();
public SwapPanelEg(){
columnPanelsHolder.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
for (int i = 0; i < MAX_COLUMN_PANELS; i++) {
int number = i + 1;
int width = 20 + i * 3;
int height = PREF_H - 30;
columnPanelsHolder.add(new ColumnPanel(number, width, height));
}
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
columnPanelsHolder.addMouseListener(myMouseAdapter);
columnPanelsHolder.addMouseMotionListener(myMouseAdapter);
setLayout(new GridBagLayout());
add(columnPanelsHolder);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
private JComponent selectedPanel;
private Point deltaLocation;
private JPanel placeHolder = new JPanel();
private JComponent glassPane;
java.util.Timer t;
#Override
public void mousePressed(final MouseEvent evt) {
if (evt.getButton() != MouseEvent.BUTTON1) {
return;
}
if(t == null) {
t = new java.util.Timer();
}
t.schedule(new TimerTask() {
public void run() {
JPanel source = (JPanel) evt.getSource();
selectedPanel = (JComponent) source.getComponentAt(evt.getPoint());
if (selectedPanel == null) {
return;
}
if (selectedPanel == source) {
selectedPanel = null;
return;
}
glassPane = (JComponent) SwingUtilities.getRootPane(source).getGlassPane();
glassPane.setVisible(true);
Point glassPaneOnScreen = glassPane.getLocationOnScreen();
glassPane.setLayout(null);
Point ptOnScreen = evt.getLocationOnScreen();
Point panelLocOnScreen = selectedPanel.getLocationOnScreen();
int deltaX = ptOnScreen.x + glassPaneOnScreen.x - panelLocOnScreen.x;
int deltaY = ptOnScreen.y + glassPaneOnScreen.y - panelLocOnScreen.y;
deltaLocation = new Point(deltaX, deltaY);
Component[] allComps = source.getComponents();
for (Component component : allComps) {
if (component == selectedPanel) {
placeHolder.setPreferredSize(selectedPanel.getPreferredSize());
source.add(placeHolder);
selectedPanel.setSize(selectedPanel.getPreferredSize());
int x = ptOnScreen.x - deltaLocation.x;
int y = ptOnScreen.y - deltaLocation.y;
selectedPanel.setLocation(x, y);
glassPane.add(selectedPanel);
repaint();
}
else {
source.add(component);
repaint();
}
}
}
},1000,500);
revalidate();
repaint();
}
#Override
public void mouseDragged(MouseEvent evt) {
if (selectedPanel != null) {
Point ptOnScreen = evt.getLocationOnScreen();
int x = ptOnScreen.x - deltaLocation.x;
int y = ptOnScreen.y - deltaLocation.y;
selectedPanel.setLocation(x, y);
selectedPanel.setBorder(BorderFactory.createLineBorder(Color.black));
selectedPanel.setOpaque(false);
repaint();
if(t != null)
{
t.cancel();
t = null;
}
}
}
#Override
public void mouseReleased(MouseEvent evt) {
if (evt.getButton() != MouseEvent.BUTTON1) {
return;
}
if (selectedPanel == null) {
return;
}
JComponent source = (JComponent) evt.getSource();
Component[] allComps = source.getComponents();
JPanel overComponent = (JPanel) source.getComponentAt(evt
.getPoint());
if (overComponent != null && overComponent != placeHolder
&& overComponent != source) {
for (Component component : allComps) {
if (component == overComponent) {
source.add(overComponent);
source.add(selectedPanel);
source.remove(placeHolder);
selectedPanel.setOpaque(true);
selectedPanel.setBorder(BorderFactory.createLineBorder(new Color(0,0,0,0)));
}
else {
source.add(component);
source.remove(placeHolder);
selectedPanel.setOpaque(true);
selectedPanel.setBorder(BorderFactory.createLineBorder(new Color(0,0,0,0)));
}
}
}
else {
for (Component component : allComps) {
if (component == placeHolder) {
source.add(selectedPanel);
source.remove(placeHolder);
}
else {
source.remove(placeHolder);
source.add(component);
selectedPanel.setOpaque(true);
selectedPanel.setBorder(BorderFactory.createLineBorder(new Color(0,0,0,0)));
}
}
}
revalidate();
repaint();
selectedPanel = null;
if(t != null)
{
t.cancel();
t = null;
}
}
}
private static void createAndShowGui() {
SwapPanelEg mainPanel = new SwapPanelEg();
JFrame frame = new JFrame("SwapPanelEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ColumnPanel extends JPanel {
private static final long serialVersionUID = 5366233209639059032L;
private int number;
private int prefWidth;
private int prefHeight;
public ColumnPanel(int number, int prefWidth, int prefHeight) {
setName("ColumnPanel " + number);
this.number = number;
this.prefWidth = prefWidth;
this.prefHeight = prefHeight;
add(new JLabel(String.valueOf(number)));
setBackground(Color.cyan);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefWidth, prefHeight);
}
public int getNumber() {
return number;
}
}
I think a better solution would be:
Create a SwingTimer when a mouse is first pressed.
Have it wait a second and track if the mouse was released or leaves the panel.
If by the time the SwingTimer ends there has been no release and the mouse didn't leave the panel then process with your task.
SwingTimer tutorial: http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Proposed timer code:
private class InnerTimer implements Runnable {
public boolean expired;
public boolean cancelled;
private long end;
public void run() {
expired = cancelled = false;
end = System.currentTimeMillis() + 1000;
while ( ! cancelled && ! expired) {
if (System.currentTimeMillis() >= end) {
expired = true;
} else {
try { Thread.sleep(100); }
catch(InterruptedException e) {/* no big deal */}
}
}
}
}
Keep an InnerTimer innerTimer = new InnerTimer(); in each panel that needs to listen for long-presses. When the mouse is pressed on such a panel, start this timer in a separate thread: (new Thread(innerTimer)).start(). If the mouse leaves the panel, or is released, cancel the timer: innerTimer.cancelled = true;. If the mouse is moved, check if the timer expired naturally: if (innerTimer.expired); if it did, then the user has successfully long-clicked on this panel. If it did not expire, then just cancel the timer.
Note that this code is somewhat quick&dirty. However, it is short and relatively efficient (except in that it creates threads, but these mostly sleep a lot and are discarded anyway after 1s). Using Swing Timers would avoid the thread-creation overhead, but be considerably more verbose.

Pulsing image-button in Java

I have created a simple musical metronome in Java. It starts and stops by pressing a button that has the shape of a butterfly. I would love to add a visual effect of the tempo by making the button/butterfly appear and disappear together with the metronome beat.
Would java.util.Timer be the way to go? Would that work with an image that is a button and that needs to keep its functions while pulsing?
Thank you so very much for suggestions and congratulations on the community.
Would java.util.Timer be the way to go?
Yes, this could easily be used to display a pulsating image. When I've done this before, I've created an array of sine wave constants in my constructor and have used them to set an alpha composite inside of the timer.
Would that work with an image that is a button and that needs to keep its functions while pulsing?
It's possible although a little more difficult since the button doesn't really render itself, but rather its componentUI, here one of the subclasses of the BasicButtonUI.
Well, I can do it without messing with the componentUI, but I'm not sure if this is the correct way:
import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class PulsingButton extends JPanel {
private static final int MAX_ALPHAS = 60;
private float alpha = 1.0f;
private JSpinner beatsPerMinSpinner = new JSpinner(new SpinnerNumberModel(60, 30, 120, 1));
private JButton button = new JButton("Button") {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha));
super.paintComponent(g2);
};
};
private float[] alphas = new float[MAX_ALPHAS];
private Timer timer;
public PulsingButton() {
beatsPerMinSpinner.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = ((Integer) beatsPerMinSpinner.getValue()).intValue();
setTimerDelay(value);
}
});
add(new JLabel("Beats Per Minute:"));
add(beatsPerMinSpinner);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello!");
}
});
add(button);
for (int i = 0; i < alphas.length; i++) {
double theta = (Math.PI * 2 * i) / alphas.length;
alphas[i] = (float) ((Math.cos(theta) + 1) / 2.0);
}
int bpm = ((Integer) beatsPerMinSpinner.getValue()).intValue();
timer = new Timer(setTimerDelay(bpm), new TimerListener());
timer.start();
System.out.println(setTimerDelay(bpm) + "");
}
private int setTimerDelay(int bpm) {
int milisecondsInMinute = 60 * 1000;
int delay = milisecondsInMinute / (bpm * alphas.length);
if (timer != null) {
timer.setDelay(delay);
}
return delay;
}
private class TimerListener implements ActionListener {
int index = 0;
#Override
public void actionPerformed(ActionEvent arg0) {
alpha = alphas[index];
index++;
index %= alphas.length;
repaint();
}
}
private static void createAndShowGui() {
PulsingButton mainPanel = new PulsingButton();
JFrame frame = new JFrame("PulsingButton");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

animate JPanel (slide in) with timer

I am trying to make a JPanel slide in from the side using this class i made:
public class AnimationClass {
private int i;
private int y;
private JPanel panel;
private int xTo;
private Timer timer;
private int xFrom;
synchronized void slidePanelInFromRight(JPanel panelInput, int xFromInput, int xToInput, int yInput, int width, int height) {
this.panel = panelInput;
this.xFrom = xFromInput;
this.xTo = xToInput;
this.y = yInput;
panel.setSize(width, height);
timer = new Timer(0, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
for (int i = xFrom; i > xTo; i--) {
panel.setLocation(i, y);
panel.repaint();
i--;
timer.stop();
timer.setDelay(100);
if (i >= xTo) {
timer.stop();
}
}
timer.stop();
}
});
timer.start();
}
}
Well, i dont know what the problem is. I've tried a lot of different things, but i doesn't seem like I can get it to work.
The timer should be changing the location on each tick, until it is in place, instead, on each tick, you're running through a for-next loop, which is blocking the EDT until the loop finishes, preventing from updating the UI
Update with example
For example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAnimatedPane {
public static void main(String[] args) {
new TestAnimatedPane();
}
public TestAnimatedPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel panel;
public TestPane() {
setLayout(null);
panel = new JPanel();
panel.setBackground(Color.RED);
add(panel);
Dimension size = getPreferredSize();
Rectangle from = new Rectangle(size.width, (size.height - 50) / 2, 50, 50);
Rectangle to = new Rectangle((size.width - 50) / 2, (size.height - 50) / 2, 50, 50);
Animate animate = new Animate(panel, from, to);
animate.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class Animate {
public static final int RUN_TIME = 2000;
private JPanel panel;
private Rectangle from;
private Rectangle to;
private long startTime;
public Animate(JPanel panel, Rectangle from, Rectangle to) {
this.panel = panel;
this.from = from;
this.to = to;
}
public void start() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long duration = System.currentTimeMillis() - startTime;
double progress = (double)duration / (double)RUN_TIME;
if (progress > 1f) {
progress = 1f;
((Timer)e.getSource()).stop();
}
Rectangle target = calculateProgress(from, to, progress);
panel.setBounds(target);
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.setInitialDelay(0);
startTime = System.currentTimeMillis();
timer.start();
}
}
public static Rectangle calculateProgress(Rectangle startBounds, Rectangle targetBounds, double progress) {
Rectangle bounds = new Rectangle();
if (startBounds != null && targetBounds != null) {
bounds.setLocation(calculateProgress(startBounds.getLocation(), targetBounds.getLocation(), progress));
bounds.setSize(calculateProgress(startBounds.getSize(), targetBounds.getSize(), progress));
}
return bounds;
}
public static Point calculateProgress(Point startPoint, Point targetPoint, double progress) {
Point point = new Point();
if (startPoint != null && targetPoint != null) {
point.x = calculateProgress(startPoint.x, targetPoint.x, progress);
point.y = calculateProgress(startPoint.y, targetPoint.y, progress);
}
return point;
}
public static int calculateProgress(int startValue, int endValue, double fraction) {
int value = 0;
int distance = endValue - startValue;
value = (int)Math.round((double)distance * fraction);
value += startValue;
return value;
}
public static Dimension calculateProgress(Dimension startSize, Dimension targetSize, double progress) {
Dimension size = new Dimension();
if (startSize != null && targetSize != null) {
size.width = calculateProgress(startSize.width, targetSize.width, progress);
size.height = calculateProgress(startSize.height, targetSize.height, progress);
}
return size;
}
}
Update
I should have added this in last night (1 year who didn't want to go to bed, 2 parents that did, say no more...)
Animation is complex topic, especially when you start looking at variable speed (the example is static).
Instead of reinventing the wheel, you should seriously consider taking a look at...
Timing Framework - This is base animation framework, that makes no assumptions about how you might like to use it.
Trident - Similar to the Timing Framework, but also has support for Swing based components (via reflection) build in
Universal Tween Engine
This well-factored example easily admits the variation below. It leverages the enclosed panel's preferred size in a JLayeredPane.
/**
* #see https://stackoverflow.com/a/16322007/230513
* #see https://stackoverflow.com/a/16316345/230513
*/
public class TestPane extends JLayeredPane {
private static final int WIDE = 200;
private static final int HIGH = 5 * WIDE / 8; // ~1/phi
private JPanel panel;
public TestPane() {
panel = new JPanel();
panel.setBackground(Color.RED);
panel.add(new JButton("Test"));
add(panel);
Dimension size = panel.getPreferredSize();
int half = HIGH / 2 - size.height / 2;
Rectangle from = new Rectangle(size);
from.translate(WIDE, half);
Rectangle to = new Rectangle(size);
to.translate(0, half);
panel.setBounds(from);
Animate animate = new Animate(panel, from, to);
animate.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDE, HIGH);
}
}
There are a number of problems in the OP's code. As MadProrammer points, should only be moving one step per timer tick. Here is a simple,tested correction to the OPs code which moves the JPanel one pixel at a time, 25 times a second. Note the comments:
synchronized void slidePanelInFromRight(JPanel panelInput, int xFromInput, int xToInput, int yInput, int width, int height) {
this.panel = panelInput;
this.xFrom = xFromInput;
this.xTo = xToInput;
this.y = yInput;
panel.setSize(width, height);
// timer runs 25 times per second
timer = new Timer(40, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
// Must 'remember' where we have slid panel to by using instance variable rather than automatic variable
// Only move one step at a time.
// No need to restart timer, it continues to run until stopped
if (xFrom > xTo){
xFrom = xFrom - 1;
panel.setLocation(xFrom, y);
panel.repaint();
} else {
timer.stop();
}
panel.setLocation(xFrom, y);
panel.repaint();
}
});
timer.start();
}
example to slid Anything
package TestingPackage;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ToggleBtn extends JPanel {
JFrame frame;
JPanel panelOut;
JLabel labelOn;
JLabel labelOff;
JButton btn;
int count = 1;
public ToggleBtn() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(500, 300, 300, 300);
frame.setLayout(null);
panelOut = new JPanel(null);
panelOut.setBounds(50, 100, 120, 30);
panelOut.setBackground(Color.gray);
frame.add(panelOut);
btn = new JButton("::");
btn.setBounds(0, 0, 60, 30);
panelOut.add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
startThread();
}
});
labelOn = new JLabel("ON");
labelOn.setBounds(0, 0, 60, 30);
panelOut.add(labelOn);
labelOff = new JLabel("OFF");
labelOff.setBounds(60, 0, 60, 30);
panelOut.add(labelOff);
frame.setVisible(true);
}
public void startThread() {
count++;
new Move().start();
}
public static void main(String[] args) {
new ToggleBtn();
}
class Move extends Thread {
#Override
public void run() {
if (count % 2 == 0) {
System.out.println("if");
for (int i = 0; i <= 60; i++) {
try {
Thread.sleep(3);
} catch (InterruptedException ex) {
Logger.getLogger(ToggleBtn.class.getName()).log(Level.SEVERE, null, ex);
}
btn.setBounds(i, 0, 60, 30);
}
} else {
System.out.println("else");
for (int i = 60; i >= 0; i--) {
try {
Thread.sleep(3);
} catch (InterruptedException ex) {
Logger.getLogger(ToggleBtn.class.getName()).log(Level.SEVERE, null, ex);
}
btn.setBounds(i, 0, 60, 30);
}
}
}
}
}

Categories

Resources