I am writing a simple game in java using Ovals and the Graphics objects. It is called virus and works like this: There is an oval in the middle, and six ovals around the outside. These outside ovals are supposed to increase in size until clicked on, when they will disappear and the player scores ten points. If an oval touches the central oval, the health of the centre oval goes down. When it hits zero, the game ends. The problem im having is that the outside ovals will not increase in size. Why is this happening?
Here is my code:
package virus;
import java.awt.*;
import java.util.Random;
import javax.swing.JPanel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class VirusGamePanel extends JPanel implements MouseListener{
private static final long serialVersionUID = 1L;//serialVersionUID field
Random colour = new Random();//the outside ovals will always be a random colour
private int sizeX = 1;//the x size of the outside ovals
private int sizeY = 1;//the y size of the outside ovals
int score = 0;
static String scorestring = "Score: ";
Color rand = new Color(colour.nextInt(255),colour.nextInt(255),colour.nextInt(255));//generate the random colour
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.magenta);
g.drawString(scorestring+score,275,250);
g.setColor(Color.orange);
g.drawOval(200,150,200,200);
g.setColor(rand);
g.drawOval(270,50,50,50);
g.drawOval(100,100,50,50);
g.drawOval(450,100,50,50);
g.drawOval(100,400,50,50);
g.drawOval(450,400,50,50);
g.drawOval(275,450,50,50);
g.fillOval(270,50,sizeX,sizeY);//these six ovals are supposed to increase in size
g.fillOval(100,100,sizeX,sizeY);
g.fillOval(450,100,sizeX,sizeY);
g.fillOval(100,400,sizeX,sizeY);
g.fillOval(450,400,sizeX,sizeY);
g.fillOval(275,450,sizeX,sizeY);
inc();
}
public static void main(String[] args) {
JPanel panel = new VirusGamePanel();
JFrame frame = new JFrame("Virus");
frame.setSize(700, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
private void inc()//increase the size of the ovals
{
for(int i = 0; i<25000; i++)
{
sizeX++;
sizeY++;
repaint();
}
}
New code with thread:
package virus;
import java.awt.*;
import java.util.Random;
import javax.swing.JPanel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class VirusGamePanel extends JPanel implements MouseListener,Runnable{
private static final long serialVersionUID = 1L;
Random colour = new Random();
private int sizeX = 1;
private int sizeY = 1;
int score = 0;
Thread thr = new Thread();
static String scorestring = "Score: ";
Color rand = new Color(colour.nextInt(255),colour.nextInt(255),colour.nextInt(255));
public void paint(Graphics g)
{
super.paint(g);
thr.start();
g.setColor(Color.magenta);
g.drawString(scorestring+score,275,250);
g.setColor(Color.orange);
g.drawOval(200,150,200,200);
g.setColor(rand);
g.fillOval(270,50,sizeX,sizeY);
g.fillOval(100,100,sizeX,sizeY);
g.fillOval(450,100,sizeX,sizeY);
g.fillOval(100,400,sizeX,sizeY);
g.fillOval(450,400,sizeX,sizeY);
g.fillOval(275,450,sizeX,sizeY);
inc();
}
public static void main(String[] args) {}
private void inc()
{
thr.run();
}
public void run(){
for(int i = 0; i<25000; i++)
{
sizeX++;
sizeY++;
repaint();
try
{
Thread.sleep(10);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
This works for me:
public class VirusGamePanel extends JPanel{
private static final long serialVersionUID = 1L;//serialVersionUID field
Random colour = new Random();//the outside ovals will always be a random colour
private int sizeX = 0;//the x size of the outside ovals
private int sizeY = 0;//the y size of the outside ovals
int score = 0;
static String scorestring = "Score: ";
Color rand = new Color(colour.nextInt(255), colour.nextInt(255), colour.nextInt(255)); //generate the random colour
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.magenta);
g.drawString(scorestring+score,275,250);
g.setColor(Color.orange);
g.drawOval(200, 150, 200, 200);
g.setColor(rand);
g.fillOval(270 - sizeX / 2, 50 - sizeY / 2, sizeX, sizeY);//these six ovals are supposed to increase in size
g.fillOval(100 - sizeX / 2,100 - sizeY / 2, sizeX, sizeY);
g.fillOval(450 - sizeX / 2,100 - sizeY / 2, sizeX, sizeY);
g.fillOval(100 - sizeX / 2,400 - sizeY / 2, sizeX, sizeY);
g.fillOval(450 - sizeX / 2,400 - sizeY / 2, sizeX, sizeY);
g.fillOval(275 - sizeX / 2,450 - sizeY / 2, sizeX, sizeY);
inc();
}
public static void main(String[] args) {
JPanel panel = new VirusGamePanel();
JFrame frame = new JFrame("Virus");
frame.setSize(700, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
private void inc()//increase the size of the ovals
{
sizeX++;
sizeY++;
repaint();
}
}
Just fixed the center points of the ovals and removed the loop in the inc method. If you want the oval boundaries drawn, just add the drawOval commands with the same parameters as the fillOval commands.
EDIT:
If you want to slow down the growth process, just add the following just before the inc() call of the paint method:
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Your code is recursive. In inc(), you call repaint, which calls VirusGamePanel.paint(), which calls inc() ...
Each call to repaint() will reinvoke the paint() method. So instead of looping in inc() do this:
private void inc()// increase the size of the ovals
{
sizeX++;
sizeY++;
repaint();
}
for(int i = 0; i<25000; i++)
{
sizeX++;
sizeY++;
repaint();
}
Your computer cannot repaint as fast as your cpu increases sizeX and sizeY. Add some waiting time in milliseconds(especially if you draw a big area like 800x600)
for(int i = 0; i<25000; i++)
{
sizeX++;
sizeY++;
repaint();
try{ sleep(100);} catch(InterruptedException e){}
}
But, do this from another thread! So, your app. will not freeze.
This is 10 FPS. But you tried 2G FPS. Good day.
Here is an example of a thread.
class TEN_FPS extends Thread
{
public void run()
{
while(working)
{
//calculations here
repaint();
try{sleep(100);}catch(InterruptedException e){}
}
}
}
Then in the main method:
working=true;
TEN_FPS.start();
When you are done with this program:
working=false;
will release your daemon thread.
Related
class GraphicsExampleComponent extends JComponent
{
//#Override
public void paintComponent(Graphics g)
{
//make the first call to your recursive routine
drawSquare1(g, 0, 0, 80);
}
public void drawSquare1(Graphics g, int x, int y, int size)
{
//draw a rectangle
g.drawRect(x, y, size, size);
g.fillRect(x, y, size, size);
//reset the parameters
x = x + size + 10;
y = y + (size/4);
size = size/4;
//determine if you should call it again.
if (size<4 || x>600)
drawSquare1(g, x, y, size);
}
My assignment is to create disappearing squares that get 25% smaller as they move to the right. When I run the code, it just creates the one square and stops. Can anyone help me understand what I am doing wrong.
disappearing squares that get 25% smaller as they move to the right.
Ok, let's step back for a second and break this down a bit.
You need to know...
The amount of space to be covered
The amount of space already covered (by the square)
The original size of the square
The size of the square should be when it reaches the other side (25% smaller)
When you have all this, you can calculate the size of the square at any point along its journey.
To determine the amount of space, you can use the component's width, via getWidth().
To determine the space already covered, you could start by having a look at box's current x position
// Assuming that box is a instance of Rectangle
double progress = (double)box.x / (double)getWidth();
We could argue that we should look at the middle of the box, or the trailing edge, but both of those are easy to implement.
Next, we need know the range of change (from start to end size), we can then use that to calculate the delta to be applied to the box...
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
Soooo, this will provide with the means to determine the size of the component based on it's current location (horizontally) through the component.
Next, you need some way to update the box's position and update the UI.
One of the better solutions is to use a Swing Timer. This will allow you to perform a repeating action (with a specified delay between updates) which won't block the UI and will generate updates within the Event Dispatching Queue, which is important because Swing is not Thread safe.
Have a look at How to Use Swing Timers for more details.
And finally, all we need, is to update the component with current state, via it's paintComponent method ... easy :P
Runnable example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static double startSize = 50;
protected static double endSize = startSize * 0.25;
private Rectangle box;
private Timer timer;
public TestPane() {
box = new Rectangle(0, 100 - 25, 50, 50);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (box.x + box.width >= getWidth()) {
box.x = getWidth() - box.width;
box.width = (int)endSize;
box.height = (int)endSize;
timer.stop();
repaint();
}
box.x += 1;
double progress = (double)box.x / (double)getWidth();
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
repaint();
}
});
}
#Override
public void addNotify() {
super.addNotify();
box.x = 0;
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(box);
g2d.dispose();
}
}
}
The condition if (size < 4 || x>600) is never true because when drawSquare1 is invoked for the first time size=80 and x=0.
Changing it to say if (size > 4 && x<600) will paint 3 squares on the screen without any noticeable animation.
To animate it we'll need to add some delay between paintings and remove previously painted squares.
To do so we use a swing Timer. We use the timer to repeatedly invoke drawSquare1.
drawSquare1 should modify the parameters controlling the painting, and call repaint.
import java.awt.*;
import javax.swing.*;
public class Main {
Main() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
GraphicsExampleComponent board = new GraphicsExampleComponent();
frame.add(board);
frame.pack();
frame.setVisible(true);
board.animate();
}
public static void main(String[] args) {
new Main();
}
}
class GraphicsExampleComponent extends JComponent{
private final static int W = 200, H = 100, MIN_SIZE = 4, STEP = 10, DELAY = 2000;
private int x= 0, y = 0, size = 80;
private Timer timer;
void animate(){
timer = new Timer(DELAY, e->drawSquare());
timer.start();
}
public void drawSquare(){
//check stop criteria
if (size < MIN_SIZE || x >= getWidth()) {
timer.stop();
return;
}
//reset the parameters
x = x + size + STEP;
y = y + size/4;
size = size/4;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//draw a rectangle
g.fillRect(x, y, size, size);
g.dispose();;
}
#Override
public Dimension preferredSize() {
return new Dimension(W, H);
}
}
(Test in online here)
Thank you everyone for the help. I unfortunately could not use a timer due to us not learning about it in class and we are not allowed to use methods/codes that we haven't gone over. To fix this I had to fix my base case by making it size >= 4 && x < 600.
I created an app that contains a square that bounces every time it touches an edge of the frame.I don't have issues lunching the app,the problem is that i don't know how to create various threads in order to have multiples squares inside the frame.
I tried multiple things but i can't figure out where i should create the threads.
I also noticed that the square is visible only when i add it directly inside the frame and not when i put it inside a JPanel.
Square.java
public class Square extends JComponent implements ActionListener {
int width = 20;
int height = 20;
double y = Math.random() * 360;
double x = Math.random() * 360;
boolean xMax = false;
boolean yMax = false;
boolean xMin = true;
boolean yMin = true;
Rectangle2D.Double square = new Rectangle2D.Double(x, y, width, height);
public Square() {
Timer t = new Timer(2, this);
t.start();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g);
g2.setColor(Color.BLUE);
g2.fill(square);
x_y_rules();
}
public void x_y_rules() {
if (xMax == true) {
x = x - 0.5;
if (x <= 0) {
xMax = false;
}
} else {
x = x + 0.5;
if (x >= this.getWidth()) {
xMax = true;
}
}
if (yMax == true) {
y = y - 0.5;
if (y <= 0) {
yMax = false;
}
} else {
y = y + 0.5;
if (y >= this.getHeight()) {
yMax = true;
}
}
square.setFrame(x, y, width, height);
}
#Override
public void actionPerformed(ActionEvent arg0) {
repaint();
}
}
App.java
public class App extends JFrame {
public static void main(String[] args) {
JFrame jf = new JFrame();
Square sqr = new Square();
jf.setSize(400, 400);
jf.setVisible(true);
jf.add(sqr);
jf.setDefaultCloseOperation(EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
}
}
Is it normal that despite i put a time of 2 inside the Timer,the square moves very slowly?
Issues:
you've got program logic, the x_y_rules() method call, inside of the paintComponent method. Get it out as it does not belong there, and instead put it into the Timer's ActionListener code where it belongs.
you can give each Square its own Swing Timer if you want. This isn't really a threading issue since each Timer's ActionListener will run on the EDT.
Two milliseconds is an unrealistic time slice to expect to use in a Swing Timer and no timer will run that fast. 11 to 13 is about the fastest to expect or hope for.
if you want your sprite to move faster, give it a greater value for delta-x and delta-y in your movement code.
Your JComponent has no preferred size defined which is likely why it's not showing up in the JPanel, since the default FlowLayout will size it then to [0, 0]. Override its getPreferredSize() and have it return a reasonable Dimension value.
you're calling setVisible(true) on your JFrame before adding all components, a no-no.
Ok,i put a getPrefferedSize() inside the square class but i've encountered a problem: the squares are not "together",it's like they're bouncing on separate panels
Then your program structure is broken. You really don't want create separate Swing components, and in fact your Square class shouldn't extend JComponent or JPanel. Rather
Square should be a logical class, one that extends from nothing (other than default Object).
Give it a drawing method, say public void draw(Graphics g) {....}
Create one class that extends JPanel, say called DrawingPanel, and override its paintComponent method.
Give the DrawingPanel class an ArrayList<Square> so that it can hold multiple Square objects.
Give the DrawingPanel class a Swing Timer
In the DrawingPanel class's Timer, have it update the position of all the Squares in the ArrayList, and then call repaint()
In the paintComponent method, iterate through all the Squares in the list, using a for loop, and call each one's draw method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 20;
private static final Color[] SQUARE_COLOR = { Color.BLUE, Color.CYAN, Color.DARK_GRAY,
Color.BLACK, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
Color.PINK, Color.RED, Color.YELLOW };
List<Square> squareList = new ArrayList<>();
public DrawingPanel() {
// create a bunch of squares
for (int i = 0; i < SQUARE_COLOR.length; i++) {
squareList.add(new Square(SQUARE_COLOR[i], PREF_W, PREF_H));
}
setBackground(Color.WHITE);
// create and start the timer
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// simply draw all the squares in the list
for (Square square : squareList) {
square.draw(g);
}
}
// set size of JPanel
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// simply iterate through list and move all squares
for (Square square : squareList) {
square.move();
}
repaint(); // then repaint the GUI
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = new DrawingPanel();
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
// this class does *not* extend JPanel or JComponent
class Square {
public static final int WIDTH = 20;
// location of Square
private double sqrX;
private double sqrY;
// X and Y speed
private double deltaX;
private double deltaY;
// width and height of DrawingPanel JPanel
private int dpWidth;
private int dpHeight;
// image to draw
private Image image;
public Square(Color color, int dpWidth, int dpHeight) {
this.dpWidth = dpWidth;
this.dpHeight = dpHeight;
// create square at random location with random speed
sqrX = Math.random() * (dpWidth - WIDTH);
sqrY = Math.random() * (dpHeight - WIDTH);
deltaX = Math.random() * 10 - 5;
deltaY = Math.random() * 10 - 5;
// one way to draw it is to create an image and draw it
image = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, WIDTH, WIDTH);
g.dispose();
}
public void move() {
// check that we're not hitting boundaries
if (sqrX + deltaX < 0) {
deltaX = Math.abs(deltaX);
}
if (sqrX + deltaX + WIDTH >= dpWidth) {
deltaX = -Math.abs(deltaX);
}
sqrX += deltaX;
// check that we're not hitting boundaries
if (sqrY + deltaY < 0) {
deltaY = Math.abs(deltaY);
}
if (sqrY + deltaY + WIDTH >= dpHeight) {
deltaY = -Math.abs(deltaY);
}
sqrY += deltaY;
}
public void draw(Graphics g) {
int x = (int) sqrX;
int y = (int) sqrY;
g.drawImage(image, x, y, null);
}
}
I need help with a simple animation assignment. It goes as follows.
I have two stop lights on a JPanel and the object is for the two of them to have different time intervals i.e there lights cycle at different times.
Everything works fine if I only have one light at a time. I am relatively new to this but I believe I know the problem.
In the code under this text, I use this several times. I believe my issue occurs in the public void cycle() method in which it just says this.repaint(); I have a feeling that the panel is being repainted at the two different time periods and it gives me a somewhat random light changing instead of a nice cycle.
Is there a way I can have these two components on the same JPanel with a more specific repaint method (maybe a bounding box around the individual light fixtures) or would creating separate panels be a better option (and a little help if that is the case because I understand the basic layouts but have never used them before).
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class DrawingPanel extends JPanel implements Lighter
{
// instance variables
private final int INTERVAL1 = 2000;
private final int INTERVAL2 = 5000;
private TrafficLight _light1, _light2;
private LightTimer _timer1,_timer2;
/**
* Constructor for objects of class DrawingPanel
*/
public DrawingPanel()
{
// initialise instance variables
super();
this.setBackground(Color.CYAN);
_light1 = new TrafficLight(50,50);
_light2 = new TrafficLight(200,50);
_timer1 = new LightTimer(INTERVAL1,this);
_timer2 = new LightTimer(INTERVAL2,this);
_timer1.start();
_timer2.start();
}
public void cycle(){
_light1.cycle();
_light2.cycle();
this.repaint();
}
public void paintComponent(Graphics pen)
{
super.paintComponent(pen);
Graphics2D aBetterPen = (Graphics2D)pen;
_light1.fill(aBetterPen);
_light2.fill(aBetterPen);
}
}
Running two timers in the fashion is achievable. Personally, I would write a "signal" class that controls a single light with it's own timing and painting routines, but that's not what you've asked.
What you need to do is maintain some kind of state variable for each signal and update them separately.
You would then need to modify the paint code to detect these states and take appropriate actions...for example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestBlink {
public static void main(String[] args) {
new TestBlink();
}
public TestBlink() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
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 Timer blink1;
private Timer blink2;
private boolean light1 = false;
private boolean light2 = false;
public TestPane() {
blink1 = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
light1 = !light1;
repaint();
}
});
blink2 = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
light2 = !light2;
repaint();
}
});
blink1.start();
blink2.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = 20;
int x = (getWidth() - (radius * 2)) / 2;
int y = (getHeight() - (radius * 2)) / 2;
Ellipse2D signal1 = new Ellipse2D.Float(x, y, radius, radius);
Ellipse2D signal2 = new Ellipse2D.Float(x + radius, y, radius, radius);
g2d.setColor(Color.RED);
g2d.draw(signal1);
if (light1) {
g2d.fill(signal1);
}
g2d.setColor(Color.GREEN);
g2d.draw(signal2);
if (light2) {
g2d.fill(signal2);
}
g2d.dispose();
}
}
}
Updated
A better solution (as I stated earlier) would be to contain all the logic for a single sequence in a single class. This isolates the painting and allows you to change the individual nature each sequence.
For example...
This is a simple example which uses a fixed rate of change, so each light gets the same about of time...
public class TraficLight01 extends JPanel {
public static final int RADIUS = 20;
private Timer timer;
private int state = 0;
public TraficLight01() {
timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
state++;
if (state > 2) {
state = 0;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(RADIUS, (RADIUS + 1) * 3);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = 20;
Ellipse2D light = new Ellipse2D.Float(0, 0, RADIUS, RADIUS);
int x = (getWidth() - radius) / 2;
int y = ((getHeight()- (radius * 3)) / 2) + (radius * 2);
Color color[] = new Color[]{Color.RED, Color.YELLOW, Color.GREEN};
for (int index = 0; index < color.length; index++) {
g2d.translate(x, y);
g2d.setColor(color[index]);
g2d.draw(light);
if (state == index) {
g2d.fill(light);
}
g2d.translate(-x, -y);
y -= radius + 1;
}
g2d.dispose();
}
}
Or you provide a variable interval for each light...
public static class TraficLight02 extends JPanel {
public static final int RADIUS = 20;
private Timer timer;
private int state = 0;
// Green, Yellow, Red
private int[] intervals = new int[]{3000, 500, 3000};
public TraficLight02() {
timer = new Timer(intervals[0], new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
state++;
if (state > 2) {
state = 0;
}
timer.setInitialDelay(intervals[state]);
repaint();
timer.restart();
}
});
timer.start();
timer.setRepeats(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(RADIUS, (RADIUS + 1) * 3);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = 20;
Ellipse2D light = new Ellipse2D.Float(0, 0, RADIUS, RADIUS);
int x = (getWidth() - radius) / 2;
int y = ((getHeight()- (radius * 3)) / 2) + (radius * 2);
Color color[] = new Color[]{Color.GREEN, Color.YELLOW, Color.RED};
for (int index = 0; index < color.length; index++) {
g2d.translate(x, y);
g2d.setColor(color[index]);
g2d.draw(light);
if (state == index) {
g2d.fill(light);
}
g2d.translate(-x, -y);
y -= radius + 1;
}
g2d.dispose();
}
}
It wouldn't take much to change these two concepts to seed them with variable intervals via a setter method.
Equally, you could use three Timers, each time one fires, you would simply start the next one. This way you could define a chain of timers, each one firing at different intervals after the completion of it's parent...
As a Note, your comment
it gives me a somewhat random light changing instead of a nice cycle.
What are you expecting it to look like?
With the Time intervals you have set it may appear somewhat random but it is actually working ie your intervals would work like this (well my assumptions of what the Interval variable are for)
Time(s) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Light1 ON OFF ON OFF ON OFF ON OFF
Light2 ON OFF ON
I have created a class whit two shapes namely two ovals. Here I draw them.
import ...;
public class Drawings extends JPanel{
public double degrees = 0;
public void paintComponent(Graphics g){
super.paintComponent(g);
int xcen = getWidth() / 2;
int ycen = getHeight()/ 2;
int radius = 10;
degrees++;
double radians = Math.toRadians(degrees);
int posx = (int)(100.getDistance() * Math.cos(radians));
int posy = (int)(100.getDistance() * Math.sin(radians));
g.setColor(Color.BLUE);
g.FillOval(xcen + posx, ycen + posy, 20, 20);
g.setColor(Color.GREEN);
g.drawOval(xcen + posx, ycen + posy, 100,100)
}
}
Now I implement it in a main.
import ....;
public class Animate extends JFrame{
public static void main(String [] args)
{
JFrame window = new JFrame();
window.add(new Drawings());
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(500,500);
window.setLocationRelativeTo(null);
window.setVisible(true);
//now I implement the thread to animate the two shapes
Thread paintThread = new Thread(new Runnable(){
#Override
public void run(){
while(true)
{
window.repaint();
try{
Thread.sleep(25);//determines how slow the ovals will move
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
});
paintThread.start();//start the animation
}
}
When the program runs the two Ovals rotate on the screen. But the two ovals rotates at the same speed as I would expect but I would like the two ovals to move at diffident speeds.
I have tried using a method to move them at different speed but with no success.
How would I get the two ovals moving at different speeds?
Make a class to represent an oval. Make two instances. Give the two instances different angular velocities. Currently because you increment degrees by 1.0 every 25 ms you have an angular velocity fixed at 40 degrees per second. If each oval has its own degrees field and you increment the two by different amounts, the ovals will rotate at different rates.
The easiest way is:
import ...;
public class Drawings extends JPanel{
public double degrees = 0;
private int firstOvalSpeed;
private int secondOvalSpeed;
public void paintComponent(Graphics g){
super.paintComponent(g);
int xcen = getWidth() / 2;
int ycen = getHeight()/ 2;
int radius = 10;
degrees++;
double radians = Math.toRadians(degrees);
int posx = (int)(100.getDistance() * Math.cos(radians));
int posy = (int)(100.getDistance() * Math.sin(radians));
g.setColor(Color.BLUE);
g.FillOval(xcen + posx*firstOvalSpeed, ycen + posy*firstOvalSpeed, 20, 20);
g.setColor(Color.GREEN);
g.drawOval(xcen + posx*secondOvalSpeed, ycen + posy*secondOvalSpeed, 100,100)
}
public void setFirstOvalSpeed(int firstOvalSpeed) {
this.firstOvalSpeed = firstOvalSpeed;
}
public void setSecondOvalSpeed(int secondOvalSpeed) {
this.secondOvalSpeed = secondOvalSpeed;
}
}
public class Animate extends JFrame {
public static void main(String[] args) {
final JFrame window = new JFrame();
Drawings drawings = new Drawings();
drawings.setFirstOvalSpeed(1);
drawings.setSecondOvalSpeed(2);
window.add(drawings);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(500, 500);
window.setLocationRelativeTo(null);
window.setVisible(true);
// now I implement the thread to animate the two shapes
Thread paintThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
window.repaint();
try {
Thread.sleep(25);// determines how slow the ovals will
// move
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
paintThread.start();// start the animation
}
}
I am trying to create a simple animation which draws random rectangles when a button is pressed. So far I managed to create rectangle on the press of a button. I want to further develop the code so that when I press the button, more than multiple random rectangles are created. I tried to create a for loop which asks the inner class to repaint itself but it still didn't work. can anyone help me please.
public class TwoButtonsRandomRec {
JFrame frame;
private int width = 500;
private int height = 500;
private DrawPanel dp;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public static void main (String[] args)
{
TwoButtonsRandomRec test = new TwoButtonsRandomRec();
test.go();
}
public void go()
{
dp = new DrawPanel();
JButton start = new JButton("Start");
start.addActionListener(new startListener());
JButton stop = new JButton("Stop");
stop.addActionListener(new stopListener());
frame = new JFrame();
frame.setSize(getWidth(), getHeight());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.NORTH, start);
frame.getContentPane().add(BorderLayout.SOUTH, stop);
}
class startListener implements ActionListener{
public void actionPerformed(ActionEvent event){
frame.getContentPane().add(BorderLayout.CENTER, dp);
frame.repaint();
frame.getRootPane().revalidate();
for(int i=0; i<10; i++){
dp.repaint();
}
}
}
class stopListener implements ActionListener{
public void actionPerformed(ActionEvent event){
System.out.println("stop");
}
}
class DrawPanel extends JPanel{
public void paintComponent(Graphics g){
int w = 5+(int)(Math.random() * width-5);
int h = 5+(int)(Math.random() * height-5);
int maxX = width-w; // diffX & diffY are used to ensure that rectangle is
int maxY = width-h; // draw completely inside the window
int x = (int)(Math.random() * maxX);
int y = (int)(Math.random() * maxY);
Color color = new Color((int) (Math.random()*256), // random red
(int) (Math.random()*256), // random green
(int) (Math.random()*256));// random blue
g.setColor(color);
g.fillRect(x,y,w,h);
}
}
}
repaint() simply tells Swing "when you'll have time, please repaint this area". So if you add rectangles in a loop and call repaint at each iteration, all the rectangles will only appear after the loop has finished, and the action event has been handled.
To have an animation, you need to loop in a separate thread. The easiest way to do that is to use a Swing Timer. When the Start button is started, start a timer which adds a random rectangle and calls repaint() every X milliseconds. When the Stop button is pressed, stop the timer.
What you should do is to put the loop inside paintComponent method and not call repaint in the loop.
So your paintComponent method should look like this:
public void paintComponent(Graphics g){
for (int i = 0; i < 10; i++) {
int w = 5+(int)(Math.random() * width-5);
int h = 5+(int)(Math.random() * height-5);
int maxX = width-w; // diffX & diffY are used to ensure that rectangle is
int maxY = width-h; // draw completely inside the window
int x = (int)(Math.random() * maxX);
int y = (int)(Math.random() * maxY);
Color color = new Color((int) (Math.random()*256), // random red
(int) (Math.random()*256), // random green
(int) (Math.random()*256));// random blue
g.setColor(color);
g.fillRect(x,y,w,h);
}
}
And your action performed should look like this:
public void actionPerformed(ActionEvent event){
frame.getContentPane().add(BorderLayout.CENTER, dp);
frame.repaint();
frame.getRootPane().revalidate();
dp.repaint();
}
Well,here I have done a short EG for you.It displays random rectangles, random times on random screen location.
(You can set your own value of randomization, and the screen location max bound,as per your requirements.)
And also note
int i=(int)(Math.random()*10);
int j=(int)(Math.random()*10);
for(;i<j;i++)
Where at times i may be > than j.So,loop may not work on one or two cliks.Change as per your need.
Here is the working code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SimpleStamper extends JApplet {
public void init() {
Display display = new Display();
setContentPane(display);
}
class Display extends JPanel implements MouseListener {
Display() {
setBackground(Color.black);
addMouseListener(this);
}
public void mousePressed(MouseEvent evt) {
if ( evt.isShiftDown() ) {
repaint();
return;
}
int x = evt.getX();
int y = evt.getY();
Graphics g = getGraphics();
//***MODIFY THE FOLLOWING LINES****//
int i=(int)(Math.random()*10);
int j=(int)(Math.random()*10);
for(;i<j;i++)
{ g.setColor(Color.red);
x=(int)(Math.random()*100);
y=(int)(Math.random()*100);
g.fillRect( x , y , 60, 30 );
g.setColor(Color.black);
g.drawRect(x , y , 60, 30 );}
g.dispose();
}
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseReleased(MouseEvent evt) { }
}
}