I have a paintComponent method, inside a class.It makes a grid of 10*10.
And I want to lower the frame rate, so that every time the function colors a rectangle in the grid, I can see the progression
public void paint(Graphics g1) {
super.paint(g1);
Graphics2D g= (Graphics2D) g1;
for(Object a: Maze_Generator.list) {
Cell c =(Cell)a;
if(c.top())
g.drawLine(c.x(), c.y(), c.x()+c.length(), c.y());
if(c.bottom())
g.drawLine(c.x(), c.y()+c.length(),c.x()+c.length(),c.y()+c.length());
if(c.left())
g.drawLine(c.x(), c.y(), c.x(), c.y()+c.length());
if(c.right())
g.drawLine(c.x()+c.length(), c.y(), c.x()+c.length(), c.y()+c.length());
// I wish to delay the following code by a second, so that I can see as the square gets coloured one by one.
if(c.visited()) {
g.setColor(Color.cyan);
g.fillRect(c.x()+1, c.y()+1, c.length()-1, c.length()-1);
g.setColor(Color.black);
}
}
I tried using Thread.sleep(), but for some reasons the App freezes, and UI crashes(I only see JFrame, with white background,and it does not close)
But the program still runs in Background
try{Thread.sleep(2000);}catch(Exception e){ e.printStackTrace();}
if(c.visited()) {
g.setColor(Color.cyan);
g.fillRect(c.x()+1, c.y()+1, c.length()-1, c.length()-1);
g.setColor(Color.black);
Any Suggestions?
The problem is that when you use the Thread.sleep() method, it stops the thread's work, and with that freezes your program.
The solution involves asynchronous programming. In java we can create another thread to preform the task that takes time, and that we want to use Thread.sleep() in.
With the release of lambda expressions in Java 8, we can use this syntax:
Thread newThread = new Thread(() -> {
// Code that you want to perform asynchronously
});
newThread.start();
I think your program should have two treads:
GUI thread (Used for rendering GUI)
Logic thread (Used for making some logical judgement)
GUI thread will have a updateGUI method for passing message to GUI thraed and rendering based on the passed message.
please take a look following answers:
How to pass the message from working thread to GUI in java
Refreshing GUI by another thread in java (swing)
Related
So I was experimenting with javaFX a bit since it is a part of one of my subjects.
I have a catapult that shoots a ball in certain direction. I was wondering if you could somehow change the color of a canvas background in a runtime on a certain occasion - when a ball hits the wall for example.
I already figured out how to make the ball bounce of the wall, but I cannot figure out how to change the bg color in runtime.
I'm using import javafx.scene.canvas.GraphicsContext; as it is what we kinda "have" to work with.
I thought something like this would work, I found some threads about sleep so I gave it a try.
public void flashCanvas() {
try
{
gc.setFill(Color.WHITESMOKE);
Thread.sleep(100);
gc.setFill(Color.BLACK);
Thread.sleep(100);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
I thought that this would just change the color, then wait and over again.
Thanks for help!
Short Answer
You can make your desired behaviour using this part of code
Runnable collide = new Runnable() {
#Override
public void run() {
Platform.runLater(() ->
damageEffect()
);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() ->
resetEffect()
);
}
};
new Thread(collide).start();
Explanation
you can't use Thread.Sleep() on the main thread that will cause your UI to freeze so you need to make it on another thread and for more safety when changing anything in the UI/Main thread you should use Platform.runLater()
you can write whatever the effect in these function boomEffect() and resetEffect()
if you want to change the canvas fill color you can use the code provided by #Barudar in the comment like this
private void resetEffect() {
canvas.getGraphicsContext2D().setFill(Color.RED);
canvas.getGraphicsContext2D().fillRect(0,0,canvas.getWidth(),canvas.getHeight());
}
where fillRect() take startingX , startingY , endX , endY as a parameters
You're blocking the JavaFX application thread with Thread.sleep which prevents the change from being rendered. (In your code there actually is no change, since you're simply replacing the color used to draw, assuming the color is not used anywhere else, but this is what would happen, if you did draw something in addition to modifying the fill property.)
I recommend using one of the classes from the javafx.animation package to change this.
In this case a PauseTransition seems the simplest way to go, but if you want to include a update loop, I recommend going with AnimationTimer or Timeline instead and integrating this kind of logic into the update "loop". Using one of the latter possiblities you could simply save the last time the ball hit a wall and decide based on this info which color to use to draw the background.
Example using PauseTransition:
gc.setFill(Color.WHITESMOKE);
gc.fillRect(x, y, w, h);
// schedule update without blocking
PauseTransition pause = new PauseTransition(Duration.millis(100));
pause.setOnFinished(evt -> {
gc.setFill(Color.BLACK);
gc.fillRect(x, y, w, h);
});
pause.play();
Note that you'll probably need to redraw more than just the background, since using fillRect replaces all the content currently in the rectangle.
I am trying to program a Battleship game with different states depending on user input. I am implementing the State Design Pattern for the different types of game types, the instructions panel and other features it is going to have. My question is, when I make the JFrame for my game, should the logic be invoked during construction, or should it be during the update of the window?
For example:
public class BattleshipGui extends JFrame
{
public BattleshipGui()
{
initizalizeFrame();
executeLogic();
}
}
The execute logic contains the game loop that changes according to the game events.
The GUI should only be responsible for rendering. All actual game logic should be handled on your main thread, while the EDT is painting for you (repaint posts events to the EDT). As for "invokingGameLogic", this should first never be done in the constructor (considering that method contains the loop thats updating your game). You should initFrame then start your loop dedicated to updating/rendering your game. Create an interface between updating and rendering; have variables that both rendering code and updating code can access. That way, your render code renders whatever the current state is, while update code changes it.
class Game extends Canvas {
//the items used between render and update
Player player;
Enemy player;
//render values
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.BLUE);
g.fillRect(player.x, player.y, 50, 50);
g.setColor(Color.RED);
g.fillRect(enemy.x, enemy.y, 50, 50);
}
//affect values
public void update() {
player.x++;
}
}
Keep your game logic away from your rendering. It makes programming a game easier, since you'll be able to easily find code that handles graphics, and what code handles moving players and what not.
If you are using Component#paint(Graphics), then you REALLY want to seperate the game logic from rendering. Code within the paint method executes om the Event Dispatch Thread, meaning issues with updating will cause very apparent issues in GUI.
I'm currently in the process of making a 2D turn based strategy game in Java. At this stage in development I am testing out different varieties of AIs.
The AIs interact with the game by sending a x and y variable to a function called gameLoop, which would would normally be called by a mouseClicked function. This allows them to directly emulate a human user which has made having them interact with the game much easier. Below is an overview for some of my code:
paintComponent() {
drawTiles();
drawUnits();
if(unitSelected && gameState == DISP_MOVE)
drawMoveSpots();
}
gameLoop(int x, int y) {
// various logic based
// on the values of x and y
// logic may set unitSelected to true or false
// may also change gameState bteween normal and displayMoveSpots
repaint();
}
My issue right now stems from the fact that the AI is executing code faster than repaint happens. When this happens, I start getting errors such as NullPointerException's and ConcurrentModificationException's in my various drawing functions because the logic inside of gameLoop is changing variables faster than repaint can happen. If I add in a manual delay to how often the AI calls gameLoop or only have 2 humans playing, the errors disappear.
From my research and reading of the Java docs, I understand that repaint returns immediately and all the graphics calls are put onto the EventQueue until Java decides that there are enough calls to warrant an actual repaint. The only solution I have been able to find so far is putting the logic code that happens in gameLoop into a anonymous runnable class and call the code using SwingUtilities.invokeAndWait, but this is not a valid solution for me, because this causes actual human interaction from clicking to bug out. I have considered using a lock to provide mutual exclusion to gameLoop's logic section/repaint (so only one can execute at a time), but I'm not sure if this is a good practice when dealing with Java graphics.
At this point I'm open to any suggestions on what I can do to alleviate my problem.
Thanks.
My advise is that you should use a swing timer
import javax.swing.Timer;
public static void main(String[] args) {
/*this is 1 sec/ 1000ms*/
Timer aiTimer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//move AI
//repaint
}
});
aiTimer.start();
}
By doing that you will not repaint so often. Hope that helps!
Right now I have a main game loop that constantly redraws the screen. Since I need to slow this thread down but continue to draw other items at a faster rate I need to make a new thread. The problem is I am not sure how to go about making a new thread that also draws to the screen I know how to make a new thread, I am just stuck on how to implement the Graphics2D drawing in the new thread. For example I have the code below which is the typical starting point and then there is the draw method defined in the other class that directs what and when to draw. If I wanted to branch off and have another thread drawing and doing its own thing how do I do that?
Do I have to make a new class that creates an entirely new PaintComponent()? Or would I simply create a new Graphics2D object so I can use different font colors and such? I guess what confuses me most is that I can't just call a different draw method because I still need to pass g2d as the argument. So it appears to me that I need to make the thread from within another method that already has the g2d object.
If this is confusing I do apologize as I am still a beginner to JAVA. If you need more information just let me know. Thank you in advance.
public abstract void draw(Graphics2D g2d);
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
draw(g2d);
}
In the first place, Swing is inherently single-threaded. This was once summarized in the "single thread rule"
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
(Unfortunately, the respective site did not survive the migration of Java from Sun to Oracle, but some information can be found here http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html or when doing a websearch for "swing single thread rule")
In general, this applies also to painting : The paintComponent may only safely be called by the Event Dispatch Thread (EDT). And it will be called "automatically". That is why this technique is called "passive rendering": You overwrite the paintComponent method, expecting that it will be called by the EDT.
However, particularly for game development, you can use a technique called "active rendering". In this case, painting is slightly more complicated and involves setting up an own BufferStrategy. The potential advantage is that any thread may perform rendering operations in this case, because you can obtain a Graphics object by calling BufferStrategy#getDrawGraphics.
Information can be found at http://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html (while this refers to fullscreen rendering, similar concepts can be applied to active rendering in a window, but I'd recommend to consult further tutorials/resources that can be found with keywords like "swing active rendering").
EDIT: solved, look below for my solution.
first of all, this is my very first question here, so if I make any mistakes, please tell me.
I am trying to write a Mandelbrot Fractal Program in Java, for training purposes. The ideal for all the functionality I want to have is the Fractalizer (http://www.fractalizer.de/en/), but for now, I will be happy with a program that draws the Mandelbrot Set on the screen (instead of, for example, writing it to an image file). Of course, I want the program to be fast, so I thought that I could split the calculation into multiple threads to utilize my multi-core processor; for example, on a quad-core system, the image would be divided into 2x2=4 images, each to be calculated by a separate thread. All these threads get a Graphics object passed on which they draw the pixels as they are calculated.
My first attempt to get this working was to have the threads draw on a BufferedImage.getGraphics() and to have the paint() method constantly call repaint() as long as the image isn't finished:
g.drawImage(tempImg, 0, 0, null);
if (waiterThread.isAlive())
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// do nothing
}
repaint(10);
}
(waiterThread joins all calculating threads one after another, so as long as the waiterThread is alive, at least one calculating thread is not yet finished.)
This works, but causes ugly flickering on the canvas because of the frequent repainting.
Then, by means of a small test program, I found out that Graphics.draw*anything* draws on the screen instantly, before the paint method returns, so my current approach is the following:
One Panel with a GridLayout that contains 2x2 (on a <4-core system, 1x1) MandelbrotCanvas objects
Each MandelbrotCanvas object will, on the first paint() call, initialize a calculating Thread, pass its own Graphics object to it (actually, I'm using a custom GroupGraphics class that passes one Graphics call to several graphics, to "backup" the image into a BufferedImage.getGraphics(), but that's not important), and start the calculating thread.
The panel will in its paint() method fetch the calculating threads from each of the MandelbrotCanvases and join() them.
Unfortunately, this creates only a black screen. Only when calculation is finished, the image is displayed.
What is the right way to have several threads paint onto one component?
EDIT:
What I didn't know: Only the Event Dispatch Thread is allowed to paint on AWT components (roughly spoken), which means that the last approach above can't possibly work - apparently, it's supposed to throw an exception, but I didn't get one. My solution is to use the first approach - draw the image onto a BufferedImage and draw that onto the Canvas - with the only modification that I overload the update() method to call the paint() method without clearing the painting area:
public void update(Graphics g)
{
paint(g);
}
So I guess my answer to the general question ("How do I let multiple Threads paint onto an AWT component?") would be: You can't, it's not allowed. Let the Threads draw onto a BufferedImage.getGraphics(), and draw that image repeatedly. Overload the update() method like above to avoid flickering. (It looks really great now.) Another tip that I can't use in my case, but is still good, is that there is a variant of repaint() that takes rectangle arguments to specify the area that has to be redrawn, and a variant that takes a time argument (in milliseconds), so the repaint doesn't have to happen immediately.
EDIT2: This link provides very helpful information: http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Only the GUI thread can paint directly on your component.
So you must call the repaint method.
When you have background computation, to force a fast drawing, you should use the version taking a time as parameter.
Some details from here :
NOTE: If multiple calls to repaint() occur on a component before the
initial repaint request is processed, the multiple requests may be
collapsed into a single call to update(). The algorithm for
determining when multiple requests should be collapsed is
implementation-dependent. If multiple requests are collapsed, the
resulting update rectangle will be equal to the union of the
rectangles contained in the collapsed requests.
You have to send requests to the EDT.
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Rectangle r = myCurrentWorkingThread.getFinishedRectangle();
myPainter.repaint(r);
}
});
The idea is that you won't repaint pixel by pixel, rather giving larger chunks to the worker-threads. As soon as they're finished with a unit of work, they notify the main object (myPainter) that would do the actual work. This construct (EventQueue.invokeLater) will guarantee that it will be on the Event Dispatcher Thread.