Skip to main content
GameDev.net gamedev.net
🔒 Locked

[java] JFrames and Graphics

Started by Pajari Jun 18, 2005 at 10:18 PM 15 replies 3.4k views
Original Post
Pajari
Pajari
I am having one hell of a time writing a simple strategy game in Java (actually not a full game, just a tech demo). The problem seems to come from drawImage(). I attempt to draw an image (or a hundred of them- it's a tile-based game and I iterate over a matrix of terrain tiles and display them in order) and I get a null pointer exception. g.drawImage(grass, 40, 40, this); gives me a null-pointer exception? The only thing that could possibly be null would be the last argument, "this", or the component that I'm supposed to draw on. I'm using a single JFrame for my game (at least right now) and I was wondering if there were any special or strange conventions with JFrames that I wasn't aware of, and that can make an error like this occur. Any help at all would be greatly apprecieated- this is driving me up the wall.
darklordsatan
darklordsatan
Could you post the stack trace?

catch(Exception e){e.printStackTrace();};
Optus
Optus
I'm pretty sure that if g.drawImage(grass, 40, 40, this); is throwing an NPE, that means your Graphics object g is null, not the parameters you're using.
shmoove
shmoove
Quote:
Original post by Pajari
g.drawImage(grass, 40, 40, this); gives me a null-pointer exception? The only thing that could possibly be null would be the last argument, "this", or the component that I'm supposed to draw on.

There is no way "this" is null. What could be null is either "g" or "grass".

shmoove
obi-wan shinobi
obi-wan shinobi
I've had this problem too. One way around this is to allow your frame-making class to extend the Canvas class and do your drawImage() in the overridden paint(). Other than that, I'd suggest looking in the documentation for methods that can create a Graphics object for a JFrame, since Graphics is abstract and cannot be instantiated.

public class demo extends Canvas implements Runnable{  JFrame jf;  Thread thr;  public void paint(Graphics g) {  //do your image drawing here  }  public void run() {    while(true) {      repaint();    }  }  public static void main(String[] args) {  thr = new Thread(this);  thr.start();  jf = new JFrame();  thr.start();  jf.add(this);                 //adds Canvas object to window  jf.pack();  jf.setVisible(true);  }}
Pajari
Pajari
Thanks for the help, everyone. Like I said, it's most apprecieated.

I'm fairly sure that my Graphics object is instantiated and that my images are ok. And I always print the stack trace when I catch an exception- that's how I found out that something in g.drawImage() was null. It did not show up at compile-time as an error (I'm using JCreator 2.5 or something, btw).

And as for shinobi's response, I was thinking about using a canvas before (it would make handling mouse events easier too) and now it looks like that will be the best solution. I've never used threads before, so this should be extremely interesting.

Thanks! I'll post the results later.

[Edited by - Pajari on June 19, 2005 10:41:20 AM]
Pajari
Pajari
Now I'm getting a null pointer exception with the Canvas- even after I've created it and gotten the accessible context for it. Any help?
Monkeyget
Monkeyget
Try to do a setVisible(true) BEFORE you do all those fancy operations.
jeeky
jeeky
I recommend finding out what is really null. To do this, enable assertions at the command line with -ea, then assert pretty much everything.

assert(g != null);
assert(tile != null);

etc, etc...

It is kind of a waste of time to switch to a different solution when you don't even know what the problem is.
jeeky
jeeky
Oh, and I would recommend using a really good IDE if the one you have stinks. Eclipse 3.1 RC2 and NetBeans 4.1 are both good and free. I am using Eclipse, and it is very nice. It lets you follow the stack trace when you get an exception.
Pajari
Pajari
Quote:
Original post by jeeky
I recommend finding out what is really null. To do this, enable assertions at the command line with -ea, then assert pretty much everything.

assert(g != null);
assert(tile != null);

etc, etc...

It is kind of a waste of time to switch to a different solution when you don't even know what the problem is.


I'm pretty sure that everything is instantiated, including the canvas, the frame, the graphics object, all the images, and the integer variables involved. That's why I came here in the first place- I was recieving a null pointer exception at run time and not compile time.

jeeky
jeeky
I am assuming based on your reply that you have no idea what assertions are. When you assert something, you are "pretty sure" that it is true. Asserts check whatever you are asserting at run time. Try googleing "java assert" or something like that.

It is quite easy to do as well.

For example,

assert(true == PajariHasNoIdeaWhatAssertsAre);

would be a pretty safe and easy assertion for me to make right now.

You might also want to try to finalize your objects. When you create your objects in your class, you can declare them final, meaning the reference will never change. Doing this will also minimize your uncertainty. In particular, I would load all of your media objects up at once and reference them with final members.

E.g.-
private static final Image tile0 = loadImage0Here...
private static final Image tile1 = loadImage1Here...
private static final Image tile2 = loadImage2Here...
private static final Image tile3 = loadImage3Here...
private static final Image tile4 = loadImage4Here...

Hope that helps. Of course, since you are pretty sure you know what you are doing, you can feel free to ignore us and guess your way to a solution.
obi-wan shinobi
obi-wan shinobi
This is a complete listing that should work once copied and pasted. It doesn't use double buffering, so you'll see a little black flickering.

import java.lang.*;import java.util.*;import java.awt.*;import java.awt.Canvas.*;import java.awt.event.*;import java.awt.Graphics.*;import java.applet.*;import javax.swing.*;class makeframe extends Canvas implements Runnable, KeyListener {				int x = 20;		int u=50;		int y = 20;		int v=50;		Thread thr;			makeframe() {		thr = new Thread(this);		thr.start();		Graphics g = getGraphics();		Dimension di = new Dimension(400,300);		JFrame jf = new JFrame("Test Program");		jf.setDefaultLookAndFeelDecorated(true);		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);		JPanel jp = new JPanel();		JLabel jl = new JLabel("JLabel!");		jl.setForeground(Color.blue);		jp.setBackground(Color.red);		jp.setPreferredSize(di);		jf.setBounds(0,0,400,300);		jp.add(jl);		jp.add(this);		jf.add(jp);		jf.addKeyListener(this);		setBounds(0,0,400,300);		jf.setSize(di);		jf.pack();		jf.setVisible(true);	}		public void paint(Graphics g) {				//System.out.println("Painting");		g.setColor(Color.blue);        g.fill3DRect(u, v, x, y, true);	}				public void run() {		while(true) {			repaint();			if(x>75)	x=20;			else		x++;			if(y>75)	y=20;			else		y++;			try {				thr.sleep(25);			}			catch(InterruptedException e) {							}		}	}		public void keyPressed(KeyEvent e) {		char c = e.getKeyChar();		if(c == 'w' & u<250)	u++;		if(u==250)				u=50;	}		public void keyReleased(KeyEvent e) {			}		public void keyTyped(KeyEvent e) {		//System.out.println(e.getKeyChar());	}}public class awtdemo {		public static void main(String[] args) {		makeframe mf = new makeframe();	}}
Son of Cain
Son of Cain
Monkeyget is right - you can draw on a JFrame that is visible. When using a BufferStrategy, it also works this way.
a.k.a javabeats at yahoo.ca
Pajari
Pajari
Alright, nothing is working (and I tried assertions, too). I was right whan I said that my graphics object was instantiated- but I was wrong in assuming that that wasn't a problem, because the graphics object has been null this whole time. Adding a canvas, adding a jpanel into my jframe, using threads and assertions, none of it has changed the fundamental problem that getGraphics(), for whatever reason, is always returning null. I thank all of you profusely for your help, but it really hasn't changed the basic problem that I now have- I guess I just wasn't asking the question correctly.

EDIT: The code.

[source lang = "java"]import java.io.*;import javax.swing.*;import java.awt.*;import java.awt.event.*;import javax.swing.JFileChooser.*;import java.awt.Graphics.*;import java.awt.Color.*;public class Screen extends Canvas implements ActionListener, MouseListener, Runnable{	public TerrainItem[][] land = new TerrainItem[10][10];	public Unit[][] unitMap = new Unit[10][10];	//public Player thePlayer;		public Unit current;		public File map;		public JMenuBar mainMenu;		Thread thr;	Graphics g;		public JMenu file;	public JMenu about;	public JMenu kingdom;		public JMenuItem loadMap;	public JMenuItem reviewTroops;	public JMenuItem credits;	public JMenuItem reviewProduction;	public JMenuItem endTurn;		private static final Image plains = Toolkit.getDefaultToolkit().getImage("grass.jpg");	private static final Image sand = Toolkit.getDefaultToolkit().getImage("sand.jpg");	private static final Image mountain = Toolkit.getDefaultToolkit().getImage("mountain.bmp");	private static final Image lake = Toolkit.getDefaultToolkit().getImage("lake.bmp");	private static final Image forest = Toolkit.getDefaultToolkit().getImage("forest.jpg");			private static final Image knight = Toolkit.getDefaultToolkit().getImage("knight.bmp");	private static final Image soldier = Toolkit.getDefaultToolkit().getImage("soldier.jpg");		public Screen()	{		thr = new Thread(this);		thr.start();				g = getGraphics();				if(g == null)		{			System.out.println("dammit...");		}			JFrame frame = new JFrame("Swing Test");		frame.setDefaultLookAndFeelDecorated(true);				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		setBounds(0,0,400,300);				Container c = frame.getContentPane();				JPanel area = new JPanel();		area.setPreferredSize(new Dimension(400,400));		area.add(this);				mainMenu = new JMenuBar();				file = new JMenu("File");		file.getAccessibleContext().setAccessibleDescription("The main menu for the game.");		mainMenu.add(file);				kingdom = new JMenu("Kingdom");		kingdom.getAccessibleContext().setAccessibleDescription("another secondary menu.");		mainMenu.add(kingdom);				about= new JMenu("About");		about.getAccessibleContext().setAccessibleDescription("The secondary menu.");		mainMenu.add(about);				loadMap = new JMenuItem("Load Map");		loadMap.addActionListener(this);		file.add(loadMap);				credits = new JMenuItem("Credits");		credits.addActionListener(this);		about.add(credits);				reviewTroops = new JMenuItem("Review Troops");		reviewTroops.addActionListener(this);		kingdom.add(reviewTroops);				reviewProduction = new JMenuItem("Review Production");		reviewProduction.addActionListener(this);		kingdom.add(reviewProduction);						endTurn = new JMenuItem("End Turn");		endTurn.addActionListener(this);		file.add(endTurn);				frame.setJMenuBar(mainMenu);				frame.getContentPane().add(area);				frame.pack();        frame.setVisible(true);	}		public void run()	{		while(true)		{			paint();		}	}		public static void main(String[] args)	{		Screen window = new Screen();	}		public void actionPerformed(ActionEvent event)	{		if("Load Map".equals(event.getActionCommand()))		{			int x = ((int)(Math.random() * 4));						for(int k = 0; k < 10; k++)			{				for(int j = 0; j < 10; j++)				{					if(x == 0)					{						land[k][j] = new TerrainItem("lake");					}					else if(x == 1)					{						land[k][j] = new TerrainItem("plains");					}					else if(x == 2)					{						land[k][j] = new TerrainItem("mountain");					}					else if(x == 3)					{						land[k][j] = new TerrainItem("forest");					}					else if(x == 4)					{						land[k][j] = new TerrainItem("sand");					}				}			}		}		else if("Review Troops".equals(event.getActionCommand()))		{			System.out.println("reviewtroops");		}		else if("Credits".equals(event.getActionCommand()))		{			System.out.println("credits");		}		else if("End Turn".equals(event.getActionCommand()))		{			Unit temp;						for(int k = 0; k < 10; k++)			{				for(int j = 0; j <10; j++)				{					if(unitMap[j][k] != null)					{						temp = unitMap[j][k];												unitMap[j][k] = null;												unitMap[temp.toX][temp.toY] = temp;					}				}			}		}	}		public void mouseClicked(MouseEvent e)	{		g = getGraphics();		int x = e.getX();		int y = e.getY();				x = (x - (x % 40)) / 40;		y = (y - (y % 40)) / 40;				if(current == null)		{			for(int k = 0; k < 10; k++)			{				for(int j = 0; j <10; j++)				{					if(unitMap[j][k].getX() == x && unitMap[j][k].getY() == y)					{						current = unitMap[j][k];					}				}			}						current.toX = current.getX();			current.toY = current.getY();		}		else		{			if((x > (current.getX() + 1)) || (y > (current.getY() + 1)) || (y < (current.getY() - 1)) || (x < (current.getX() - 1)))			{				g.drawLine(current.getX(), current.getY(), x, y);				current.toX = x;				current.toY = y;			}		}	}		public void mouseReleased(MouseEvent e) {}	public void mousePressed(MouseEvent e) {}	public void mouseExited(MouseEvent e) {}	public void mouseEntered(MouseEvent e) {}		public void paint()	{		g = getGraphics();				if(land != null)		{			for(int k = 0; k < 10; k++)			{				for(int j = 0; j <10; j++)				{					if(g != null)					{						System.out.println("okay...");												if(land[j][k].getName() == "lake")						{							g.drawImage(lake, j*40, k*40, this);						}						if(land[j][k].getName() == "plains")						{							g.drawImage(plains, j*40, k*40, this);						}						else if(land[j][k].getName() == "mountain")						{							g.drawImage(mountain, j*40, k*40, this);						}						else if(land[j][k].getName() == "forest")						{							g.drawImage(mountain, j*40, k*40, this);						}						else if(land[j][k].getName() == "sand")						{							getGraphics().drawImage(sand, j*40, k*40, this);						}						if(unitMap[j][k].getName() == "soldier")						{							g.drawImage(soldier, j*40, k*40, this);						}						else if(unitMap[j][k].getName() == "knight")						{							g.drawImage(knight, j*40, k*40, this);						}					}				}			}		}	}}
Son of Cain
Son of Cain
Hmmm... I'm pretty sure you're calling getGraphics() where you shouldn't. A Graphics instance is generated by the JVM when it is ready to draw your component - the Canvas, for example.

Another way to grab an instance for Graphics is by using a BufferStrategy; since you're probably going to use it for the sake of performance, I recommend you take a look at the FAQ and search for capn midnight's post regarding double buffering.

Son Of Cain
a.k.a javabeats at yahoo.ca
Pajari
Pajari
Thank you. That looks like it will help. I'll post here in a few minutes after I've tested it out.

EDIT: It worked. Hoo-rah!

[Edited by - Pajari on July 2, 2005 12:17:09 AM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.