I have searched the problem, but everyone seems to have problems with graphics updating too slow.
To present my situation:
I have a JFrame which I set to full screen, by using a compatible display mode.
Within the JFrame I have a couple of JPanels and JButtons...
On one of JPanels I am drawing moving objects that need to be updated.
I am updating the graphics like this : validate and repaint the JFrame, then revalidate and repaint the corresponding JPanel.
The graphics are updating too fast. (I need to mention that on the JPanel I am overriding the paintComponent method).
I have tried to use BufferStrategy on the JFrame, however this will prevent me from showing the JPanels and JButtons (have no idea why).
I also take this oppurtunity to ask some of you guys if you can give a clear distinction between paint, validate, repaint, revalidate, invalidate, etc... all the tutorials barely scratch the surface.
"The timing mechanism is simply a loop that runs for 5 minutes using System.timeCurrentMillis to count that"
That's not going to work. The loop is ultimately blocking the painting to occur until the loop is complete, if there is no delay.
I suggest you look into using a javax.swing.Timer for the animation. You can see more at How to Use Swing Timers
The basic construct of Timer is as follows
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis is the time in milliseconds to delay between "ticks" and the listener provides the actionPerformed which is called every delayInMillis milliseconds. So ultimately you do something like
Timer timer = new Timer (40, new ActionListener(){
public void actionPerformed(ActionEvent e) {
for (Ball ball: balls) {
ball.move();
repaint();
}
}
});
You can see a complete example here
Related
I have a GUI based game that is real time and uses a JPanel and JFrame. I currently have a overridden paintComponent which paints based on certain conditions in my code. For example:
protected void paintComponent(Graphics gfx)
{
super.paintComponent(gfx);
gfx.setFont(new Font("default", Font.BOLD, 18));
if (one)
{
//do something
}
else
{
// do something else
}
}
This works once depending on the condition. I have implemented a mouse listener that I'd like to use for the statements so that if someone clicks on a certain part it shows something else. I'd like to go back and run the paintComponent again so that it paints over top of the old one. Does paintComponent() already loop? If not, how can you run the method in a loop?
You can repaint by calling repaint(). You can do this in a loop by creating a timer to repaint it, or just call it when it needs to be updated (probably better if your code is simple enough)
EDIT: About when to use a timer:
The timer is not necessary. I used it in a game once because I was custom painting the entire window and there were dozens of always-changing things to paint, and calling render() every time would not make my CPU happy. For something simple, though, it is better to call repaint() only when something changes, because if nothing changed, you are wasting that much time repainting it.
As a coursework piece for school, we had to come up with a self-defined project, which I chose a game idea I was originally going to write on actionscript but decided to push for it on Java. This was initially going well, with the game code working, the sprite animations were being handled fine and everything was being maintained within a game loop. That was until i began to try and add components such as buttons and labels to it.
Currently the structure of my game is as this( GameSystem[handles loop and redrawing] -- GameMenu [Simple intro screen with start game ] <---swaps between ---> GameContainer [holds information about the game] )
My loop inside the GameSystem updates all necessary objects, repaints and then tells all the objects to repaint after.
Game System:
/**/for(int i = 0; i < UpdateList.size();i++){
/**/UpdateList.get(i).Update(delta * 1.1); // tell list the element is an object that extends an interface
/**/}
/**/Game.repaint();
/**/#Override
/**/public void paint(Graphics g) {
/**/super.paint(g);
/**/Graphics2D g2d = (Graphics2D) g;
/**/g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
/**/ RenderingHints.VALUE_ANTIALIAS_ON);
/**/for(int i = 0; i < UpdateList.size();i++){
/**/UpdateList.get(i).paint(g2d); // tell list the element is an object that extends an interface
/**/}
/**/}
This is where it starts getting iffy, because the game system repaints it's self each loop, clearing the frame of previous stuff, the buttons which I add in my gamemenu which is created inside the game system get cleared and only reappear briefly when you mouse over them.
Game Menu:
//////////////////
// Start Button //
//////////////////
Buttons = new JPanel();
Buttons.add(Start_game = new JButton("Start Game"));
this.add(Buttons);
Start_game.addActionListener(new Load());
/**/}
///////////////////
//////////////////
//Paint sprite //
//////////////////
#Override
/**/public void paintComponent(Graphics g) {
/**///super.paintComponent(g);
/**/Buttons.paintComponents(g);
/**/}
////////////////////////////////////
At this point I've tried paint, paint component, revalidate() and a variety of other stuff I've gleamed from other posts but none have helped. The only way I've had the button appear visible is if I remove the call to repaint in the game loop. However this then impacts the game as none of the objects are repainted.
TL DR: How can I Handle repainting the various objects in my update list and refreshing the Gamesystem panel without making any components such buttons disappear? Sorry if this is a noobish question but this is my actual first time working with Java as I wanted to expand outwards from actionscript, if any more info is required i will gladly add to the post, thank you for your help.
The problem is that you are trying to handle the painting of the buttons differently than the painting of your game.
Game System
{
paint loop
{
painting Game
}
}
Components //managing the painting itself through paintComponent, trying to run at same time as Game System.
{
paintComponent
}
So in effect, you are wanting to switch one to the other. You could either use paintComponent() in the GameSystem instead of paint(), which manages disposing of the graphics and repainting for you (if it is already extending a jComponent)...
or you need to start managing the buttons' painting similar to how you do it for your game, instead of relying on paintComponent()
I have looked all round, and no one seems to have an answer for me. I have a JPanel where I add/remove labels from it while it;s running. The problem is that every time I add a new label to it I have to revalidate() it so the new label shows up. Every time I revalidate(), it I get a very annoying flicker, and all my labels ghost up to the top of my window for a millisecond then return to what they were doing. The only time I call revalidate() is after I add a label; I do not change the paint method or anything only did
public void paintComponent(Graphics page)
{
super.paintComponent(page);
}
so that my page would not look bad. I could really use some help on this. I've made a asteroids game using JFrame, Jlabel and Jpanel game; it work great, but this flickering and component "ghosting quickly ghosting to top of panel" is just unbearable, and I know for a fact this is the revalidate() method.
Using labels (or generally GUI components) to represent quickly animating, dynamically created/moved objects is not practical. Its like using a car to drive from the living room to the kitchen.
The Swing components come with a lot of nice features, e.g. they can determine the space they require and a layout manager can use that to automatically place them nicely. Unfortunately for a game, you neither need all the nice stuff, nor do you want to pay the processing overhead involved with it. Matters are further complicated by swings multi-platform capabilities which mandate that each component has a peer component (the peer deals with rendering the component to the active look).
To solve this, you do not represent individual game objects as swing components. Instead you use a single swing component (usually JPanel) as a canvas to paint your game objects on to. You simply call repaint() for that JPanel periodically (e.g. 30 or 60 times a second). Of course you will override paintComponent() on the JPanel to do paint all the game objects using your own code.
Edit: To outline how you do your own rendering
You need to decide how to organize your game related objects. A very simple skeleton could look like this (getters/setters omitted):
public class GameObject {
public int x;
public int y;
public Image image;
}
In practice this class will have additional members (like a behavior that controls how the object moves, and maybe some states/counters for animation). It may also be abstract with a concrete subclass for each possible behavior (like ship, asteroid and bullet). All instances of GameObject are managed in some kind of collection, I assume you use a List.
When implementing the paintComponent it comes down to just loop through the list and render each objects's image:
public void paintComponent(Graphics g) {
List<GameObject> list = ... // however you obtain it in your game
for (GameObject gobject : list) {
g.drawImage(gobject.image, gobject.x, gobject.y, (ImageObserver) null);
}
}
More elaborate games may order the gameobjects to get control of how game objects overlap each other. But in principle its incredibly simple.
When I was having problems with filckering Swing components it turned out to be concurrency issues. Since you are doing a game, which means you are doing animation it may be the case here.
Take care to modify Swing components only from AWT-EVENT thread. Either use SwingUtilities.invokeLater or do your animation using javax.swing.Timer.
As a concrete example, Asteroids doesn't flicker at all. Here are a few reasons:
It runs on the event dispatch thread.
It uses JPanel, which is double buffered by default.
It paces the animation using javax.swing.Timer, updating the game's model in actionPerformed().
It overrides paintComponent(), but can safely omit super.paintComponent() because the action listener repaints the entire panel.
The listener calls paintImmediately() to avoid any potential delay; substitute repaint() to see if there's any difference on your platform.
Addendum: As shown in this counter-example, revalidate() does not cause flickering. Instead of replacing labels, update the label's icon, as shown here.
Have you considered reusing them - i.e. using setVisible(false) when they're not needed?
Not sure if adding and removing labels is the best way of achieving what you want for a game.
I'd personally be tempted to manage the drawing myself.
I try to optimize the drawing of my application. Currently i have implemented an animation as well as several GUI components. Some are separated exactly, some overlap each other. Currently i face a problem within the overlapping of swing components. A part of the GUI, which overlaps my animation, needs to draw a lot of Strings and - into an Jlist placed - common swing components.
In result the overlapping GUI becomes redrawn as often as the animation gets an update. I tried using quite a lot of different methods to make sure, whats drawn in front of each other. Things like GlassPane, Jlayeredpane. Unfortunately in any of these tries, the overlapping Menus paintcomponent method, which only need to become called as the user interacts with them, gets called frequently due to animation and causes a quite high cpu usage.
Ive tried to position the Menus in a lower order within the Layeredpane, i.e.:
getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);
During the paint process of the painter i tried to modify the area - i.e.:
#Override
protected void paintComponent(Graphics g) {
g2 = (Graphics2D)g;
g2.setRenderingHints(DefaultResources.getRenderQuality());
g2.clip(JMain.getInstance().getMapBounds());
...}
Well - as soon as the painter component !isOpague(); All components underneath become redrawn. Unfortunately if i do place the menus in a higher order, they as well need to become redrawn with any animation update.
Does somebody has any idea, how to avoid a permanent redraw of overlapping components with an animated component?
The only solution i have seen was using heavyweight containers. Unfortunately the relative positioning has also shown a behavior during moving purposes, which aren't appropriate.
Thanks for any kind of advice!!
Well, its pretty obvious that if you have overlapping non-opaque components all of them will be repainted on any changes in one of them unless you optimize your animation repaint calls to some specific rectangles, so there won't be any useless operations.
Let me describe how Swing works a bit more - all of the paintings you do in paint, paintComponent and other methods (which get called on each component repaint) are done onto subimages of a single image that holds a "cached" version of the whole frame interface.
Now imagine that you change something in your UI (add/remove/repaint component) - that final image (or atleast a small part of it that contains your component) must be properly updated. To do that in case your component is NOT opaque - all of sub-components will be repainted with your component bounds as a repaint rect to create a proper background for your component. If your component is opaque - it will be the only one repainted, but it also has to fill the whole bounds rect on its own, otherwise you will see awful painting artifacts behind your component on each repaint.
To summ up - to avoid pointless repainting of overlapping components there are a few approaches:
Optimize animation repaint calls to areas you actually need to repaint
Use opaque components, unless you want to paint something transparent in your component
Optimize all overlapping components painting operations so repaints won't take much time
There still might be more optimization approaches depending on your specific case, but you will have to find them on your own, since that is impossible without seeing the whole picture.
You can also find a lot of useful information about optimizations in Swing in this book: Filthy Rich Clients
Well in terms of optimization i do have a component, which is causing quite some trouble, but that one i have planned to rewrite. Therefore i just want to make sure, that painting areas are properly. Following that point, i have computed all necessary areas and filled them into a list, which i pass as data has changed.
Its working properly as long as i only have one rectangle applied. As soon as i pass a second one, it seems its y - extension becomes ignored. For instance:
[x=0,y=0,width=322,height=20]
[x=0,y=620,width=322,height=20]
There everything between y=20 and y=620 also gets redrawn.
for (Rectangle rec : clippingAreas) {
painter.repaint(rec);
}
Ok i have tried paintImmediately within the EDT, which does work for now, but i wonder if this is a proper way to go:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (Rectangle rec : clippingAreas) {
painter.paintImmediately(rec);
}
}
});
Ok there we go:
package animationissue;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class AnimationIssue extends JFrame {
JPanel a = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("map has become repainted");
}
};
JPanel b = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("menu as well");
}
};
public AnimationIssue() {
this.setSize(500, 500);
this.setLayout(null);
a.setSize(400, 400);
b.setSize(400, 200);
this.getLayeredPane().add(a, JLayeredPane.DEFAULT_LAYER); // Map
this.getLayeredPane().add(b, JLayeredPane.PALETTE_LAYER); // Menu
a.setLocation(0, 0);
b.setLocation(0, 100);
a.setBackground(Color.red);
b.setBackground(Color.blue);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// doin some computations for animation
// cast a repaint after having finished new
//animation information i.e. interpolation
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
// case 1 - just a repaint of the whole component - triggering map redraw results in menu redraw
// a.repaint();
// case 2 - repaint of specified rectangle
// Either passing one - the menu does not get repainted, or passing both - menu also gets repainted
//a.repaint(0, 0, 400, 100);
//a.repaint(0, 300, 400, 100);
// paintimmediately works for now
//a.paintImmediately(0, 0, 400, 100);
//a.paintImmediately(0, 300, 400, 100);
// Just repainting Menu does not trigger map to become repainted, except if its opague, but then it should become repainted
b.repaint();
}
}
});
t.start();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
AnimationIssue f = new AnimationIssue();
f.setVisible(true);
}
}
I was really looking forward to optimize the behavior, that no menu gets redrawn, if its not necessary. You have to imagine the menu of being a component holding several JLists with a lot of String drawing tasks, which really have a massive impact on cpu usage. I havent been wondering, since it gets redrawn approximately 25 times per sec.
I am just uncertain for the current solution, if using paintImmediately is proper. Apart from this - if you or somebody has an alternative - better - way to prevent useless redrawing (i really thought Glasspane or JLayeredPane or isOptimizedDrawing or isOpaque might help out), i am really thankful.
Best regards.
I'm currently using an animation engine I designed that takes objects of type Drawable and adds them to a List. Drawable is an interface that has one method:
public void draw(Graphics2D g2d);
The extending animation manager iterates through this list and calls the draw() method on every object, passing the Graphics2D object obtained from the Swing component.
This method seemed to work well at first, but as I feared, it seems to be unable to handle multiple objects in the long run.
With merely two Drawables registered, both drawing images on screen, I'm seeing a bit of flashing after 30-60 seconds.
Is there a way to optimize this method? It currently calls upon the AWT thread (invokeLater) to handle all of the tasks. Concurrent drawing isn't really an option as this nearly always causes issues in Swing/AWT, in large part due to the fact that Graphics isn't synchronized.
If this just simply is a bad way of animating, what is a better method when you have multiple objects that all need things rendered with their own specific variables cough game cough?
Any help would be appreciated. Thanks!
EDIT:
I can't use repaint() beacuse my engine already calls the AWT thread to paint stuff. If I call invokeLater from the AWT thread, the image never gets painted for some reason.
I should also add that I'm using a system of ticks and fps. 60 ticks # 120 fps.
Each tick updates the game logic, while each frame render calls draw on the frame manager.
Is this a bad idea? Should I just use FPS and not ticks?
I think it would be more appropriate to override paintComponent(Graphics g) and regularly call the repaint method on the JPanel or whatever you're drawing on with a Timer. Your problems may be due to to you trying to draw and then Swing doing it's own draw.
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel() {
public void paintComponent(Graphics g) {
//draw here
}
};
panel.setPreferredSize(800, 600);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true)
new Timer(16, new ActionListener() {
public void actionPerformed(ActionEvent event) {
panel.repaint();
}
}).start();
}
}