JFrame Woun't Rener on Panel

I am creating a Pong Remake. However when i open the game window all i get is a blank frame. None of the images are being rendered even though its supposed to since repaint() is being called in the thread. I have tried to get it to work with no avail.No errors are being reported by the error handlers i have, so my guess is everything is loading correctly. Can someone please help me out.

Link:
You will need to visit:
Hyper Rush Pong Website
and then copy and paste this link once you are on the site:
Source

Thanks in Advance,

Hyper Rush Pong Development Team

source:
HTTP 403 - Forbidden

…anyway I wouldn’t read it anyway if I couldn’t find render stuff right away. Basicly you need to put panel into JFrame container (or make it container), override it’s paintComponent() and paint there. From main loop you can call repaint(). This method of rendering is bad, but since you asked about it I’ll help. Post your rendering code, describe implementation, tell us what you have tried to solve it, …and such stuff. Don’t just put bunch of code and expect us to read the whole thing and just tell you what line to correct.

It’s more complicated then you can imagine. I am doing exactly what you saying. However i have 2 classes paddle and ball which have a draw(Graphics g) method which will draw in the class OnePlayerGame/TwoPlayerGame by getting the graphics contex of that frame. Also if you follow the instructions i gave above, you will be able to download the source code, Anyways heres the code for the one player game:


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.applet.Applet;
import java.applet.AudioClip;

import javax.swing.JFrame;
import javax.swing.JPanel;


class OnePlayerGame extends Canvas implements WindowListener, Runnable{

//global variables for off-screen rendering
private Graphics dbg;
private Image dbImage = null;

// for the animation
private Thread animator;


//background bufferedImage object to hold the background image
private BufferedImage background_image;

//background AudioClip object to hold the gameengine's sound effect
private AudioClip sound_effect;

//boolian to hold wether the game is paused or not
private boolean ispaused = false;

//boolian to hold wether the game is running or not
private boolean running = false;

public final int screenWidth;

public final int screenHeight;

OnePlayerGame(int new_screenWidth, int new_screenHeight, String filename){
	
	//create a frame
	JFrame frame = new JFrame();
	
	//create a panel
	JPanel panel = (JPanel)frame.getContentPane();
	
	//set the frames window title
	frame.setTitle("Pong Master v1.0 - One Player Game");
	
	//set the default close operation to close when the window is closed
	frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
	
	//get the current screens height
	screenWidth = new_screenWidth;
	
	//get the current screens height
	screenHeight = new_screenHeight;
	
	//set the game windows size and center it
	frame.setBounds(0, 0, screenWidth, screenHeight);
	
	//set the game panels size and position it
	panel.setBounds(0, 0, screenWidth, screenHeight);
	
	//load the background image
	load_background_image(filename);
	
	panel.setFocusable(true);
	panel.requestFocus(); //now has focus, so recieves key events
	
	panel.addKeyListener( new KeyAdapter() {
		public void keyPressed(KeyEvent e)
		{ processKey(e); }
	});
	
	//add the panel
	panel.add(this);
	
	//set the panel layout to null
	panel.setLayout(null);
	
	//show the frame
	frame.setVisible(true);
	}

Paddle player1 = new Paddle("Data/Graphics/bat1.png", 10, 10);
Paddle player2 = new Paddle("Data/Graphics/bat2.png", 20, 20);
Ball ball = new Ball("Data/Graphics/ball.png", 320, 240, 2, 2);

//*************************************************************************************
// Function add_Notify()
// wait for the panel to be added to the JFrame/Japplet before starting
//*************************************************************************************
public void add_Notify(){
super.addNotify();
startGame();
}

//*************************************************************************************
// Function draw()
// Draws the paddle to the current graphics contex
//*************************************************************************************
public void draw(Graphics g){
	g.drawImage(background_image,0,0,this);
}

//*************************************************************************************
// Function gameOver_message()
// displays a gameover message
//*************************************************************************************
private void gameOver_Message(Graphics g){
	//center the gameover message
	
	//get the center x position
	int x = screenWidth / 4;

	int y = screenHeight / 4; 

	g.drawString("GameOver!", x, y);
}


//*************************************************************************************
// Function gameRender()
// renderss the game images
//*************************************************************************************
private void gameRender(){
	//draw the current frame to an image buffer
	if (dbImage == null){
	//create the buffer
	dbImage = createImage(screenWidth, screenHeight);
	if(dbImage == null){
	System.out.println("Could not create Screen Buffer");
	return;
	}
	else
		dbg = dbImage.getGraphics();
	}

//clear the background
dbg.setColor(Color.white);
dbg.fillRect(0, 0, screenWidth, screenHeight);

// draw the game elements

// draw the background
draw(dbg);

// draw player 1's paddle
player1.draw(dbg);

// draw player 2's paddle
player2.draw(dbg);

// draw the ball
ball.draw(dbg);

}//end of gamerender


//*************************************************************************************
// Function gameUpdate()
// updates the game state
//*************************************************************************************
private void gameUpdate(){
	
}

//*************************************************************************************
// Function load_image()
// loads the paddle image into bufferedimage object paddle_image
//*************************************************************************************
public void load_background_image(String filename){
try{
	background_image = ImageIO.read(getClass().getResource(filename));
	}catch(IOException e){
	System.out.println("Load Image error: Failed to Load" + filename);
	}//end of catch block
}//end of try block

//*************************************************************************************
// Function paintComponent()
// Flips the Buffers
//*************************************************************************************
public void paint(Graphics g){
	if(dbImage != null){
		g.drawImage(dbImage, 0, 0, null);
	}
}

//*************************************************************************************
// Function run()
// Runs the game
//*************************************************************************************
public void run(){
//repeatly update, render, sleep
running = true;

while(running){
gameUpdate();
gameRender();
repaint();

try {
Thread.sleep(20); //sleep a bit
}
catch(InterruptedException ex) {}
	}
 System.exit(0);
} //end of run

//*************************************************************************************
// Function startGame()
// initalize and start the thread
//*************************************************************************************
public void startGame(){
if (animator == null || !running) {
	animator = new Thread(this);
	animator.start();
	}
} // end of startGame()

}

Plus i plan on converting to Active Rendering Soon.

I’ve took a look at your code, there are 2 things you missed.
First one is you don’t set size (bounds) of Canvas you add to container, so it’s never displayed. You can confirm you got it working by adding some test paint code in overriden paint(). When your frame shows you should able to see it. Second one is that you never start game thread. You have your startGame() in addNotify() method which I don’t know what is used for, but it never gets called. Print some text every tick in game loop and you should see it’s never printed. Call your startGame() somewhere else, or debug what you have now.

p.s. I see you are using Killer Game Programming in Java :slight_smile: … nice book, I started with it also, but it’s too old in some areas (like the hook thing, never got it why is termination like that)

addNotify is called from swing, when a component is added to a container, there is also removeNotify when it is removed (or the frame is closed). This two methods are quite handy in swing programming.

addNotify is called from swing, when a component is added to a container. Since panel is added to the frame:


//add the panel
panel.add(this);

addNotify should be called:


//*************************************************************************************
// Function add_Notify()
// wait for the panel to be added to the JFrame/Japplet before starting
//*************************************************************************************
public void add_Notify(){
super.addNotify();
startGame();
}

Both Frame and Panal are set using setBounds:


//set the game windows size and center it
	frame.setBounds(0, 0, screenWidth, screenHeight);
	
	//set the game panels size and position it
	panel.setBounds(0, 0, screenWidth, screenHeight);

If i’m missing something please feel free to post some code to explain it better. Like i said i’m new at this.

is it called? I’ve put “System.out.println(“game update”);” in game loop and it didn’t get called. When I called startGame() manually in constructor everything was fine.

yeah but Canvas is also a component you add to the panel, and it’s starting size is 0.
use “this.setBounds(0, 0, screenWidth, screenHeight);”

btw. you might have problem with this form of set bounds since it dosen’t calculate how much pixels border and title bar of JFrame has taken. Part of your game might not be rendered (right and down of screen). Use getInsets() or something like that to set bounds proper way, also you might use setUndecorated(true) to get rid of JFrame’s titlebar and border.

Canvas is part of AWT not swing. You should use JPanel instead.

Your version of addNotify is called add_Notify. It will never be called by Swing, since it is spelled incorrectly.

Not correct. The default layout for the content pane of a JFrame is BorderLayout. When you add a component to the content pane with out any constraints, it automatically goes to BorderLayout.CENTER. Since he set the size of the JFrame and added the Canvas in this manner, the Canvas will take the size of all the available space inside the JFrame.

I agree that you use JPanel instead. I’ve heard it’s not good mixing awt and swing.

I wasn’t speaking generally, what you say is (probably) correct. I just said what happened to me, the code inside paint() didn’t get called until I did setBounds() on Canvas. Of course I might be wrong, but this is how I remember it, so michael try it first without it.

Like This:


class OnePlayerGame extends JFrame implements WindowListener, Runnable{

//global variables for off-screen rendering
private Graphics dbg;
private Image dbImage = null;

// for the animation
private Thread animator;


//background bufferedImage object to hold the background image
private BufferedImage background_image;

//background AudioClip object to hold the gameengine's sound effect
private AudioClip sound_effect;

//boolian to hold wether the game is paused or not
private boolean ispaused = false;

//boolian to hold wether the game is running or not
private boolean running = false;

public final int screenWidth;

public final int screenHeight;

OnePlayerGame(int new_screenWidth, int new_screenHeight, String filename){
	
	//create a panel
	JPanel panel = (JPanel)this.getContentPane();
	
	//set the frames window title
	this.setTitle("Pong Master v1.0 - One Player Game");
	
	//set the default close operation to close when the window is closed
	this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
	
	//get the current screens height
	screenWidth = new_screenWidth;
	
	//get the current screens height
	screenHeight = new_screenHeight;
	
	//set the game windows size and center it
	this.setBounds(0, 0, screenWidth, screenHeight);
	
	//set the game panels size and position it
	panel.setBounds(0, 0, screenWidth, screenHeight);
	
	//load the background image
	load_background_image(filename);
	
	panel.setFocusable(true);
	panel.requestFocus(); //now has focus, so recieves key events
	
	panel.addKeyListener( new KeyAdapter() {
		public void keyPressed(KeyEvent e)
		{ processKey(e); }
	});
	
	//add the panel
	panel.add(this);
	
	//set the panel layout to null
	panel.setLayout(null);
	
	//show the frame
	this.setVisible(true);
	}

There are several errors/misbehavours in your code:

  • dont use the contentPane as your panel, create a new one (panel= new JPanel())

  • dont set the size of the panel (the default layout manager of jframe will handle this)

  • dont add the frame to the panel, add the panel to the contentpane (getContentPane().add(panel));

  • dont show your frame from the constructor, do it from your main()-method

  • add the keylistener to the frame instead of the panel and set the panel to setFocusable(false)

  • call requestFocus() on the frame from your main-method after setVisible(true)
    I did not test this, but I think it will get you started

  • and finally: read the Swing tutorial about JFrame and maybe the others, too :wink:

if we’re going that way I’ll add a little bit:

so it’s not a bad thing… but creating new container as content pane is better :slight_smile:

I think you meant don’t add panel to frame… anyway this is not wrong and maybe it’s more intuitive then first getting content pane. Javadoc:

[quote=JavaDoc for JFrame]As a conveniance add and its variants, remove and setLayout have been overridden to forward to the contentPane as necessary. This means you can write:
frame.add(child);
[/quote]
other advices are excellent

@CaptainJester
I see now that later michael set layout to null, so I guess that is why Canvas isn’t displayed until you do setBounds() on it

@michael
try to make more readable code… use spaceing better or something, I’ve had a hard time figuring out where some blocks of code start/end.

so is this correct:


class OnePlayerGame extends JFrame implements WindowListener, Runnable{

//global variables for off-screen rendering
private Graphics dbg;
private Image dbImage = null;

// for the animation
private Thread animator;


//background bufferedImage object to hold the background image
private BufferedImage background_image;

//background AudioClip object to hold the gameengine's sound effect
private AudioClip sound_effect;

//boolian to hold wether the game is paused or not
private boolean ispaused = false;

//boolian to hold wether the game is running or not
private boolean running = false;

public final int screenWidth;

public final int screenHeight;

OnePlayerGame(int new_screenWidth, int new_screenHeight, String filename){
	
	//create a panel
	JPanel panel = nw JPanel();
	
	//set the frames window title
	this.setTitle("Pong Master v1.0 - One Player Game");
	
	//set the default close operation to close when the window is closed
	this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
	
	//get the current screens height
	screenWidth = new_screenWidth;
	
	//get the current screens height
	screenHeight = new_screenHeight;
	
	//set the game windows size and center it
	this.setBounds(0, 0, screenWidth, screenHeight);
	
	//load the background image
	load_background_image(filename);
	
	panel.setFocusable(false);
	
	this.addKeyListener( new KeyAdapter() {
		public void keyPressed(KeyEvent e)
		{ processKey(e); }
	});
	
	//add the panel
	getContentPane().add(panel);
	
	//set the panel layout to null
	panel.setLayout(null);
	
	//show the frame
	this.setVisible(true);
	
           panel.requestFocus(); //now has focus, so recieves key events
               }

… short anwser: no

What I think you should do is:
OnePlayerGame should extend JPanel, as you want to override it’s paintComponent() for manual drawing
then you create JFrame and do frame.setContentPane(this); and your JPanel with overriden paintComponent() will became content pane of your JFrame. Use this.setFocusable(false); Use frame.requestFocus(); Use frame.addKeyListener(); Don’t use this.setLayout(null); (it’s already FlowLayout by default). Rename your add_Notify() to addNotify() or call startGame() some different way (maybe from same block after you set your frame visible).
I typed this all out of my head so if I missed something sorry.

and if you want to follow good programming style do everything else what we talked about


class OnePlayerGame extends JPanel implements WindowListener, Runnable{

//global variables for off-screen rendering
private Graphics dbg;
private Image dbImage = null;

// for the animation
private Thread animator;


//background bufferedImage object to hold the background image
private BufferedImage background_image;

//background AudioClip object to hold the gameengine's sound effect
private AudioClip sound_effect;

//boolian to hold wether the game is paused or not
private boolean ispaused = false;

//boolian to hold wether the game is running or not
private boolean running = false;

public final int screenWidth;

public final int screenHeight;

OnePlayerGame(int new_screenWidth, int new_screenHeight, String filename){
	
	//create a panel
	JFrame frame = nw JFrame();
	
	//set the frames window title
	frame.setTitle("Pong Master v1.0 - One Player Game");
	
	//set the default close operation to close when the window is closed
	frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
	
	//get the current screens height
	screenWidth = new_screenWidth;
	
	//get the current screens height
	screenHeight = new_screenHeight;
	
	//set the game windows size
	frame.setBounds(0, 0, screenWidth, screenHeight);
	
	//load the background image
	load_background_image(filename);
	
	this.setFocusable(false);
	
	frame.addKeyListener( new KeyAdapter() {
		public void keyPressed(KeyEvent e)
		{ processKey(e); }
	});
	
	//add the frame
	frame.getContentPane(this);
	
	//show the frame
	frame.setVisible(true);
	
           frame.requestFocus(); //now has focus, so recieves key events
               }

Ok is this correct?

I’m placing a space in between every commented code and seperating the blocks from non blocks. What else can i do?

actually I meant what I wrote, the original code was:


	//add the panel
	panel.add(this);

:o which was most certainly a quick-rush-error and not intended, but might lead to interesting results (since “panel” is the contentpane of “this”) :wink:

Maybe you should leave this question to the compiler and ask more specifically, if you have problems :wink:

You could consider splitting up your code in different classes, so you encapsulate the things that belong together and separate them from things, that do not.

Ok, i’ll get back to you when i get a response from the compiler.

You mean seperating the frame and panel code? How do i implement each game mode without having to write 3 classes per game mode(etc. OnePlayerGameFrame.class, OnePlayerGamePanel.class, OnePlayerGame.class)?

One way to separate your code would be, to create… :

  • a HyperRushPong-Class containing the main()-method, the game-loop
    parametrization, setting up the game etc.
  • a Sprite class, representing a player or a ball
  • a Render class, which is a JPanel able to paint a list of Sprites
  • a Game-Interface that has a animate(int time)-Method
  • a OnePlayerGame class, which implements Game and KeyListener and modifies the sprites in the list
  • a TwoPlayerGame class, which is the same as above, but for two players
  • etc.

So you would

  • create a List of Sprites
  • create a Renderer and pass the list
  • create a game Object and pass the list (Game game= new One(or Two)PlayerGame(sprites))
  • create a JFrame
  • add the Renderer
  • attach the game object as KeyListener to the frame
  • show the frame and call requestFocus()
  • loop the game:
    o call game.animate(System.currentTimeMillis());
    o call renderer.repaint();
    o call Thread.yield();
    o call Thread.sleep(200);

Keep in mind, that there are a lot of different ways to do this and this outline might not be the best or most intuitive way of doing it, but it feels natural to me.

You might find it useful to read the Space Invaders Tutorial