Using threads on animations in Java - java

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
}
}

Related

How do you rotate a circle about a point in JPanel?

I am trying to rotate a circle around a separate point in a program. right now I can get the circle to rotate but it slowly starts getting closer and closer to the point it's rotating from. I am trying to do this using JPanel and implementing it as a rectangle.
package WoffindenZone;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.lang.Math;
public class Protector extends Rectangle{
double Velocity;
int speed = 3;
Protector(int x, int y, int PROTECTOR_DIAMETER){
super(x,y,PROTECTOR_DIAMETER,PROTECTOR_DIAMETER);
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(speed);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(speed);
move();
}
}
public void keyReleased(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(0);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(0);
move();
}
}
public void setDirection(int Direction){
Velocity = Direction*Math.PI/180;
}
public void move(){
x = (int)Math.round(500 + Math.cos(Velocity) * (x-500) - Math.sin(Velocity) * (y-((1000*0.5555)/2)));
y = (int)Math.round(((1000*0.5555)/2) + Math.sin(Velocity) * (x-500) + Math.cos(Velocity) * (y-((1000*0.5555)/2)));
System.out.println(x);
System.out.println(y);
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillOval(x,y,width,height);
}
Use a rotation instance of an AffineTransform. See getRotateInstance(theta,anchorx,anchory) for details.
Returns a transform that rotates coordinates around an anchor point. This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).
How do you rotate a circle about a point in JPanel?
Here's how I rotate a circle about a point in a JPanel.
I don't know how to make an animated GIF. Just imagine the blue circle rotating clockwise around the center of the drawing JPanel.
So, let's start at the beginning. Basically, I have a circle rotating on the circumference of another circle. So, I create a Circle model class from plain Java.
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
The class consists of basic getters and setters. I make the radius and color final because they don't change value in this Java application.
The calculateCircumferencePoint method is the only interesting method. It takes an int angle in degrees, and calculates the point on the circumference represented by that angle, rounded to the nearest X and Y integer points.
Next, we create two Circle instances, an inner circle and an outer circle. Here's the class constructor setting the preferred size of the drawing area, the inner circle, and the outer circle. We start the outer circle at zero degrees (to the right);
private Circle innerCircle;
private Circle outerCircle;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
Now, we can start coding the GUI. First, we start the Java application by calling the SwingUtilities invokeLater method. This method ensures that we create and execute the Swing components on the Event Dispatch Thread.
Next, we define the JFrame. Here's the code we have so far.
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
The JFrame methods must be called in a certain order. This is the order I use for most of my SWwing applications.
I pack the JFrame. I don't set a JFrame size. I let the Swing layout managers set the size of my JFrame. The default layout of a JFrame content pane is a BorderLayout. I put my drawing JPanel in the center of the BorderLayout.
Next, I create the drawing JPanel.
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
All the drawing JPanel does is paint a Circle object. Pretty straightforward.
The fillOval method paints an oval from the upper left hand corner. We calculate the upper left hand point from the center point.
The responsibility for calculating and updating the outer circle center point falls to my controller class, the Animation class. I use a simple loop to update the theta angle, calculate the new outer circle center point, paint the outer circle, and wait a period of time.
Here's that code.
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The Animation repaint method makes the call to the drawing JPanel repaint method inside another SwingUtilities invokeLater method. This method ensures that the drawing happens on the Event Dispatch Thread.
Finally, here's the complete, runnable, example. I used inner classes so I could post the code as one block, and you can copy and run this code as one block. Generally, classes should be in separate files and for a more complex GUI, separate packages.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RotateCircle implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
}

How to multithread with paintComponent()?

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);
}
}

Stop pause on key hold with KeyBindings Java

I'm trying to write a Pong applet in Java. When the user holds down either up or down, their paddle should move smoothly, but this isn't the case. It moves, then pauses, then starts moving again. Is there a way to stop this short pause from happening?
Main Class:
public class Main extends JApplet {
public DrawPanel dp = new DrawPanel(400, 400);
public void init(){
add(dp);
setSize(400, 400);
requestFocusInWindow();
Action moveDown = new AbstractAction(){
public void actionPerformed(ActionEvent e){
dp.player.y += 10;
dp.repaint();
}
};
dp.getInputMap().put(KeyStroke.getKeyStroke("pressed DOWN"), "move down");
dp.getActionMap().put("move down", moveDown);
}
}
DrawPanel Class:
public class DrawPanel extends JPanel {
public Paddle player;
public Paddle opponent;
public DrawPanel(int height, int width){
int y = (height / 2) - (height / 10);
int w = 15;
int h = height / 5;
player = new Paddle(2, y, 15, h, Color.white);
opponent = new Paddle(width - (w+2), y, 15, h, Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.BLACK);
g.setColor(player.color);
g.fillRect(player.x, player.y, player.width, player.height);
g.setColor(opponent.color);
g.fillRect(opponent.x, opponent.y, opponent.width, opponent.height);
}
}
Paddle Class:
public class Paddle {
public int x, y, width, height;
public Color color;
public Paddle(int _x_, int _y_, int w, int h, Color c){
x = _x_;
y = _y_;
width = w;
height = h;
color = c;
}
}
The underlying issue is an impedance mismatch between the "instance" notion of a keyPressed and the "duration" notion of the movement.
Instead of trying to smooth that over in the view (which shouldn't have anything to do with the details of the game logic anyway), enhance the game model with api that's a better fit. F.i. add start/stop logic and bind those methods to keyPressed/keyReleased:
public class Paddle {
public void startMoveDown() {
// here goes the logic, f.i. starting a timer
// in the actionPerformed of that timer:
... moveUnitDown();
}
public void stopMoveDown() {
//...
}
protected void moveUnitDown() {
y+=unit;
// ideally, have listeners and notify them the change of y
}
}
// in the view:
Action startMoveDown = new AbstractAction(){
public void actionPerformed(ActionEvent e){
player.startMoveDown();
}
};
dp.getInputMap().put(KeyStroke.getKeyStroke("pressed DOWN"), "start move down");
dp.getActionMap().put("start move down", startMoveDown);
// in the view:
Action stopMoveDown = new AbstractAction(){
public void actionPerformed(ActionEvent e){
player.stopMoveDown();
}
};
dp.getInputMap().put(KeyStroke.getKeyStroke("released DOWN"), "stop move down");
dp.getActionMap().put("stop move down", stopMoveDown);

animation using repaint()

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) { }
}
}

Graphics drawn oval will not increase in size in java

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.

Categories