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.
Related
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 learning how to make an Applet using Java in the form of a game. In the game, I have a character sprite drawn at the center and moves when the player presses w a s d.
It goes like this:
public game extends applet implements KeyListener {
int x, y;
URL url;
Image image;
public void init() {
x = getSize().width/2;
y = getSize().height/2;
url = new URL(getCodeBase());
image = getImage(url, "player.gif"); //take note that this is a still image
addKeyListener(this);
}
public void paint(Graphics g) {
g.drawImage(image, x, y, 32, 32, this); //the size of the image is 32x32
}
public void KeyPressed(arg0) {
char c = arg0.getKeyChar();
switch(c) {
case 'w':
y -= 10;
break;
/*And so on. You guys know how it works.*/
}
repaint();
}
My problem is, the character sprite seems dull when the user doesn't press anything. What I want to do is to make the Image an array of images and put a simple image animation by looping the array in paint like so:
public void paint(Graphics g) {
for(int i = 0; ; i++) {
g.drawImage(image[i], x, y, 32, 32, this);
if(i == image.size() - 1) { i = 0;}
}
}
However, if I do this, I won't be able to get anymore KeyEvents that would activate when the user wants to move. My question is this: How will I make it so that my character does an animation when the program is "idle" (i.e. the user isn't pressing anything) while still maintaining the capability to take in KeyEvents (e.g. moving when the player types in w, a, s, or d, and then continuing the idle animation after repainting)?
Thanks in advance.
PS. I'm still quite a beginner in Java so sorry if my code is not very nice. Any advice is welcome.
You need to make your application multithreaded so that the painting runs in a separate thread.
Otherwise you will have the painting blocked while waiting for the next key.
This is not a trivial change to your code though.
Perhaps you would give yourself a new class, say InterestingCharacter, which can cycle through any of N states (corresponding to your N images). You clearly can't let any paint method run infinitely, but if your InterestingCharacter could render itself in its current state you might be onto something with that. Maybe it will be enough that this InterestingCharacter knows what state it is in and then some other object manages the rendering. Would it be helpful if the InterestingCharacter could tell you that its state has changed and so needs to be rendered again? If so, you could implement the Observer pattern such that the character is observed and your game an observer.
I think the trick will be to break the problem down into a few classes that have appropriate responsibilities--ideally a class should have one responsibility and each of its methods should do one thing.
Just some ideas to help you move forward. Experiment with it and see how it goes. Hope it helps!
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.
I think I have figured out where my problem is coming from. when I comment out repaint() I get what I would normally get. I screen with a racetrack on it that I created using rectangles along with 2 cars. Obviously nothing looks like it moves because I commented out repaint(). If I run it as is my program does one of two things. It either doesn't work or it blinks about 3 to 5 times and then stops. if I comment out the sleep() the program just keeps blinking till I exit. The cars move like they are supposed to. I don't know what I'm doing wrong. I can also include the code for the actually track if you would like. It is just way to long and I think I narrowed it down to this.
private class MoveTwo extends Thread{
public void run(){
//infinite loop
while(true){
try{
repaint();//Refresh Screen
if(playerTwoSpeed<=5) playerTwoSpeed+=.2;//slow acceleration
playerTwo.y-=playerTwoSpeed;
Thread.sleep(100);//Delay
} catch(Exception e){
break;//Stop if there is an error
}
}
}
}
Here you go:
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, WIDTH, HEIGHT);
//Turn Border green when we draw.
g.setColor(Color.GREEN);
//fill rectangles.
g.fillRect(left.x, left.y, left.width, left.height);
g.fillRect(right.x, right.y, right.width, right.height);
g.fillRect(top.x, top.y, top.width, top.height);
g.fillRect(bottom.x, bottom.y, bottom.width, bottom.height);
g.fillRect(center.x, center.y, center.width, center.height);
g.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
g.fillRect(obstacle2.x, obstacle2.y, obstacle2.width, obstacle2.height);
g.fillRect(obstacle3.x, obstacle3.y, obstacle3.width, obstacle3.height);
g.fillRect(obstacle4.x, obstacle4.y, obstacle4.width, obstacle4.height);
g.fillRect(obstacle5.x, obstacle5.y, obstacle5.width, obstacle5.height);
g.setColor(Color.WHITE);//Change color to white.
g.fillRect(outerStart.x, outerStart.y, outerStart.width, outerStart.height);
g.fillRect(innerStart.x, innerStart.y, innerStart.width, innerStart.height);
g.setColor(Color.YELLOW);
g.fillRect(finish.x, finish.y, finish.width, finish.height);
//Player one is blue
g.setColor(Color.BLUE);
g.fill3DRect(playerOne.x, playerOne.y, playerOne.width, playerOne.height, true);
g.setColor(Color.RED);
g.fill3DRect(playerTwo.x, playerTwo.y, playerTwo.width, playerTwo.height, true);
}
You probably use an override of a paint method from a JFrame subclass. This leads to blinks on many OS as JFrame are heavy components. The solution is to use an override of a JComponent (typically a JPanel)'s paintComponent method. The code would be the same but would integrate much more smoothly in Swing framework.
Also, you should consider using a better control over your thread, a while truc loop is...difficult to stop. :)
You should use a boolean flag and a method to set it to true or false.
Regards,
Stéphane
You should use double-buffering in order to get smooth animations.
JComponent.setDoubleBuffered(boolean flag);
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.