I just started in Swing and using the Timer. The program I wrote basically move a rectangle up and down to the specific point on the screen, I used the timer to get it run slowly and smoothly. But I got problem when trying to stop it. Here is the code below:
Lift class change y position of rectangle:
public void moveUp(int destination){
speed++;
if(speed>5){
speed = 5;
}
System.out.println("Speed is: "+speed);
yPos -= speed;
if(yPos < destination){
yPos = destination;
isStop = true;
}
setPos(xPos, yPos);
}
And the class that got Timer and MouseListener:
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
liftArray.get(0).moveUp(rowDisctance / 2);
repaint();
}
});
timer.start();
}
}
if i did understood you correctly you are looking for something like this , you need two timers to control up and down mechanism , timer1 one moves down ,timer2 moves up or vice versa. You need to stop timer1 then inside timer1 you need to start timer2 , here is the code and animation below.
add your fields
Point rv;
set initial location to your dialog (rectangle) within constructor
rv= rectangle.this.getLocation();
your button action performed
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
timer1.setInitialDelay(0);
timer1.start();
jTextArea1.append("Timer 1 Started Moving Down\n");
}
copy paste these two timer1 and timer2 like method in java
private Timer timer1 = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rv.y++;
if (rv.y == 500) {
timer1.stop();
jTextArea1.append("Timer 1 Stopped\n");
jTextArea1.append("Timer 2 Started Moving Up\n");
timer2.setInitialDelay(0);
timer2.start();
}
rectangle.this.setLocation(rv.x , rv.y);
rectangle.this.repaint();
}
});
private Timer timer2 = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rv.y--;
if (rv.y == 200 ) {
timer2.stop();
jTextArea1.append("Timer 2 Stopped\n");
}
rectangle.this.setLocation(rv.x , rv.y);
rectangle.this.repaint();
}
});
To visualize how to proceed with this issue, I've coded a little example:
When you start it, two rectangles will be created. They start moving, when you click with the left mouse button on the drawing area.
Motion will stopped, when the destination line is arrived.
The logic depends on a status in the Rectangle class. When the timer event handler runs, every rectangle's status is checked and the timer is stopped, if all rectangles reached have the isStopped status:
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/**
* Variable isStopped is used to track, if any
* rectangle didn't reach the destination yet
*/
boolean isStopped = true;
for(int i = 0; i < count; i++){
rectangles[i].moveUp(destination);
if (!rectangles[i].isStopped()) {
isStopped = false;
}
}
drawPanel.repaint();
/**
* With all rectangles having arrived at destination,
* the timer can be stopped
*/
if (isStopped) {
timer.stop();
}
}
});
timer.start();
}
}
Excerpt from the Rectangle class - here you see, how the isStopped status is handled - internally as private isStop variable - retrievable with the isStopped getter.
/**
* Moves the rectangle up until destination is reached
* speed is the amount of a single movement.
*/
public void moveUp(int destination) {
if (speed < 5) {
speed++;
}
y -= speed;
if (y < destination) {
y = destination;
isStop = true;
}
}
public boolean isStopped() {
return isStop;
}
Here is the whole Program:
Main Program MovingRectangle, creating the drawing area and providing control:
package question;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingRectangle extends JPanel {
/**
* The serial version uid.
*/
private static final long serialVersionUID = 1L;
private Rectangle[] rectangles = new Rectangle[10];
private int count;
private int destination;
private JPanel controlPanel;
private DrawingPanel drawPanel;
private JButton stop;
private Timer timer;
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Rectangles");
frame.setContentPane(new MovingRectangle());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
public MovingRectangle(){
/** Imaginary horizontal line - the destination */
destination = 200;
/** Create the control panel for the left side containing the buttons */
controlPanel = new JPanel();
controlPanel.setPreferredSize(new Dimension(120, 400));
/** Create the button */
stop = new JButton("Stop");
stop.addActionListener(new ButtonListener());
controlPanel.add(stop);
/** Create a hint how to start the movement. */
JTextArea textHint = new JTextArea(5, 10);
textHint.setEditable(true);
textHint.setText("Please click on the drawing area to start the movement");
textHint.setLineWrap(true);
textHint.setWrapStyleWord(true);
controlPanel.add(textHint);
/** Add control panel to the main panel */
add(controlPanel);
/** Create the drawing area for the right side */
drawPanel = new DrawingPanel();
/** Add the drawing panel to the main panel */
add(drawPanel);
/** With a left mouse button click the timer can be started */
drawPanel.addMouseListener(new MouseListener() {
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/**
* Variable isStopped is used to track, if any
* rectangle didn't reach the destination yet
*/
boolean isStopped = true;
for(int i = 0; i < count; i++){
rectangles[i].moveUp(destination);
if (!rectangles[i].isStopped()) {
isStopped = false;
}
}
drawPanel.repaint();
/**
* With all rectangles having arrived at destination,
* the timer can be stopped
*/
if (isStopped) {
timer.stop();
}
}
});
timer.start();
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
/** Add two rectangles to the drawing area */
addRectangle(100, 30, 0, 370, new Color(255, 0, 0));
addRectangle(120, 50, 200, 350, new Color(0, 0, 255));
}
private void addRectangle(final int widthParam, final int heightParam, final int xBegin, final int yBegin, final Color color) {
/** Add a new rectangle, if array not filled yet */
if (count < rectangles.length) {
rectangles[count] = new Rectangle(widthParam, heightParam, xBegin, yBegin, color);
count++;
drawPanel.repaint();
}
}
/** This inner class is used to handle the button event. */
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e){
if (e.getSource() == stop){
timer.stop();
}
}
}
/** This inner class provides the drawing panel */
private class DrawingPanel extends JPanel{
/** The serial version uid. */
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(400, 400));
setBackground(new Color(200, 220, 255));
}
public void paintComponent(Graphics g){
super.paintComponent(g);
/** Draw destination line */
g.setColor(new Color(150, 150, 150));
g.drawLine(0, destination, 400, destination);
/** Draw rectangles */
for(int i = 0; i < count; i++){
rectangles[i].display(g);
}
}
}
}
The Rectangle Class for the moving shape:
package question;
import java.awt.Color;
import java.awt.Graphics;
public class Rectangle {
private int x, y, width, height;
private Color color;
private boolean isStop = false;
private int speed = 0;
public Rectangle(final int widthParam, final int heightParam, final int xBegin, final int yBegin, final Color colorParam) {
width = widthParam;
height = heightParam;
this.x = xBegin;
this.y = yBegin;
this.color = colorParam;
}
public void display(Graphics g){
g.setColor(this.color);
g.fillRect(this.x, this.y, this.width, this.height);
}
/**
* Moves the rectangle up until destination is reached
* speed is the amount of a single movement.
*/
public void moveUp(int destination) {
if (speed < 5) {
speed++;
}
y -= speed;
if (y < destination) {
y = destination;
isStop = true;
}
}
public boolean isStopped() {
return isStop;
}
}
Related
I am learning Java currently and have been given the assignmnet of finidhing off a program to create the game Conways's life (we started with some code provided to us and must add features etc to this).
I am currently stuck on a menu option for the game. I want it to start off at the menu screen, wherein buttons appear at the top for "Start", "Random", "Load", Save". I have written code so that the program displays these buttons, through a fillRect option in my paint method.
My question is, how do I use the mousePressed method to recognise the cells selected so that I can get an action to occur when they are selected. I been looking at this for a while but can't seem to get this working.
Any suggestion would be a massive help. I have shared my code below. It's a work in progress but I would really like to get this working before continuing on with the other functionality.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
public class ConwaysLife extends JFrame implements Runnable, MouseListener {
// member data
private BufferStrategy strategy;
private Graphics offscreenBuffer;
private boolean gameState[][] = new boolean[40][40];
private boolean isGameInProgress = false;
// constructor
public ConwaysLife () {
//Display the window, centred on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - 400;
int y = screensize.height/2 - 400;
setBounds(x, y, 800, 800);
setVisible(true);
this.setTitle("Conway's game of life");
// initialise double-buffering
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenBuffer = strategy.getDrawGraphics();
// register the Jframe itself to receive mouse events
addMouseListener(this);
// initialise the game state
for (x=0;x<40;x++) {
for (y=0;y<40;y++) {
gameState[x][y]=false;
}
}
// create and start our animation thread
Thread t = new Thread(this);
t.start();
}
// thread's entry point
public void run() {
while ( 1==1 ) {
// 1: sleep for 1/5 sec
try {
Thread.sleep(200);
} catch (InterruptedException e) { }
// 2: animate game objects [nothing yet!]
/*if (isGameInProgress == false) {
this.repaint();
}*/
// 3: force an application repaint
this.repaint();
}
}
// mouse events which must be implemented for MouseListener
public void mousePressed(MouseEvent e) {
while (!isGameInProgress) {
int x = e.getX()/20;
int y = e.getY()/20;
if(x >= 10 && x <= 80 && y >= 40 && y <= 65) {
isGameInProgress = !isGameInProgress;
this.repaint();
}
}
// determine which cell of the gameState array was clicked on
int x = e.getX()/20;
int y = e.getY()/20;
// toggle the state of the cell
gameState[x][y] = !gameState[x][y];
// request an extra repaint, to get immediate visual feedback
this.repaint();
}
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
//
// application's paint method
public void paint(Graphics g) {
Font font = new Font("Veranda", Font.BOLD, 20);
g = offscreenBuffer; // draw to off screen buffer
// clear the canvas with a big black rectangle
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
/*look to add a while game in progress loop here!!!*/
// draw menu options
if(!isGameInProgress) {
g.setColor(Color.green);
g.fillRect(10, 40, 70, 25);
g.fillRect(100, 40, 100, 25);
g.fillRect(300, 40, 170, 25);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString("Start", 15, 60);
g.drawString("Random", 105, 60);
g.drawString("Load", 305, 60);
g.drawString("Save", 395, 60);
}
// redraw all game objects
g.setColor(Color.WHITE);
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (gameState[x][y]) {
g.fillRect(x*20, y*20, 20, 20);
}
}
}
// flip the buffers
strategy.show();
}
// application entry point
public static void main(String[] args) {
ConwaysLife w = new ConwaysLife();
}
}
You're not going to like the answer, but it's the "correct" way to approach the problem.
What you need to understand is, Swing/AWT is using a "passive" rendering workflow and BufferStrategy is using a "active" rendering workflow, these are incompatible with each other.
As a general rule, you should not be overriding paint of top level containers like JFrame, this is going to end in no end of issues. Instead, you should be starting with something like JPanel and overriding it's paintComponent method instead.
Having said that, there's a "simpler" solution available to you. CardLayout. This will allow you to seperate the workflows of the menu from the game and resolve the issue between Swing/AWT and BufferStrategy
For example...
import java.awt.Canvas;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
enum View {
MENU, GAME;
}
private CardLayout cardLayout = new CardLayout();
private GamePane gamePane;
public MainPane() {
setLayout(cardLayout);
gamePane = new GamePane();
add(new MenuPane(new MenuPane.Observer() {
#Override
public void startNewGame() {
showGame();
}
#Override
public void randomGame() {
}
#Override
public void loadGame() {
}
#Override
public void saveGame() {
}
}), View.MENU);
add(gamePane, View.GAME);
}
protected void add(Component compent, View view) {
add(compent, view.name());
}
protected void showGame() {
show(View.GAME);
gamePane.start();
}
protected void showMenu() {
gamePane.stop();
show(View.MENU);
}
protected void show(View view) {
cardLayout.show(this, view.name());
}
}
public class MenuPane extends JPanel {
public interface Observer {
public void startNewGame();
public void randomGame();
public void loadGame();
public void saveGame();
}
private Observer observer;
public MenuPane(Observer observer) {
this.observer = observer;
JButton startButton = new JButton("Start");
JButton randomButton = new JButton("Random");
JButton loadButton = new JButton("Load");
JButton saveButton = new JButton("Save");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.ipadx = 10;
gbc.ipady = 10;
gbc.insets = new Insets(8, 8, 8, 8);
gbc.weightx = GridBagConstraints.REMAINDER;
add(startButton, gbc);
add(randomButton, gbc);
add(loadButton, gbc);
add(saveButton, gbc);
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.startNewGame();
}
});
randomButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.randomGame();
}
});
loadButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.loadGame();
}
});
saveButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.saveGame();
}
});
}
}
public class GamePane extends Canvas {
private Thread thread;
private volatile boolean isRunning = false;
public GamePane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void start() {
if (isRunning) {
return;
}
createBufferStrategy(3);
isRunning = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
mainLoop();
}
});
thread.start();
}
protected void stop() {
if (!isRunning || thread == null) {
return;
}
isRunning = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
protected void mainLoop() {
try {
while (isRunning) {
render();
Thread.sleep(16);
}
} catch (InterruptedException ex) {
}
}
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics graphics = strategy.getDrawGraphics();
FontMetrics fm = graphics.getFontMetrics();
String text = "All your game are belong to us";
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = (getHeight() - fm.getHeight()) / 2;
graphics.setColor(Color.WHITE);
graphics.drawString(text, x, y + fm.getAscent());
// Render to graphics
// ...
// Dispose the graphics
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
}
I would strongly recommend that you take the time to read through:
Creating a GUI With Swing
A Visual Guide to Layout Managers
Performing Custom Painting
Painting in AWT and Swing
BufferStrategy and BufferCapabilities
JavaDocs for BufferStrategy which demonstrate how the API should be used.
A "fully" BufferStrategy based approach...
Now, if you can't use Swing, "for reasons", you can still achieve a simular concept using "delegation".
Basically this means "delegating" responsibility for performing some workflow to another. In this case, we want to delegate the rendering and the handling of the mouse events.
This allows you to have a dedicated workflow for the menu and a dedicated workflow for the game, without having to try and mix a lot of state.
Why do I keep on insisting on separating these two workflows? Simply, because it makes it MUCH easier to manage and reason about, but also because it supports the Single Responsibility Principle.
The follow example makes use of Renderable interface to define the core functionality that end "render" delegate will need to implement, in this case, it's pretty simple, we want to tell the renderer to "render" it's content on each paint pass and we want to (optionally) delegate mouse clicked events (this could be done via a second interface or even just the MouseListener interface directly, but I've made it a requirement of the Renderable interface for demonstration purposes.
The "basic" solution to your actual question is found through the use of Rectangle#contains(Point).
This basically inspects each "button" Rectangle to determine if the supplied MouseEvent occurs within it's bounds, if it does, we take action.
It is, however, a little more complicated then that, as we need to have the Rectangles built ahead of time, not difficult, but it's state which is actually reliant on the parent, as we need to know the area in which the renderer is been displayed, run the example, you'll see what I mean 😉
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MainPane mainPane = new MainPane();
JFrame frame = new JFrame();
frame.add(mainPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
mainPane.start();
}
});
}
public interface Renderable {
public void render(Graphics2D g2d, Dimension size);
// We could just extend from MouseListener
// but I don't need all those event handlers
public void mouseClicked(MouseEvent e);
}
public class MainPane extends Canvas {
private Thread thread;
private volatile boolean isRunning = false;
private Renderable currentRenderer;
private MenuRenderer menuRenderer;
private GameRenderer gameRenderer;
public MainPane() {
setBackground(Color.BLACK);
gameRenderer = new GameRenderer();
menuRenderer = new MenuRenderer(new MenuRenderer.Observer() {
#Override
public void startNewGame() {
showGame();
}
#Override
public void randomGame() {
}
#Override
public void loadGame() {
}
#Override
public void saveGame() {
}
});
showMenu();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (currentRenderer == null) {
return;
}
currentRenderer.mouseClicked(e);
}
});
}
protected void showMenu() {
// This may need to tell the game renderer to stop
// or pause
currentRenderer = menuRenderer;
}
protected void showGame() {
currentRenderer = gameRenderer;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void start() {
if (isRunning) {
return;
}
createBufferStrategy(3);
isRunning = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
mainLoop();
}
});
thread.start();
}
protected void stop() {
if (!isRunning || thread == null) {
return;
}
isRunning = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
protected void mainLoop() {
try {
while (isRunning) {
render();
Thread.sleep(16);
}
} catch (InterruptedException ex) {
}
}
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null && currentRenderer != null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
g2d.setBackground(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
);
g2d.setRenderingHints(hints);
// Render to graphics
currentRenderer.render(g2d, getSize());
// Dispose the graphics
g2d.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
public class GameRenderer implements Renderable {
#Override
public void render(Graphics2D g2d, Dimension size) {
FontMetrics fm = g2d.getFontMetrics();
String text = "All your game are belong to us";
int x = (size.width - fm.stringWidth(text)) / 2;
int y = (size.height - fm.getHeight()) / 2;
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y + fm.getAscent());
}
#Override
public void mouseClicked(MouseEvent e) {
}
}
public class MenuRenderer implements Renderable {
public interface Observer {
public void startNewGame();
public void randomGame();
public void loadGame();
public void saveGame();
}
private Observer observer;
private String[] menuOptions = new String[]{
"New Game",
"Random",
"Load Game",
"Save Game"
};
private Rectangle[] menuBounds;
private int internalPadding = 20;
private int horizontalGap = 16;
public MenuRenderer(Observer observer) {
this.observer = observer;
}
#Override
public void render(Graphics2D g2d, Dimension size) {
if (menuBounds == null) {
createMenus(g2d, size);
}
renderMenus(g2d);
}
protected void createMenus(Graphics2D g2d, Dimension size) {
FontMetrics fm = g2d.getFontMetrics();
int totalHeight = (((fm.getHeight() + internalPadding) + horizontalGap) * menuOptions.length) - horizontalGap;
int buttonHeight = fm.getHeight() + internalPadding;
menuBounds = new Rectangle[menuOptions.length];
int buttonWidth = 0;
for (int index = 0; index < menuOptions.length; index++) {
int width = fm.stringWidth(menuOptions[index]) + internalPadding;
buttonWidth = Math.max(width, buttonWidth);
}
int yPos = (size.height - totalHeight) / 2;
for (int index = 0; index < menuOptions.length; index++) {
int xPos = (size.width - buttonWidth) / 2;
Rectangle menuRectangle = new Rectangle(xPos, yPos, buttonWidth, buttonHeight);
menuBounds[index] = menuRectangle;
yPos += buttonHeight + (horizontalGap / 2);
}
}
protected void renderMenus(Graphics2D g2d) {
for (int index = 0; index < menuOptions.length; index++) {
String text = menuOptions[index];
Rectangle bounds = menuBounds[index];
renderMenu(g2d, text, bounds);
}
}
protected void renderMenu(Graphics2D g2d, String text, Rectangle bounds) {
FontMetrics fm = g2d.getFontMetrics();
int textWidth = fm.stringWidth(text);
int textXPos = (bounds.x + (internalPadding / 2)) + ((bounds.width - internalPadding - textWidth) / 2);
int textYPos = bounds.y + (internalPadding / 2);
RoundRectangle2D buttonBackground = new RoundRectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height, 20, 20);
g2d.setColor(Color.BLUE.darker());
g2d.fill(buttonBackground);
g2d.setColor(Color.WHITE);
g2d.drawString(text, textXPos, textYPos + fm.getAscent());
}
#Override
public void mouseClicked(MouseEvent e) {
if (menuBounds == null) {
return;
}
for (int index = 0; index < menuOptions.length; index++) {
if (menuBounds[index].contains(e.getPoint())) {
switch (index) {
case 0:
observer.startNewGame();
break;
case 2:
observer.randomGame();
break;
case 3:
observer.loadGame();
break;
case 4:
observer.saveGame();
break;
}
}
}
}
}
}
So I am creating a brick breaker game. I got the paddle moving using key listeners, however I got some advice to use key bindings instead. I've been reading and looking everywhere on this and I think I've kinda got the idea but the implementation is confusing me a bit.
For my paddle, I created it with the paint component. Hence this is what I want to be able to move left and right with the arrow keys. However, all the key bindings explanations I find seem to use JComponent which the paddle is not. Is there a way around it or will I have to make my paddle a JComponent image icon instead? If so would I just load the image into my Paddle class?
Also what's the best way of structuring my code to contain the key bindings? For instance am I better off creating a whole new class for it, or putting it in e.g. my GamePanel class
Any tips?
Here is some of my code so you can get an idea:
Main Class:
public class BrickBreakerGameApp {
public static void main(String[] args) {
int pw = 500;
int ph = 900;
GamePanel gPanel = new GamePanel(pw,ph);
GameFrame gFrame = new GameFrame();
gFrame.getContentPane().add(gPanel); //add game panel to frame
// gFrame.addKeyListener(gPanel); //adds the key listener for the game panel frame
gFrame.pack();
gFrame.setVisible(true); //make frame visible
}
}
GameFrame class:
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
//Global Variables
public int frameWidth = 500;
public int frameHeight = 800;
//create constructor
GameFrame () {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //so app closes properly
this.setPreferredSize(new Dimension(frameWidth, frameHeight)); //set frame size
this.setTitle("Brick Breaker Game");
ImageIcon icon = new ImageIcon("res/brick-breaker-logo.jpg"); //create image icon
this.setIconImage(icon.getImage()); //update frame icon
this.pack();
}
}
GamePanel Class
import java.awt.Color;
import java.awt.event.*;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import java.awt.*;
/**
* This class handles the game panel as a whole
*/
public class GamePanel extends JPanel {
// Global Variables
private Ball ball;
private Paddle paddle;
private Bricks bricks;
private GameFrame gameFrame;
private int width;
private int height;
private int[] bArray = new int[20];
// create a constructor
GamePanel (int gamePanelWidth, int gamePanelHeight) {
this.setWidth(gamePanelWidth);
this.setHeight(gamePanelHeight);
initialiseGame();
this.isVisible();
}
private void initialiseGame() {
ball = new Ball(10, 520, 30, 30); //create the ball object
paddle = new Paddle(this, 50, 700, 100, 10); //creates paddle object
bricks = new Bricks(10, 10, 60, 30, bArray); //creates the bricks object
//for key binding initialisation
MotionAction motion = new MotionAction(paddle, 24);
}
// paint all the elements in
public void paintComponent(Graphics g) {
super.paintComponent(g);
// background
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
// paddle
// g.setColor(Color.CYAN);
// g.fillRect(paddle.getPaddleX(), paddle.getPaddleY(), paddle.getPaddleWidth(), paddle.getPaddleHeight());
// ball
g.setColor(Color.MAGENTA);
g.fillOval(ball.getBallX(), ball.getBallY(), ball.getBallWidth(), ball.getBallHeight());
// brick
g.setColor(Color.GREEN);
g.fillRect(bricks.getBrickX(), bricks.getBrickY(), bricks.getBrickWidth(), bricks.getBrickHeight());
}
//add any unimplemented methods because we are using an interface
/* #Override
public void actionPerformed(ActionEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT) {
paddle.moveLeft();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.moveRight();
}
this.repaint();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
} */
//create get and set methods for all panel attributes
public int getWidth() {
return width;
}
public void setWidth(int pWidth) {
this.width = pWidth;
}
public int getHeight() {
return height;
}
public void setHeight(int pHeight) {
this.height = pHeight;
}
}
Paddle class:
import javax.swing.AbstractAction;
import java.awt.event.*;
public class Paddle extends AbstractAction implements ActionListener {
//Global Variables
private int paddleWidth;
private int paddleHeight;
private int paddleX; //paddle x position
private int paddleY;
private GamePanel gamePanel;
//create paddle constructor
Paddle() {
}
//create a paddle constructor based off parameters
Paddle(GamePanel gPanel, int pX, int pY, int pWidth, int pHeight) {
//set the values
this.setPaddleWidth(pWidth);
this.setPaddleHeight(pHeight);
this.setPaddleX(pX);
this.setPaddleY(pY);
this.setGamePanel(gPanel);
}
//create get and set methods for all paddle attributes
public int getPaddleWidth() {
return paddleWidth;
}
public void setPaddleWidth(int pWidth) {
this.paddleWidth = pWidth;
}
public int getPaddleHeight() {
return paddleHeight;
}
public void setPaddleHeight(int pHeight) {
this.paddleHeight = pHeight;
}
public int getPaddleX() {
return paddleX;
}
public void setPaddleX(int pX) {
this.paddleX = pX;
}
public int getPaddleY() {
return paddleY;
}
public void setPaddleY(int pY) {
this.paddleY = pY;
}
public GamePanel getGamePanel() {
return gamePanel;
}
public void setGamePanel(GamePanel gPanel) {
this.gamePanel = gPanel;
}
//move the paddle left if it is not already positoned at 0 (far left)
public void moveLeft() {
try {
if(getPaddleX() <= 0) {
setPaddleX(0);
System.out.println("less than 0, x: " + getPaddleX());
}
else if (getPaddleX() > 0) {
setPaddleX(getPaddleX()-10); //to move paddle left -10
// gamePanel.repaint(getPaddleX()+10, getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint old position
// gamePanel.repaint(getPaddleX(), getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint new position
System.out.println("left, x: " + getPaddleX());
}
}
catch (Exception e) {
}
}
//move the paddle right if it is not already positioned to the far right of the panel
public void moveRight() {
if(getPaddleX() >= gamePanel.getWidth() - getPaddleWidth()) { //dont move the paddle if it is on the right edge of the panel
setPaddleX(gamePanel.getWidth() - getPaddleWidth());
System.out.println("right1, x:" + getPaddleX());
}
else if ((getPaddleX()+getPaddleWidth()) <= gamePanel.getWidth()){ //if the paddle is within the panel bounds
setPaddleX(getPaddleX() + 10); //to move paddle right +10
System.out.println("right, x:" + getPaddleX());
}
}
}
Here are some of the main resources I've being trying:
This one I tried but got really confused on the best way to integrate it into my own code --> https://tips4java.wordpress.com/2013/06/09/motion-using-the-keyboard/
This one I tried until I realised I don't have jcomponent --> https://coderanch.com/t/606742/java/key-bindings
You could register the key bindings with the GamePanel, or the frame content pane (both extend from JComponent). E.g.:
public class DemoFrame extends JFrame {
public static void main(String[] args) throws Exception {
new DemoFrame().setVisible(true);
}
public DemoFrame() {
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
{
String actionId = "left";
KeyStroke keyStroke = KeyStroke.getKeyStroke("LEFT");
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, actionId);
getRootPane().getActionMap().put(actionId, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
actionLeft();
}
});
}
}
private void actionLeft() {
System.out.println("left");
}
}
I am writing a Crazy Eights game and trying to have mouse control added in. I just starting writing it but I can't verify if it's working or not. I've added System.out.println() to the pressed and released event calls but no output happens. I just need to get it working and be able to see an output of some kind for debugging. I've also tried to use another example on stackoverflow to help me out but I'm still having issues. The below code is what I'm working with. Let me know if you need to see another class.
Thanks
MouseControl.java
package crazyeightscountdown.CoreClasses;
import java.awt.Canvas;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MouseControl extends MouseAdapter {
public Canvas canvas;
public MouseControl (Canvas c){
this.canvas = c;
}
#Override
public void mouseReleased (MouseEvent e){
System.out.println("Mouse Released.\n");
}
#Override
public void mousePressed (MouseEvent e){
System.out.println("Mouse Pressed.\n");
}
#Override
public void mouseClicked (MouseEvent e){
}
#Override
public void mouseEntered (MouseEvent e){
}
#Override
public void mouseExited (MouseEvent e){
}
}//class
Game.java
package crazyeightscountdown;
import static com.sun.java.accessibility.util.AWTEventMonitor.addMouseListener;
import static crazyeightscountdown.CoreClasses.Constants.CARDPICX;
import crazyeightscountdown.CoreClasses.Deck;
import crazyeightscountdown.CoreClasses.MouseControl;
import crazyeightscountdown.CoreClasses.Player;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
//Sets up parameters for the game window
public class Game implements Runnable {
private Display display;
public int width, height;
public Game(String title, int width, int height) {
this.width = width;
this.height = height;
display = new Display(title, width, height);
StartGame();
}
//create the game decks
//Deck maindeck = new Deck();
public Deck faceupdeck = new Deck();
public Deck facedowndeck = new Deck();
Deck tempdeck = new Deck();
public int deckindex = 0;
public Player playerone = new Player();
public Player playertwo = new Player();
private BufferStrategy bs;
private Graphics g;
private boolean running = false;
private Thread thread;
public void StartGame() {
//setup mouse
addMouseListener (new MouseControl(display.getCanvas()));
//set players
playerone.SetPlayer(1);
playertwo.SetPlayer(2);
//set values to main deck
facedowndeck = facedowndeck.SetDeck(facedowndeck);
//shuffle the deck
facedowndeck = facedowndeck.ShuffleDeck(facedowndeck);
//hand out first deal
FirstDeal();
}
public void FirstDeal() {
int playerindex = 1;
deckindex = 1;
//deal each player 8 cards to start
for (int h = 0; h < 8; h++) {
playerone.hand.card[playerindex] = facedowndeck.card[deckindex];
facedowndeck.card[deckindex].present = false;
playerone.hand.card[playerindex].present = true;
deckindex++;
playertwo.hand.card[playerindex] = facedowndeck.card[deckindex];
facedowndeck.card[deckindex].present = false;
playerone.hand.card[playerindex].present = true;
deckindex++;
playerindex++;
//facedowndeck.Truncate(facedowndeck);
}
//put card face up
faceupdeck.card[1] = facedowndeck.card[deckindex];
deckindex++;
}
private void render() {
bs = display.getCanvas().getBufferStrategy();
if (bs == null) {
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
//Clear Screen
g.clearRect(0, 0, width, height);
/******* START DRAWING HERE **********/
//draw player1 deck
for (int f = 1; f < 9; f++) {
g.drawImage(playerone.hand.card[f].pic, (CARDPICX * (f - 1)) + (f * 5), 5, null);
g.drawImage(playertwo.hand.card[f].pic, (CARDPICX * (f - 1)) + (f * 5), 450, null);
}
g.drawImage(faceupdeck.card[1].pic,400, 200, null);
/*********** END DRAWING HERE ***********/
bs.show();
g.dispose();
}
private void tick() {
}
public void run() {
//init();
while (running) {
tick();
render();
}
stop();
}
public synchronized void start() {
if (running) {
return;
}
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
if (!running) {
return;
}
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//class
Display.java
package crazyeightscountdown;
import java.awt.Canvas;
import java.awt.Dimension;
import javax.swing.JFrame;
//display parameters for the window
public class Display {
public JFrame frame;
public Canvas canvas;
public String title;
public int width, height;
public Display(String title, int width, int height){
this.title = title;
this.width = width;
this.height = height;
createDisplay();
}
private void createDisplay(){
frame = new JFrame(title);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(width, height));
frame.add(canvas);
frame.pack();
}
public Canvas getCanvas(){
return canvas;
}
}
You have to add the MouseListener to a component: frame.addMouseListener(...)
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.
I have a screen with say 500 width and 400 height, and I have a vector with a bunch of shapes. let say the vector has 2 different shapes for example. I want the object to randomly pop up from the bottom of the screen reach a certain ascent and then fall back down (similar to game fruit ninja, where the fruits are my shapes).
In my main (view) I have a vector of shapes of which i instantiate the timers, add to array and place them in the buttom of the screen using the translate function. My timer takes in an action listener which basically changes the translate of the shape to move up till ascent and then down, but my problem is that all the shapes start at the same time regardless.
Something like this:
Shape f = new Shape(new Area(new Ellipse2D.Double(0, 50, 50, 50)));
f.translate(0, 400);
f.timer = new Timer( 10 , taskPerformer);
f.timer.start();
vector.add(f);
Shape f2 = new Shape(new Area(new Rectangle2D.Double(0, 50, 50, 50)));
f2.translate(200, 400);
f2.timer = new Timer( 10 , taskPerformer);
f2.timer.setInitialDelay(5000);
f2.timer.start();
vector.add(f2);
and my action listener:
Random generator = new Random();
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
for (Shape s : model.getShapes()) {
// Scale object using translate
// once reached ascent drop down
// translate to diffrenet part of the bottom of the screen
// delay its timer
}
update();
//basically repaints
}
};
I'm running into problems that all shapes follow the same timer, and begin to pop up at the same time (no delay) ...
Any suggestions on how to avoid this or if there is a different approach i should try
"I want the object to randomly pop up from the bottom of the screen reach a certain ascent and then fall back down"
See the runnable example below. What I do is pass a radomDelayedStart to the Shape. Every tick of the timer, the randomDelayedStart decreases til it reaches 0, that's when the flag to be drawn in raised. Most of the logic is in the Shape class methods, which are called in the Timers Actionlistener. Everything is done in one Timer. For the ascent, I just used a hard coded 50, but you can also pass a random ascent to the Shape. Let me know if you have any questions. I tried to made the code as clear as possible.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class RandomShape extends JPanel {
private static final int D_HEIGHT = 500;
private static final int D_WIDTH = 400;
private static final int INCREMENT = 8;
private List<Shape> shapes;
private List<Color> colors;
private Timer timer = null;
public RandomShape() {
colors = createColorList();
shapes = createShapeList();
timer = new Timer(30, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Shape shape : shapes) {
shape.move();
shape.decreaseDelay();
repaint();
}
}
});
JButton start = new JButton("Start");
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
JButton reset = new JButton("Reset");
reset.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
shapes = createShapeList();
timer.restart();
}
});
JPanel panel = new JPanel();
panel.add(start);
panel.add(reset);
setLayout(new BorderLayout());
add(panel, BorderLayout.PAGE_START);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
shape.drawShape(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_WIDTH, D_HEIGHT);
}
private List<Color> createColorList() {
List<Color> list = new ArrayList<>();
list.add(Color.BLUE);
list.add(Color.GREEN);
list.add(Color.ORANGE);
list.add(Color.MAGENTA);
list.add(Color.CYAN);
list.add(Color.PINK);
return list;
}
private List<Shape> createShapeList() {
List<Shape> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 20; i++) {
int randXLoc = random.nextInt(D_WIDTH);
int randomDelayedStart = random.nextInt(100);
int colorIndex = random.nextInt(colors.size());
Color color = colors.get(colorIndex);
list.add(new Shape(randXLoc, randomDelayedStart, color));
}
return list;
}
class Shape {
int randXLoc;
int y = D_HEIGHT;
int randomDelayedStart;
boolean draw = false;
boolean down = false;
Color color;
public Shape(int randXLoc, int randomDelayedStart, Color color) {
this.randXLoc = randXLoc;
this.randomDelayedStart = randomDelayedStart;
this.color = color;
}
public void drawShape(Graphics g) {
if (draw) {
g.setColor(color);
g.fillOval(randXLoc, y, 30, 30);
}
}
public void move() {
if (draw) {
if (y <= 50) {
down = true;
}
if (down) {
y += INCREMENT;
} else {
y -= INCREMENT;
}
}
}
public void decreaseDelay() {
if (randomDelayedStart <= 0) {
draw = true;
} else {
randomDelayedStart -= 1;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new RandomShape());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}