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()
Related
I am sorry about the non-descriptive title but I am not sure how to communicate the problems. For starters the JButtons every now and again are creating themselves multiple times in the same order that the loop should create them. Another issue I am having is that when I reposition them using the setLocation() method it creates new JButtons where I want them but also leaves the old ones where they are. I don't know if I just have to refresh the graphics or what is going on. Thanks for the help.
the array playerHand()is defined in the Player class and is 5 in length.
public void paintComponent(java.awt.Graphics g){
setBackground(Color.GREEN);
// create a Graphics2D object from the graphics object for drawing shape
Graphics2D gr=(Graphics2D) g;
for(int x=0;x<Player.hand.size();x++){
Card c = Player.hand.get(x); //c = current element in array
c.XCenter = 30 + 140*x;
c.XCord = c.XCenter - 30;
c.YCord = 0;
//5 pixel thick pen
gr.setStroke(new java.awt.BasicStroke(3));
gr.setColor(Color.black); //sets pen color to black
gr.drawRect(c.XCord, c.YCord, c.cardWidth-1, c.cardHeight-1); //draws card outline
gr.setFont(new Font("Serif", Font.PLAIN, 18));
gr.drawString(c.name, c.XCord+10, c.YCord+20);
gr.drawString("Atk: ", c.XCord+10, c.YCord+60);
gr.drawString(""+c.attack, c.XCord+60, c.YCord+60);
gr.drawString("Def: ", c.XCord+10, c.YCord+80);
gr.drawString(""+c.defence, c.XCord+60, c.YCord+80);
gr.drawString("HP: ", c.XCord+10, c.YCord+100);
gr.drawString(""+c.health, c.XCord+60, c.YCord+100);
JButton button = new JButton(c.name);
button.setSize(c.cardWidth, c.cardHeight);
//button.setLocation(c.XCord, c.YCord);
this.add(button);
repaint();
}
} //end of paintComponent
There are several problems (marked by "!!!!" comments) in your method below:
public void paintComponent(java.awt.Graphics g){
setBackground(Color.GREEN); // !!!!
Graphics2D gr=(Graphics2D) g;
for(int x=0;x<Player.hand.size();x++){
// .... etc ....
JButton button = new JButton(c.name); // !!!! yikes !!!!
button.setSize(c.cardWidth, c.cardHeight);
//button.setLocation(c.XCord, c.YCord);
this.add(button); // !!!! yikes !!!!
repaint(); // !!!!
}
}
Do not add components to GUI's in paintComponent(...). Never do this. Ever.
This method should be for drawing and drawing only and never for program logic or GUI building.
You do not have full control over when or if it will be called.
It needs to be as fast as possible so as not to make your program poorly responsive.
Avoid object creation within paintComponent(...) if possible.
Avoid file I/O in this method (though not a problem with your code above).
Don't call setBackground(...) in this method. Do that in the constructor or other method.
Never call repaint() from within this method.
Don't forget to call the super's paintComponent(g) method in your override.
To go over your problems:
For starters the JButtons every now and again are creating themselves multiple times in the same order that the loop should create them.
This is because you do not have control over when or how often paintComponent is called. The JVM may call it in response to information from the operating system about a dirty region, or the program may request a repaint, but for this reason, program logic and gui building should never be in this method.
Another issue I am having is that when I reposition them using the setLocation() method it creates new JButtons where I want them but also leaves the old ones where they are.
Quite likely you're seeing image remnants from your not calling the super's paintComponent method. By not doing this, you don't have the JPanel repaint itself and remove old image pixels that need to be replaced or refreshed.
I don't know if I just have to refresh the graphics or what is going on.
No, you need to change just about everything.
It looks like you have to re-think your GUI's program flow and logic and re-write a bit of code.
I'm trying to create a java game in which balls randomly drop from the top of the screen and we need to catch the balls using a catcher which is located at the bottom of the screen.
I'm having a difficult time figuring out how to actually draw this onto my JFrame.
I've got a class for my 'catcher', 'ball', 'game space' and I would like to put it all together.
How do I draw my 'catcher' onto my screen?
Currently, I have a 'Game' class which looks like this.
public class Game extends JFrame implements KeyListener {
GameScreen gameScreen;
Catcher playerOneCatcher;
public static void main (String[] args) {
new Game();
}
public Game() {
super("CATCH");
setSize(640,480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
setResizable(false);
addKeyListener(this);
this.gameScreen = new GameScreen();
this.playerOneCatcher = new Catcher(40, 10);
}
I've tried something like this in my Catcher class...
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(positionX, positionY, this.width, this.height);
}
However, its not showing on my screen.
Any help would be greatly appreciated.
You need to rethink your strategy here. Swing is a component framework, with most components intended for building user interfaces. These components are not optimised for what's typically required in games. You want to look into double-buffering, sprites etc. The way to go will be to read up on Graphics2D class (or abandon Swing altogether!)
However answering to your question - if Catcher is a Swing component - you need to add it to the "parent" component, e.g. like this:
this.add(playerOneCatcher);
Same goes to gameScreen but from your snippet it is not obvious what this component is. I hope this helps.
Also, check this out for some ideas: 2D Java Game. Moving sprite above tiled images
Did you call super.paintComponent (g) ? That can cause a few bugs.
Did you call invalidate () or repaint () to repaint the thing you are painting on? I hope you have a special JComponent, and you are not drawing on a JFrame. That is NOT good.
I am creating a program in which the user can draw lines and erase them. I am able to create my window and create a functioning JMenuBar. When the drawing capability is not enabled, everything is fine. But when the drawing capability is enabled, a second JMenuBar is painted below the original. This menu bar is not functional, and sometimes when it is drawn, ALL of the graphics on the screen are repainted lower as well. Re-sizing the window in any way remedies these problem.
I would post pictures but, you know, I'm a new user and can't.
I have been able to narrow the problem down to a point in my paint() method. Need to know: When the mouse is dragged, the repaint() method is called. It uses my paint() method which calls the paintObject(Graphics g) method in my Line class. The JMenuBar error is appearing at the END of the paint method in the top class.
public void paint(Graphics g)//paint() in the top class
{
for(int i = 0; i < objs.size(); i++)//Loop that finds all Tool(Line) objects that
((Tools)objs.get(i)).paintObject(g);//have been drawn, and redraws them.
//This part of the code completes successfully with no error.
}//ERROR OCCURS HERE!!!!!!
I have no idea why this is happening. As a side note, there is also a JTextField on the screen that doesn't appear until it is highlighted. Thanks for at least reading this far!
Edit: I'll go on a limb, I'll bet you in fact are drawing directly on the top level window (JFrame or JApplet or JDialog...) and have added the mouse listener to this window as well -- hence the drawing of components translated down a bit.
The solution is to look at the tutorials first before attempting something fairly complex like this. They'll tell you how to draw correctly including the suggestions I've given in my comments, such as drawing in a JComponent or JPanel's paintComponent method and calling the super.paintComponent(g) method as the first method call of your paintComponent override.
A mouse listener calls repaint() and I can see that it does actually go past the drawing part because I see globalcounter incremented in the output of System.out.println(). On the screen, however, I don't see any change until I minimize the window and maximize again, resize it, or move it out of the visible screen area and back in. Obviously I'd like it to update without my intervention.
class GUI extends javax.swing.JFrame {
int globalcounter=0;
class MyCanvas extends Canvas {
#Override
public void paint(Graphics g) {
globalcounter++;
g.drawString(globalcounter,100,100);
System.out.println(globalcounter);
}
}
}
(Originally I was loading an image from a file that got constantly updated (webcam) and painting it on the canvas. When I dragged it partly out of the visible screen area and back in, the part that has been 'outside' was refreshed, the rest not.)
revalidate() instead of repaint() didn't change anything.
I know this post is a duplicate to Java repaint not working correctly but when I posted it there it was deleted.
Why are you adding an AWT component, Canvas, to a Swing component, JFrame? You should stick with Swing components only. And also do you know the size of your MyCanvas, and how have you added it to the JFrame as you don't show this code.
Consider
using a JPanel instead of a Canvas object,
drawing in its paintComponent method,
showing us an sscce if you're still stuck.
And also, if all you're doing is drawing text, use a JLabel rather than drawing in paint/paintComponent, and change its text with its setText(...) method.
I'm writing a program that will be just a simple shape maker, I'm supposed to my main panel, ShapeMakerPanel, along with 2 panels on that one, controlPanel(which holds buttons for choosing the shape and clearing it, etc) and drawingArea (where the shapes are actually drawn), but no matter what I do, I can't get the paint to show up on drawingArea. If I just use paintComponent and comment out add(drawingArea) then the drawing stuff works, but on the bottom layer, how can I paint on the drawingArea Jpanel. Also, when I do draw the shapes, I can only have one at a time, because every time I start a new one, the panel is wiped clean`
super.paintComponent(g);
g.setColor(penColor);
if (p1 != null && p2 != null)
{
if (shapeChoice.getSelectedItem().equals("Line"))
{
Line line = new Line(p1.x, p1.y, p2.x, p2.y);
line.draw(g);
}
}
I know its the super.paintComponent(g) messing me up, but without that, as I drag the mouse, it draws hundreds of lines.
If you were wondering about the Line class, we had to make a class for each shape we drew, the draw() method just uses the coordinates of the line and puts them into drawLine().
Don't override paintComponent() in ShapeMakerPanel; override it in drawingArea's class (if drawingArea is a plain JPanel, then create a new subclass of JPanel). In general you need to subclass the component on which you're going to paint.
Also, when I do draw the shapes, I can only have one at a time, because every time I start a new one, the panel is wiped clean`
See Custom Painting Approaches for a couple of ways to solve this.