Simple Java Swing Animation Lag - java

I am a java beginner and I'm having trouble with a simple swing animation I created. The animation lags when running unless something else is happening like mouse movement or a key being pressed down. I have searched for answers but none of them solve this problem.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Animation extends JPanel implements ActionListener {
Timer timer = new Timer(5, this);
int y = 0, velY = 2;
public void actionPerformed(ActionEvent e) {
y += velY;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.fillRect(50, y, 50, 50);
timer.start();
}
public static void main(String[] args) {
Animation drawPanel = new Animation();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setVisible(true);
frame.add(drawPanel);
}
}

First and foremost, get that timer.start() out of your paintComponent method since it does not belong there, and serves no purpose other than to slow the rendering down. Instead you should call that method once, and once only.
Next, 5 mSecs may be an unrealistic timer delay. Experiment with this number, but expect to get a decent functioning somewhere near 10 to 15 mSecs.
Next, base your position change on actual time slice differences that have been measured, not on what you're hoping the timer is doing.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Animation extends JPanel {
private static final int TIMER_DELAY = 16;
private static final double Y_VELOCITY = 0.05;
private double dY = 0.0;
private Timer timer = new Timer(TIMER_DELAY, new TimerListener());
public Animation() {
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.fillRect(50, (int) dY, 50, 50);
// timer.start();
}
private class TimerListener implements ActionListener {
private long prevTime;
#Override
public void actionPerformed(ActionEvent e) {
if (prevTime == 0L) {
repaint();
prevTime = System.currentTimeMillis();
} else {
long currentTime = System.currentTimeMillis();
long deltaTime = currentTime - prevTime;
double deltaY = Y_VELOCITY * deltaTime;
dY += deltaY;
prevTime = currentTime;
repaint();
}
}
}
public static void main(String[] args) {
Animation drawPanel = new Animation();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setVisible(true);
frame.add(drawPanel);
}
}

Related

Java JFrame render lagging

I'm trying to make a simple game in java using the swing engine. However currently I am having issues with lag. This simple code draws a circle and moves it from the top left corner of the frame to the bottom right corner, but it lags a lot. While my pc is somewhat old, i3 4gb ram, I think it should manage to perform this without lag?
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
public class Game extends JPanel implements ActionListener {
public Timer timer;
int x = 0;
int y = 0;
public Game() {
timer = new Timer(20, this);
timer.start();
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.fillOval(x, y, 15, 15);
}
public void actionPerformed(ActionEvent e) {
x++;
y++;
repaint();
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("test");
Game game = new Game();
frame.add(game);
frame.setSize(600, 600);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

drawString on large JPanel turns messy on Linux but not on Windows

I've experienced a problem on the Graphics drawString method. Briefly, I observe unexpected behaviour of drawString on Linux where the string goes messy if the panel size is large.
As an example. I have modified the ScrollDemo2 class from Oracle to draw strings on the drawingPane:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollDemo2 extends JPanel
{
private Dimension area;
private JPanel drawingPane;
public ScrollDemo2() {
super(new BorderLayout());
area = new Dimension(100000,1000);
drawingPane = new DrawingPane();
drawingPane.setBackground(Color.white);
drawingPane.setPreferredSize(area);
drawingPane.revalidate();
drawingPane.repaint();
JScrollPane scroller = new JScrollPane(drawingPane);
scroller.setPreferredSize(new Dimension(500,500));
add(scroller, BorderLayout.CENTER);
}
public class DrawingPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// drawString gives correct results in Windows but gives messy strings in Linux; drawLine gives correct lines in both Windows and Linux
for (int i = 0; i < 100000; i += 100)
g.drawString("Mark " + i, i, 20);
for (int i = 0; i < 100000; i += 100)
g.drawLine(i, i % 1000 / 10 + 25, i, i % 1000 / 10 + 35);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("ScrollDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ScrollDemo2();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Program runs on Windows 7
The program displays correct strings on Windows 7.
Program runs on Linux 14.04
The program displays messy strings on Linux.
A drawLine method is added in addition to the drawString method, and the drawLine method works properly in both Windows and Linux.
Why is it? How should I rewrite the code to solve the problem on Linux?
As noted in comments, if drawing a static (stable unchanging background image) use a BufferedImage. For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollDemo2B extends JPanel
{
private static final int IMG_W = 100000;
private static final int IMG_H = 1000;
// private Dimension area;
private JPanel drawingPane;
private BufferedImage img = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_ARGB);
public ScrollDemo2B() {
super(new BorderLayout());
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.BLACK);
for (int i = 0; i < 100000; i += 100) {
g2.drawString("Mark " + i, i, 20);
}
for (int i = 0; i < 100000; i += 100) {
g2.drawLine(i, i % 1000 / 10 + 25, i, i % 1000 / 10 + 35);
}
g2.dispose();
drawingPane = new DrawingPane();
drawingPane.setBackground(Color.white);
drawingPane.revalidate();
drawingPane.repaint();
JScrollPane scroller = new JScrollPane(drawingPane);
scroller.setPreferredSize(new Dimension(500,500));
add(scroller, BorderLayout.CENTER);
}
public class DrawingPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
int w = img.getWidth();
int h = img.getHeight();
return new Dimension(w, h);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("ScrollDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ScrollDemo2B();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Then if you have mobile sprites or dynamic images, you would draw them within the paintComponent method.

How can I close a window which is using a for loop and thread.sleep

For a school project I have to create a simple canvas on which I have to draw random circles. For this I am using a for loop where I kept spawning circles. To keep my PC from crashing, I used the Thread.sleep(20) method. It worked well but had one flaw: it is unable to close itself using the close button.
public class CanvasDraw extends Canvas {
private Random rand = new Random();
public void paint(Graphics graph){
setBackground(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
for(int i = 0; i<9999; i++){
graph.setColor(new Color(rand.nextInt(255),
rand.nextInt(255), rand.nextInt(255)));
graph.fillOval(rand.nextInt(480), rand.nextInt(480), 120, 120);
}
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
CanvasDraw drawing = new CanvasDraw();
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.getContentPane().add(drawing);
}
}
How can I fix this?
In your code you paint 10000 ovals each time paint is invoked (which can be quite often). This will clog the event dispatch thread. Paint your ovals on a BufferedImage in your main, and paint that instead. Also extend JComponent instead of Canvas and overwrite paintComponent instead of paint.
Here you go:
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CircleDraw {
public static void main(String[] args) throws InterruptedException {
Random rand = new Random();
BufferedImage bufferedImage = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferedImage.getGraphics();
g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
g.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
for (int i = 0; i < 9999; i++) {
g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
g.fillOval(rand.nextInt(480), rand.nextInt(480), 120, 120);
}
JComponent component = new JComponent() {
#Override
protected void paintComponent(Graphics g) {
g.drawImage(bufferedImage, 0, 0, null);
}
};
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(component);
frame.setSize(bufferedImage.getWidth(), bufferedImage.getHeight());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Try running your spawning circles loop to run on separate thread.
Don't run it on MAIN thread!!
I created a simple circle animation using Java Swing. Here's what the GUI looks like.
The circle repaints every second with a different color and a different circle radius. The drawing area is 420 x 420 pixels. The color varies randomly. The circle radius varies randomly from 42 pixels to 140 pixels.
The JFrame is created in the run method of the CircleAnimation class.
The drawing panel Is created in the DrawingPanel class. All of the custom painting happens in the paintComponent method of the DrawingPanel class.
The animation is set up as a java.swing.Timer after the JFrame is created. The Animation class holds the ActionListener for the Timer. The actionPerformed method creates a new random color and circle radius.
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.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class CircleAnimation implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleAnimation());
}
#Override
public void run() {
JFrame frame = new JFrame("Circle Animation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel drawingPanel = new DrawingPanel();
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Timer timer = new Timer(1000, new Animation(drawingPanel));
timer.setInitialDelay(1000);
timer.start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int radius;
private Color color;
private Random random;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(420, 420));
this.random = new Random();
setRandomColorAndRadius();
}
public void setRandomColorAndRadius() {
this.color = new Color(getRandom(0, 255),
getRandom(0, 255), getRandom(0, 255));
int start = getPreferredSize().width / 10;
int limit = getPreferredSize().width / 3;
this.radius = getRandom(start, limit);
}
private int getRandom(int start, int limit) {
return random.nextInt(limit - start) + start;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() / 2 - radius;
int y = getHeight() / 2 - radius;
int width = radius + radius;
int height = width;
g.setColor(color);
g.fillOval(x, y, width, height);
}
}
public class Animation implements ActionListener {
private DrawingPanel drawingPanel;
public Animation(DrawingPanel drawingPanel) {
this.drawingPanel = drawingPanel;
}
#Override
public void actionPerformed(ActionEvent event) {
drawingPanel.setRandomColorAndRadius();
drawingPanel.repaint();
}
}
}

Keyboard input with KeyAdapter

I'm trying to make a simple game in java where you can move the player (Defender) in the 4 directions. I tried to make the key detecting with a key adapter, but it doesn't work. What could be the problem (I tried to do a System.out.println at the key press to make sure that the problem isn't at the Defender)?
Code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class DefenderComponent extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private static final int WIDTH = 160;
private static final int HEIGHT = 120;
private static final int SCALE = 4;
Defender player = new Defender();
public DefenderComponent() {
Dimension size = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
setMinimumSize(size);
setMaximumSize(size);
setPreferredSize(size);
addKeyListener(new TKeyListener());
Timer timer = new Timer(5, this);
timer.start();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Test2");
frame.add(new DefenderComponent());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setFocusable(true);
new DefenderComponent();
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
Image i = player.getImage();
g2d.drawImage(i, player.getX(), player.getY(), i.getWidth(this) * SCALE, i.getHeight(this) * SCALE, this);
}
public void actionPerformed(ActionEvent e) {
player.move();
repaint();
}
}
KeyEvents are only generated for the component that has focus. A JPanel is not focusable by default.
Don't use a KeyListener. Instead you should be using Key Bindings which are more flexible.
See Motion Using the Keyboard for more information and examples.
frame.addActionListener(this);
is what you missed.
that line says. this class is an ActionListener. please call this class when you receive an action.
if you want to add the ActionListener to the JPanel
public DefenderComponent() {
addActionListener(this);
....
}

Using java timer in JFrame

when handset button pressed I want to green button color change red.After a certain time,again
I want to green button.I dont use timer class.Please help me.
For example my code
if (e.getSource() == handsetbutton) {
text1.setBackground(Color.RED);
// What l have to add here?
}
Run this example. I use a Swing Timer and set the delay to 4 seconds. When the button is pressed, it fires an action to change the color and start the timer. When the 4 seconds is up, the color changes back
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 javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class GreenToRed extends JPanel {
private Color color = Color.RED; // global color object
static JButton button = new JButton("Change");
private Timer timer = null;
public GreenToRed(){
timer = new Timer(4000, new ActionListener(){ // Timer 4 seconds
public void actionPerformed(ActionEvent e) {
color = Color.RED; // after 4 seconds change back to red
repaint();
}
});
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
color = Color.GREEN; // change to green
repaint(); // repaint
timer.start(); // start timer
}
});
}
private static void createAndShowGui() {
JFrame frame = new JFrame();
frame.add(new GreenToRed(), BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval(getWidth() / 2 - 50, getHeight() / 2 - 50, 100, 100);
}
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Categories