I'm relatively new to programming in Java. I'm trying to create objects (apples) falling in different locations on the x-axis. However every time a new one is added to the ArrayList the image slows down and flickers
import org.w3c.dom.ls.LSOutput;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GamePanel extends JPanel {
private final int FPS = 60;
private final int originalSize = 16;
private final int scale = 3;
private final int tileSIze = originalSize * scale;
private final int maxScreenCol = 16;
private final int maxScreenRow = 12;
private final int screenWidth = tileSIze * maxScreenCol;
private final int screenHeight = tileSIze * maxScreenRow;
private BufferedImage image;
private Thread thread;
private boolean start = true;
private ArrayList<Apple> apples;
public GamePanel() throws IOException {
setPanelCharacteristics();
image = ImageIO.read(new File("C:\\Users\\Gebruiker\\IdeaProjects\\AppleCatcher\\src\\apple.png"));
initApples();
start();
}
public void start() throws IOException {
thread = new Thread(new Runnable() {
@Override
public void run() {
double targetTime = 1000000000 / FPS;
double delta = 0;
double lastTime = System.nanoTime();
long currentTime;
while (start) {
currentTime = System.nanoTime();
delta += (currentTime - lastTime) / targetTime;
lastTime = currentTime;
if (delta >= 1) {
try {
update();
} catch (IOException e) {
throw new RuntimeException(e);
}
delta--;
repaint();
}
}
}
});
thread.start();
}
public void initApples() {
apples = new ArrayList<>();
new Thread(new Runnable() {
@Override
public void run() {
while (start) {
try {
addApples();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}).start();
}
public void setPanelCharacteristics() {
setPreferredSize(new Dimension(screenWidth, screenHeight));
setDoubleBuffered(true);
}
public void addApples() throws IOException {
Random random = new Random();
Apple apple = new Apple(screenWidth, screenHeight, image);
apple.setSize(50, 50);
int randomX = random.nextInt((screenWidth - apple.getWidth()));
apple.setLocationX(randomX);
apples.add(apple);
}
public synchronized void update() throws IOException {
Random random = new Random();
for (int i = 0; i < apples.size(); i++) {
Apple apple = apples.get(i);
if (apple != null) {
apple.setVelocity(2);
apple.animation();
if (apple.getIsRemoved()) {
apples.remove(i);
i--;
}
}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics g2D = g.create();
for (int i = 0; i < apples.size(); i++) {
Apple apple = apples.get(i);
if (apple != null) {
apple.drawImage(g2D);
}
}
g2D.dispose();
}
}
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
/**
* @author Gebruiker
*/
public class Apple {
private final BufferedImage apple;
private final int heightParent;
private final int widthParent;
private int width;
private int height;
private int yVelocity = 1;
private int x;
private int y = -height;
private boolean isRemoved = false;
public Apple(int widthParent, int heightParent, BufferedImage image) throws IOException {
this.heightParent = heightParent;
this.widthParent = widthParent;
this.apple = image;
}
// public Apple(BufferedImage apple, int heightParent, int widthParent) {
// this.apple = apple;
// this.heightParent = heightParent;
// this.widthParent = widthParent;
// }
public void setLocationX(int x) {
this.x = x;
}
public int getLocationX() {
return x;
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
}
public void setVelocity(int yVelocity) {
this.yVelocity = yVelocity;
}
public int getWidth() {
return width;
}
public void drawImage(Graphics g) {
Graphics g2D = (Graphics2D) g;
g2D.drawImage(apple.getScaledInstance(this.width, this.height, java.awt.Image.SCALE_SMOOTH), this.x, this.y, null);
}
public boolean getIsRemoved() {
return isRemoved;
}
public synchronized void animation() {
if (this.y >= this.heightParent) {
// int randomX = random.nextInt(widthParent - width);
// this.setLocationX(randomX);
this.isRemoved = true;
}
this.y = this.y + yVelocity;
}
}
I tried to change the way it is rendering but the problem persists. I don't know if there is any better management of the rendering and adding new objects.
Let's start with obvious:
ArrayListis not thread safe, you have multiple threads accessing and update the list which could cause issues.getScaledInstance(...)(and evenList#add) can be expensive calls. Unless the size of the image is changing dynamically during its life span, scale the image once when theAppleis created.If you absolutely need control over the rendering pipeline, you will need to use
BufferStrategyinstead.Before you do try that, I recommend trying to make it work within the Swing rendering pipeline, as it will simplify the process.
The following example:
Timerfor the "main loop" which triggers the model to update and schedules the paint pass.Runnable example...
Just as a side note, I made a mistake when testing the code, which added a new apple on every update pass (so one every 5 milliseconds). It peaked out at 648 entities without any slow down or issues.