I am working on a school project about multiple balls bouncing. So far, I managed to create the app and everything works ok. But, I also need to implement multi-threading in the app, and this is where I am stuck. I was thinking of one ball one thread, but I am not sure of how to implement it. Here is my code so far (part):
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//The balls are painted only after the timer is started
if(bTimer)
{
for(Ball ball:ballList.ballsArrayList)
{
Thread ballThread = new Thread(ball);
ballThread.start();
ball.draw(g);
/*other code for moving the ball*/
}
}
}
In the class Ball:
public void draw(Graphics g) {
Color color = new Color(this.getColorR(),this.getColorG(),this.getColorB());
g.setColor(color);
int radius = this.getsize();
g.fillOval((int)(this.getX() - radius), (int)(this.getY() - radius), (int)(2 *
radius), (int)(2 * radius));
}
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 200; i++) {
//ball.draw(g); ??
try {
Thread.sleep(50);
System.out.println("Sleeping");
} catch (Exception ex) {}
}
}
I was thinking that I could put the ball.draw() function in the run() function for the thread. But I don't know how I can do that or if it's a good idea. Multi-threading is still difficult for me to understand and implement =((
Not a real answer, but too long to put in a comment.
You should note that Swing is not thread-safe. All Swing components should be accessed on the Event Dispatch Thread, and on that thread only. See the Concurrency in Swing guide for more information.
This means that you can have one thread per ball which updates the position of the ball. However, if you access the position of the ball during the painting, this access happens on the EDT. Meaning that you cannot update the position of the ball in your background thread at any moment. You will have to implement some locking or simply update the position on the EDT.
I am not sure what you try to achieve, but if you simply want to update the position of a ball at certain time intervals I would opt for a javax.swing.Timer. This timer is triggered on the EDT, allowing you to update the position in a thread-safe manner. The Swing wiki tag contains some more links for implementing animation in Swing.
All swing code has to run on the event dispatching thread. Therefore what you're doing in the code snippets is bad.
However, if calculating ball positions is cpu intensive and requires time, you do want to move the logic in a separate thread, otherwise your UI will become unresponsive.
This would become a typical producer/consumer problem: one thread produces cooridnates and the event dispatch thread consumes them by drawing the balls.
Related
I am learning Java and I've tried to build an app that drops a ball when I click on the panel. The problem is that when the oval is painted its moving so fast that even setting Thread.sleep to max value just makes it barely noticable. How can I slow it down?
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.red);
g2.drawOval(x,y,20,20);
Thread thread = new Thread() {
public void run() {
while (true) {
y = y + 1;
repaint();
try {
Thread.sleep(2147483647);
}
catch (InterruptedException ex) {
}
}
}
};
thread.start();
}
I have not tried your program in my own environment, but from what I know, it seems like what is moving your ball is the:
y = y + 1;
line, therefore you could probably consider changing that to a smaller number, most likely a double. Also, as was already mentioned, maybe you can try not using the:
while (true)
statement, as that will just always and forever evaluate to true and it's not the biggest issue, but maybe you can think of using something else like using something that has to do with the y variable like: while (y < 768 ) or even something like a for loop depending on what it is you're doing.
Hopefully this helps, and I would also advise you take a look at this answer here:
Java Graphics Updating Too Fast
Wishing you the best!
Get rid of creating the thread in the paintComponent method.
You need to use the java Swing Timer with an actonListener to
increment the x and y coordinates and then call repaint(). Check the Java API for details.
When you start the program, use SwingUtilities.invokeLater(()->new className()) to invoke the class. This schedules the app in queue for processing in the Event Dispatch Thread (EDT). All events and repainting need to be handled in the EDT.
In your paintComponent method, set anti-aliasing using Graphics2D via setRenderingHints. It is also explained in the Java API. It will make your graphics appear smoother by averaging the edges.
So I was messing with Swing for the first time in a while and I came across a strange issue. So I am adding "shapes" to a list every so often, and then in the paintComponent method of a JPanel I am looping through the list and drawing the shapes. I also draw a shape outside of the for loop for testing purposes.
What happens is the shapes in the for loop will jump around the screen randomly. This only happens when the shapes are drawn in this loop.
What I have tried already:
Updating graphics drivers for both the integrated GPU and discrete GPU
Using java.util.Timer instead of Swing Timer
Using Thread/Runnable
Using things other than ArrayList, such as LinkedList, Vector, and a normal Array.
Trimmed literally everything out of my code except the basics, which is what we're left with here. I was drawing more complex things before when I noticed it.
Changed the timing (PERIOD variable, in millis). It will get worse if I increase or decrease it.
Changed from using System time in milliseconds to the System time in nanoseconds, converted to milliseconds. I know this should be the same but I was running out of ideas.
Here is a gif of the problem (15 seconds long):
image
You'll notice that the small squares will jump around at random intervals. This should not occur. I'm just trying to "spawn" a square at random coords.
Here is the code in a pastebin:
code
I have included all 3 classes in this order: the JPanel class, the Main class (extends JFrame), and the shape class
If any of the links don't work, inform me and I will promptly post other links.
Thanks.
This setup ...
#Override
public final void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (this.startTime == -1L) {
this.startTime = Main.timeMillis();
}
final long timeDiff = Main.timeMillis() - this.startTime;
if (this.circlesIndex <= 19 && timeDiff > 2000) {
final int randX = this.rand.nextInt(this.WIDTH);
final int randY = this.rand.nextInt(this.HEIGHT);
this.testShapes.add(new TestShape(randX, randY));
this.startTime = Main.timeMillis();
}
for (TestShape ts : this.testShapes) {
ts.draw(g);
}
g2.setColor(Color.gray);
g2.fill3DRect(350, 400, 100, 60, true);
}
#Override
public final void actionPerformed(final ActionEvent event) {
x++;
if (x > WIDTH) {
x = -50;
}
repaint();
}
is wrong.
Paint is for painting - you should not modify the state of the UI from inside any paint method, do this within your ActionListener. The problem is, your component can be painted for any number of reasons, many of which you don't control or know about
I'm writing a tank game . I want to have a method called shoot that when I press Space the tank have to shoot . my problem is that when the program calls this method it goes through the while loop and after that it prints the end location of the ball . I need to implement something in the while loop that every time it calculates dx and dy it goes to the paint method and paint the new location of the ball. I tried adding paintImmediately() but it throws stackoverflow error. thanks for helping me.
actually I'm changing dx and dy and I want the paint method to draw the ball at that place...
public void shoot(Image img, double fromx, double fromy, double ydestination, int speed) {
int time = 0;
double speedy, speedx;
while (dy!=ydestination) {
time++;
speedy = speed * Math.sin(Math.toRadians(angle));
speedx = speed * Math.cos(Math.toRadians(angle));
dy = (int) ((-5) * time * time + speedy * time + fromy);
dx = (int) (speedx * time + fromx);
// paintImmediately((int)dx,(int) dy, 10, 10);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
and here is my overrided paint method the last line is for the bullet that is my question :
#Override
public void paint(Graphics g) {
System.out.println("paint");
super.paint(g);
render(bufferedGraphics);
g.drawImage(bufferedScreen, 0, 0, null);
// System.out.println(x1);
BufferedImage buff = rotateImage(mile1, angle);
BufferedImage buf = rotateImage(mile2, angle);
g.drawImage(buff, mx1 - 40, my1, null);
g.drawImage(buf, mx2 , my2, null);
g.drawImage(bullet, (int) dx, (int) dy, null);
//setVisible(true);
}
You are using the wrong approach. You are tying 3 events together: user input (click to shoot), game state update (bullet moves) and draw refresh rate (paint).
In general trying to make these work at the same speed is a nightmare and you'll never achieve it. The most common, easy and robust approach is to have an event loop. User input events trigger changes to the game state, the game state is updated periodically either by turns or by some elapsed time (and state update will depend on how much time has elapsed), the state is drawn every time it is needed, which is periodically but also for some other events like minimizing the windows, etc etc...
For Java, you can find a good library for this here. With a sample hello world that shows the different parts here.
P.S: Also, be very careful when manually sending threads to sleep. That might make your entire program unresponsive.
If your drawing code has sleep() in it to introduce waits, you are doing things very wrong indeed. You are trying to sleep there because you want the screen to keep on updating with new positions... but you are actually making the screen freeze completely, because there is only 1 thread drawing things in Java Swing; and if you sleep that thread, nothing gets drawn (and you can't even press keys or use the mouse).
What you should do instead is to update the position of your bullet over several calls to your paint() method. In pseudocode:
paint(Graphics g) {
// calls paint() on all objects in the game world
}
// you should call this once per frame
update(World w) {
// call update() on each game-world object
}
// in Tank
fire(Tank t, Point target) {
// creates a bullet at the tanks' position, and adds it to the World
}
// within Bullet
update() {
// moves this bullet along its path to its target;
// if target reached, add an explosion there and destroy the bullet
}
// within Explosion
update() {
// advance to next explosion frame;
// or if finished, destroy the explosion object
}
You can read more on game event loops here and here.
I have a JPanel subclass with custom paintComponent() implementation. It is being refreshed at 50fps. It is typically in the range of 500x300 pixels in size. I'm seeing some flickering (not too bad but noticeable) and I've inserted some debug code that indicates that Swing/EDT is skipping (presumably) redundant painting. I am guessing that's because the EDT is not giving enough time for paintComponent() to always finish or it is taking too much time on the EDT.
My thinking is that I need to take the code currently implementing paintComponent() (which is not very complex but not completely trivial either) and refactor it so it is executed on its own Thread (or at least not the EDT) and draws to an ImageBuffer. I then implement paintComponent on my custom JPanel and draw (render) from the ImageBuffer to the screen (actually to the buffer behind Swing components as my research into the solution led me some information about Swing being (by default) double-buffered, though I'm not completely clear on that). If it is true that the rendering from the ImageBuffer to the JPanel is faster than my implementation that constructs the ImageBuffer then I will be going in the right direction.
Is this the proper design direction for me to take?
UPDATE
I modified my implementation as discussed in reponses below:
1) Create a BufferedImage
BufferedImage myBufferedImage = new BufferedImage(mySize.width,mySize.height,BufferedImage.TYPE_INT_ARGB)
2) Create a Thread dedicated to peforming the processing to determine what is to be drawn.
3) Move the code previously in paintComponent() to another method that is executed by the dedicated Thread. At the end of this method, call repaint();
4) Create a new paintComponent() that simply calls g.drawImage(myBufferedImage,0,0,null);
5) Where I previously would call repaint(), trigger myThread to perform the drawing to myBufferedImage.
This was a disaster, as predicted. Much worse flickering and sluggishness, partial paints, etc. I believe this was due to contention reading/writing myBufferedImage (as mentioned below). So I then created a lock and lock myBufferedImage when I am writing to it (in the dedicated drawing Thread) and wait to get that lock in paintComponent() before calling Graphics2D.drawImage(); The flicker and partial paints go away - but performance is no better (maybe even worse) than when I was doing all the calculations for the drawing in paintComponent (and therefore in the EDT).
This has me stumped at this point.
If you're not updating the entire component (ie only small areas are changing), you could use JComponent#repaint(Rectangle r) indicating the areas that have changed. This will result in a repaint cycle that updates (potentially) a much smaller area.
I generated a "animated sequence" library some time ago to take a series of images and layer them ontop of each, given a "speed" of each layer, it would transpose them from right to left.
The whole sequence would cycle for 10 seconds, where a speed of 1 would take take 10 seconds to complete. Each layer is moving at difference speeds.
The original images where 1024x256, and the sequence was devised of 5 animated layers and 2 static layers...
I only wish I could show you how smooth this plays on my PC and Mac.
The only signification issue I had to over come was making sure that the images where compatible with the screen devices color model.
UPDATED
These are some utility classes I use when loading or creating BufferedImages, especially for animation. The make sure that the colour models are the same as those used by the screens, which will make them faster to update/repaint
public static BufferedImage loadCompatibleImage(URL resource) {
BufferedImage image = null;
try {
image = ImageIO.read(resource);
} catch (IOException ex) {
}
return image == null ? null : toCompatibleImage(image);
}
public static BufferedImage toCompatibleImage(BufferedImage image) {
if (image.getColorModel().equals(getGraphicsConfiguration().getColorModel())) {
return image;
}
BufferedImage compatibleImage =
getGraphicsConfiguration().createCompatibleImage(
image.getWidth(), image.getHeight(),
image.getTransparency());
Graphics g = compatibleImage.getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return compatibleImage;
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
// Check out java.awt.Transparency for valid values
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
I think this is what you're looking for on information about double buffering:
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
You could turn off double buffering with setDoubleBuffered(false) if you can't get access to the underlying buffer which I'm not entirely sure you can.
I don't think you can safely draw on an image from another thread because you'll get into the thread writing to the image while the EDT is reading that same image as it redraws. If you share an image between them you're going to have multi-threading issues that you'll have to synchronize. If you synchronize then you're performance isn't going to be very good. If you instantiate a new image every frame you're memory is going to skyrocket and GC will get you. You may be able to instantiate 10 frames and keep the writing away from the reading or something like that, but either way this is going to very tricky to make it performant and correct.
My suggestion is to do all drawing from EDT, and figure out a way to do the calculations (rendering) on another thread that doesn't involve ImageBuffer sharing.
Update While it is used for fullscreen. The suggestions in there apply to windowed mode as well: "Separate your drawing code from your rendering loop, so that you can operate fully under both full-screen exclusive and windowed modes." See this http://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html
i've has similar problems trying to paint smoothly.
try running this and see how smooth it is (its smooth for me).
profiler says most of the time is in paint component. interestingly draw image is not mentioned.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class P extends JPanel {
void init(Dimension d) {
GraphicsConfiguration gc=getGraphicsConfiguration();
bi=gc.createCompatibleImage(d.width,d.height);
}
#Override public void paintComponent(Graphics g) {
//super.paintComponent(g);
if(bi!=null)
g.drawImage(bi,0,0,null);
}
BufferedImage bi;
}
public class So13424311 {
So13424311() {
p=new P();
}
void createAndShowGUI() {
Frame f=new JFrame("so13424311");
// f.setUndecorated(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.add(p);
p.init(d);
p.setSize(d);
p.setPreferredSize(d);
f.pack();
// if(moveToSecondaryDisplay)
// moveToSecondaryDisplay(f);
f.setVisible(true);
}
void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
Timer t=new Timer(20,new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
Graphics g=p.bi.getGraphics();
Color old=g.getColor();
g.fillRect(0,0,d.width,d.height);
g.setColor(Color.red);
g.fillRect(n%(d.width/2),n%(d.height/2),20,20);
g.setColor(Color.green);
g.fillRect(n%(d.width/2)+20,n%(d.height/2),20,20);
g.setColor(Color.blue);
g.fillRect(n%(d.width/2),n%(d.height/2)+20,20,20);
g.setColor(Color.yellow);
g.fillRect(n%(d.width/2)+20,n%(d.height/2)+20,20,20);
g.setColor(old);
g.dispose();
p.repaint();
n++;
}
int n;
});
t.start();
}
public static void main(String[] args) {
new So13424311().run();
}
final P p;
Dimension d=new Dimension(500,300);
}
I've got this Thread that is supposed to Update the game's elements, i.e. repaint, wait for a bit of time then do it again, the problem is that it doesn't repaint. I've tested every other component, they all works, but the paint method is only called once ( Because the components are painted ), even if I call repaint() in the loop. Here's my Code of the Loop:
Thread t = new Thread(){
public void run()
{
mouse.init();
while(true)
{
mouse.Refresh();//Adds Dirty Regions in the RepaintManager
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//What here?
}
});
}
}
};
No need to see the thread or anything, it loops.
Here's the Paint Method :
#Override
public void paint(Graphics g)
{
EndTime = System.currentTimeMillis();//For the FPS Counter
Time = EndTime - StartTime;
FPS = (byte) (1000/Time);
TotalFPS += FPS;
TotalFrame++;
JPT.AverageFPs.setText( "" + TotalFPS/TotalFrame);
JPT.CurrentFPS.setText( "" + FPS);
StartTime = System.currentTimeMillis();
g.clearRect(0,0,dim.width,dim.width);
for(int x = 0; x < Variables.Width; x++)
{
for(int y = 0; y < Variables.Height; y++)
{
if(Variables.Map[x][y] == 0)
{
g.setColor(new Color(0x613F37));
g.drawLine(x, y, x, y);
}
else if(Variables.Map[x][y] == 1)
{
g.setColor(Color.black);
g.drawLine(x, y, x, y);
}
else if(Variables.Map[x][y] == 2)
{
g.setColor(new Color(0xDEDEDE));
g.drawLine(x, y, x, y);
}
}
}
g.setColor( new Color(0.5f, 0.5f, 0.5f, 0.5f));
g.fillRect(Variables.CurrentX, Variables.CurrentY, Variables.Zoom, Variables.Zoom);
}
Thanks alot in advance.
Also I want to point out that I made this Game as an Applet before and it was working like a charm, but now I need it as Application.
Try paintImmediately(0, 0, getWidth(), getHeight());
This is because the RepaintManager collapses multiple requests into a single repaint for members of a component tree.
I think your data is not synchronized between threads (there is not enough code to determine if this the case tough).
repaint is called on the Swing EDT and update.UpdateGravity() is called in your main thread. I guess your update changes Variable, but do you ever synchronize this data structure such that the Swing EDT sees the updated value?
EDIT:
here is what you should do:
variables = update.updateGravity(); // runs in your main thread
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
yourCustomSwingComponent.update(variables); // will run on the Swing EDT
}
}
and yourCustomSwingComponent contains the paint() method you have. There is a rule in Swing that the state of any Swing component can only be modified (or read) from the Swing EDT.
You should read about Swing and multi-threading. Actually, you must read about multi-threading to use Swing.
Not answering the question directly, but it might be very useful to have a look at some of the existing Java2D animation libraries. For example, Trident originally developed by Kirill Grouchnikov. If there is a reason not to use it, at least it could be useful to inspect the source and borrow some ideas.
Also, Swing Timer could be of some interest for implementing time based Swing related actions.