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.
Related
I'm working on a project where I am displaying a square and a circle.
The circle moves on its own, but the user moves the square via the arrow keys. Whenever the circle touches the square, it rebounds.
Square and circle are different classes (2 different panels). I want to add those two to a frame, one on top of the other such that both are visible.
Can someone tell me how to do so?
JFrame n = new JFrame();
n.setTitle("Background Color for JFrame");
n.setSize(1000,600);
n.setLocationRelativeTo(null);
n.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
n.setResizable(false);
n.add(new Ball());
n.add(new Team());
n.setVisible(true);
User interface components at the same level in a hierarchy are assumed to be non-overlapping by default. You can explicitly work around this by making your components transparent with setOpaque(false), assuming you are taking care to only draw whats needed in the components, e.g. in case of a JPanel ensure that its background is not drawn. Its still somewhat random (implementation dependent) which component has precendence over another when doing this.
There is a component explicitly designed for that: JLayeredPane (https://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html), which manages "layers" in which components can be put, giving you full control which overlays which.
Games often implement this by themselves, since the full features of JComponent are not needed to represent a simple graphical element. In that case a single component is used as a "canvas" to paint self-defined objects onto making use of an override of paintComponent (See: https://docs.oracle.com/javase/tutorial/uiswing/painting/)
If you want to do this in swing, as it sounds, I would really recommend making a new class that extends JPanel, and override it's paintComponent method. In this method you use the Graphics in the argument to paint to the canvas. Then you can add this custom panel instead of two separate components to your JFrame, and handle the rendering in there. This render panel can then keep track of all objects that needs to be rendered, preferable implementing some interface (Drawable?) with a draw(Graphics g) method. That way you can make your classes that needs to be rendered implement your Drawable interface, keep then as a list in your render panel and just iterate through them in your paintComponent method and call draw on your Drawables.
I am doing a project with double buffering. When I paint, it simply paints on top of the old layers, but I need to erase them. Repaint() didn't work, but I'm guessing something equally as simple is the answer.
Any ideas?
Added code, and now it disappears, but it erases the background color.
public void paint(Graphics g)
{
super.paint(buffer);
for(Projectile p: projectiles)
drawRectImage(buffer, p.image, p.getRectangle());
}
Suggestions:
If this is a Swing GUI, then don't override the paint method, but instead override the paintComponent method. This won't help your current problem, but will help prevent future problems including problems with painting of borders and child components.
If Swing (again you don't say), then make sure that your painting component extends JPanel, not JComponent, since JPanel is opaque and fills its background rectangle in its super method.
If it's not Swing, then you should strongly consider changing from AWT to Swing.
If you're still stuck, then yep, you'll want to create and post a minimal example program. Please check out the link.
I'm learning Java as part of my degree but its very brief but what ever I do I like to make sure I atleast have some understanding.
Until now anything that I want displayed on the screen is put into the paintcomponent method of the JPanel.
However, I have drawn out some parts of my layout which never change, and only a certain thing in the middle rotates.
I have a timer which calls repaint().
If I correct in thinking that everything including the components that never change are being removed and then redrawn and the whole paintcomponent method is being run every time.
To me I feel like I should (or there must be) a way where the static stuff is moved out / is only drawn once, and only the parts that I specifically want to be redrawing should stay in the paintcomponent method?
Is this correct or am I not fully understanding something?
I am assuming that you are not adding GUI components in the paintComponent method, right?
Components are not "removed" during a repaint
Better to put the stable part of any image into a single background BufferedImage and draw the BufferedImage in the paintComponent method. This can make for more efficient painting.
Consider calling an overload of repaint(...) that sets a bounding rectangle for the region to be painted.
i wanted to ask, if somebody might have a solution about a problem i face. I am working at an application, which draws an animation - for instance a map with objects moving onto. My problem is, that on top of the drawing, a Jtable, Jlist as well as other Components are also placed.
In my particular example all of those components have been added to the Panel, which holds the map. In result each component gets redrawn as often as good my fps is. Therefore making one of the tables invisible reduces the already high cpu usage of sometimes around 50% to less than 30%.
My question is, how can i avoid calling somewhat static visual contents paintComponent() method, without having the "background" - the map - whited out the menu.
Since the animation redraws permanently the menu is not shown at all, if its separated from the corresponding JPanel.
First thoughts move into following directions:
Clipping - actually not as good as i would like to, since id like to enable moving around the menus.
JLayeredPane - already tried but seemed to turn out, that the paintComponent method of menus still gets called frequently.
JWindow/Internal Frame - had that thought a couple of minutes ago. Having a complete independent container shall be able to handle my regard, or?
I am looking forward, if somebody has an elegant idea, how to fix that and reduce the cpu usage significantly.
Thanks!!
Best regards.
I would create a custom Shape for clip. Use Area class and subtract from the Area all the children components' bounds.
For painting over JComponent(s) placed into JPanel you have look at
JLayer (Java7) based on JXLayer(Java6)
GlassPane, notice all JComponents must be lightweight, otherwise is GlassPane behind heavyweight (J)Components
is possible painting to the JViewport,
EDIT
you have to use Swing Timer for moving with Icon (in the JLabel) placed into JXLayer, GlassPane or JViewport, don't use Runnable#Thread and never, not by using plain Thread.sleep(int)
Basically I'm drawing a lot of transparent JPanels; profiling shows that most time is spent in Component.paint(). It would be possible to optimize this quite radically since in most cases the real non-opaque area per JPanel is quite small, for example around the edges.
As it stands now, repainting() a component would trigger a repaint of all its parents, since the RepaintManager can't know the dirty parent area is in fact opaque and will go up the component hierarchy. I was thinking about calling markCompletelyClean() on all parents whenever a panel is invalidated and managing the dirty regions myself with addDirtyRegion().
However, is there a cleaner approach of marking only specific rectangles of a JPanel as opaque (or transparent, doesn't matter)?
Even if you do something fancy with markCompletelyClean() and addDirtyRegion(), I doubt you'll get much of a performance benefit. When swing goes for a repaint, it collates all of the dirty regions to paint and starts a repaint with the minimum bounding box of all of the dirty regions. Thus if you mark the perimeter of a JPanel as dirty, then the bounding box of the JPanel is the same as the entire JPanel, which means you'll be repainting the entire thing any way.
Consider using JLabel (non-opaque by default) instead of JPanel, then you are able (to the JLabel) you can add any JComponent same as to the JPanel, but you have to set LayoutManager, then you forgot care about Opacity/Transparency and for particulars Region(s)
Why can't you define your own method myRepaint() calling multiple
public void repaint(long tm, int x, int y, int width, int height)
for all the borders?
Also try to play with clipBounds of your Graphics to repaint only really necessary parts. You can set any custom Shape as clip.