I'm trying to make a game with Swing and I need to make a board with players on the board, so I've used the JLayeredPane. At layer 0 I drew the board and then on layer 1 I've painted the players.
This works well but the problem is when I redraw layer 1 (e.g. change the position of a given player) the performance is very bad. My assumption is it's repainting the background board again and this is causing the issue.
Here's the code:
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(boardView, new Integer(0));
layeredPane.add(creepsView, new Integer(1));
creepsView and boardView override the paint method and when changes to the state of the creeps happens, it's calling the creepsView.repaint() method, but then the boardview.paint() is called by the JLayeredPane and I don't understand why. How can I avoid it? (I don't want to redraw the things that didn't change).
I guess it's because it's repainting the background board again.
I doubt that is the problem since painting an image that is loaded into memory is very efficient.
, it's calling the creepsView.repaint() method and then the boardview.paint is called by the JLayeredPane
In order for you to see the background that means the panel containing the players must be transparent. This means when you repaint the player panel, the background panel must also be repainted to make sure there are no painting artifacts. For example, the player must be removed from its old position and painting in the new position.
You can try to make the painting more efficient by only painting the affected area of the panel. That is instead of repainting the entire player panel you only repaint the player at its old position and at its new position. Read the section from the Swing tutorial on Custom Painting for an example of this approach. ThemoveSquare(...) method shows how this approach works.
Related
in a game, I would like to display simultaneously two JComponent instances in a JFrame. One would be a background board, the another - a player character.
So, one component (a background) would be behind another (a character). The character would be drawn of several rectangles and thus it will most commonly have some wholly transparent area.
How to do that? I know that normally, when I add two components to a frame (method add(Component)), only the last-added component is visible. This is done by following code:
frame.add(backg); // backg is an instance of a certain class that derives from JComponent
// (...)
frame.add(psc); // psc is an instance of an another class that derives from JComponent
frame.pack();
frame.setVisible(true);
How should I change the code above?
First of all, if you are looking to write a game in Java, try out Slick2D provides numerous tools and far better graphical capabilties (being wrapped around LWJGL which wraps around OpenGL). Secondly, if you do decide to go the Swing route, here's a simple solution:
//Player + background components defined here
playerComp.setOpaque(false);
JLayeredPane layer = new JLayeredPane();
layer.add(backgroundComp,1,0);
layer.add(playerComp,2,0);
I believe that both these solutions were mentioned above in the comments. Setting the player component opaquity to false allows those transparent areas to show components behind the player, or the background image. If you're familiar with a z-index in CSS/HTML, a JLayeredPane basically adds a z-index to Swing by allowing you to set the order in which components are rendered. So, set the player to opaque, and then render it in front of the background component.
I am attempting to add 3 graphics, two of which have to move(presumably through each other, moving on a single axis) and only the lattermost one added to the panel shows.
In other words, I have many panels added to my JFrame, and in the biggest panel I have put in graphics objects using specific coordinates. It may be more helpful to show the code.
//the gameArea is the referred-to JPanel, above this code
TankOne tank1 = new TankOne(Color.GREEN);
TankTwo tank2 = new TankTwo(Color.MAGENTA);
FieldBar fieldb = new FieldBar(Color.getHSBColor((float) Math.random(),(float) Math.random(),(float) Math.random()));
JPanel tank1panel = new JPanel();
JPanel tank2panel = new JPanel();
tank1panel.add(tank1);
tank2panel.add(tank2);
gameArea.add(tank1panel);
gameArea.add(tank2panel);
gameArea.add(fieldb);
//repaint code here
As you can see, it is a tank game.
The one that shows up on the GUI is the last gameArea.add. [Here it would be fieldb, a bar that shows the game floor].
The attempt made here is me trying to put two new panels into the gameArea Jpanel and then placing the two tank objects inside those. Still no dice.
Is there a workaround for this that would allow me to move the tanks by pixels?
I have seen some answers include switching to gridlayout, but I don't think that will help seeing as the extra Panels did nothing.
Thank you for your answers.
I'm guessing that each tank JPanel shows an image of a single tank (? not 100% sure). Some suggestions, but please do tell me if my assumptions are way off base:
Keep the logic and view well separated a la M-V-C.
Do all graphics in a single JPanel, perhaps called a DrawingPanel, in its paintComponent(...) method.
The Background image will be a BufferedImage that is draw in the DrawingPanel's paintComponent(...) method.
The a tank itself would not be represented by a JPanel but would be its own small BufferedImage sprite, again draw in the paintComponent method, but drawn after the background image.
Each tank would need at least 4 sprites, one for each direction.
You may need more sprites if your tanks will move along a diagonal.
And a separate sprite/image for the turret.
You would move a sprite's drawn position in response to changes of it's model representation's position.
Same thing for turret rotation.
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.
At the moment, I have two JPanel classes that draw images and shapes to a JFrame (I will have more in the future), I'm doing this to make things organised.
At first I tried to add each JPanel to the JFrame, but one JPanel would paint over the other.
Each class should be able to call other classes that may draw images to screen as well.
The problem I have is that I cannot get them to draw to the screen.
Should I use paintComponent or paintAll? And how should they be used?
Thank you for any help :)
It sounds as though you are adding both panels to the same location in the JFrame probably in the BorderLayout.CENTER position. One solution is to use a GridLayout with 2 columns for the JFrame and add the 2 panels.
paintComponent is the correct method to override in your panels.
Follow the custom painting trail to see how it should be used.
I am making a game in Java as a way to teach me more about Java and Game Programming in general. But, I have one question. The Title Bar on my JFrame takes up 29 Y-pixels of the space. Therefore, I have less drawing room then I had planned. Is there a way to make it so the corner of the JFrame Canvas is the point (0,0) and not (0, 29)? Thanks
The answer is simple: don't draw in the JFrame's paint(...) method but rather in a JPanel's paintComponent(...) method, and then make the JPanel the JFrame's contentPane. This solution is well described in the Swing Tutorials painting section, and there are several reasons to do this including:
You don't risk drawing over things that shouldn't be drawn over including borders and title bars.
You get the advantage of Swing's default double buffering which can make a huge improvement if you do any animation.