# Graphics Objekt liefert null



## kaoZ (13. Jul 2014)

Kann mir vielleicht schnell einer sagen ob meine Vermutung richtig ist, warum g2 hier null liefert?

Ich vermute weil der panel / dessen Graphics Objekt noch nicht vollständig initialisiert ist,
und ich versuchen mit diesem direkt zu zeichnen.

Die betreffende stelle ist markiert, egal wie ich auch versuche an das vollständig initialisierte Objekt zu gelangen fliegt hier immer eine NPE.

*Witzigerweise beim debuggen nicht o0.....*


```
public class GamePanel extends JPanel implements Runnable, KeyListener{
    private static final long serialVersionUID = 1L;
    
    //properties
    static int WIDHT	 = 320;
    static int HEIGHT	 = 240;
    static int SCALE	 = 2;
    
    //image
    BufferedImage image;
    Graphics g;
    
    //gameloop
    Thread gameLoop;
    boolean running;
    static int FPS = 60;
    static int targetTime = 1000 / FPS;

    public GamePanel() {
    	setProperties();
    	initialise();
	    startGameLoop();
    }
    
    private void setProperties(){
    	setPreferredSize(new Dimension(WIDHT *SCALE, HEIGHT * SCALE));
    	setFocusable(true);
    	requestFocus();
    }
    
    private void initialise(){
    	
    	image = new BufferedImage(WIDHT, HEIGHT, BufferedImage.TYPE_INT_RGB);
    	
    	g = image.getGraphics();
    	
    	running = true;
    }
    
    private void startGameLoop(){
    	
    	if(gameLoop == null){
    		gameLoop = new Thread(this);
    		gameLoop.start();
    	}
    }
    
    private void update() {}
    private void render() {}
    
    private void draw() {
    	
    	Graphics g2 = getGraphics();  // <-- bleibt null / ggf. weil panel noch nicht komplett initialisiert ?!
    	g2.drawImage(image, 0, 0, WIDHT, HEIGHT, null);
    	g2.dispose();
    }
    
	@Override
    public void keyTyped(KeyEvent e) {}

	@Override
    public void keyPressed(KeyEvent e) {}

	@Override
    public void keyReleased(KeyEvent e) {}

	@Override
    public void run() {
	    
		update();
		render();
		draw();
		
		try {
	        Thread.sleep(1000);
        } catch (Exception e) {
	        // TODO: handle exception
        }
    }

}
```

Das hier kommt beim debuggin heraus :

Was mich hier irritiert ist das laut Belegung hier ein Graphics2D Objekt geliefert wird, das ist hier aber nicht der Fall ...... 





Keine Exception !


und das hier wenn ich es normal ausführe :


```
Exception in thread "Thread-1" java.lang.NullPointerException
	at dev.eflow.game.main.GamePanel.draw(GamePanel.java:67)
	at dev.eflow.game.main.GamePanel.run(GamePanel.java:85)
	at java.lang.Thread.run(Unknown Source)
```

also fliegt quasi hier die NPE 


```
g2.drawImage(image, 0, 0, WIDHT, HEIGHT, null);

    // g2 ist hier angeblich null, aber warum dann beim debugging nicht ?
```


----------



## kaoZ (13. Jul 2014)

Wenn ich aber während der Normalen Ausführung 


```
if(g2 == null){
  System.out.println("g2 == NULL");
  System.exit(0);
}
```

scheint die Annahme doch korrekt zu sein das g2 null ist , aber warum passiert das nicht beim Debuggen, 

und liegt dann doch daran das vielleicht das Graphics Objekt noch nicht vollständig Initialisiert wurde , und falls dem so sein sollte, warum ? und wo kann ich sonst das vollständig Initialisierte Objekt herbekommen da ich dies ja brauche um direkt zu zeichnen, oder sollte vielleicht der erste aufruf von draw(); verzögert stattfinden ?


----------



## turtle (13. Jul 2014)

Das Graphics-Objekt ist in der Tat nicht vollständig initialisiert. 

Dieses nennt man "Realization" und geschieht u.a. wenn ein Objekt sichtbar gemacht wird. 

Ich habe mal zwei Links dazu (eins und zwei)


----------



## kaoZ (13. Jul 2014)

Ich bin Grade nicht Zuhause, aber da ich hier von JPanel ableite und somit nur vollständig  initialisieren kann wenn ich das Objekt erstellt habe, bleibt nur also quasi nur der Weg nicht abzuleiten und einfach eine Referenz auf ein Jpanel zu halten und dann mit getter zu arbeiten, sprich schon vorher das panel vollständig zu initialisieren und mir dann das graphics Objekt zu holen ? Was ich nicht verstehe ist das es im tutorial auch so gemacht wird, da scheint es ohne Probleme zu funktionieren, ich habe lediglich das starten des gameloops am anderer Stelle realisiert,  ob es damit zusammenhängen könnte? Das tutorial ist das was ich bereits im letzten Post erwähnt hatte,  ich hab leider vom Handy aus den link Grad nicht parat, aber das beschäftigt mich jetzt :-D


----------



## Thallius (13. Jul 2014)

Ich finde es nicht besonders geschickt die GameLoop in ein Panel zu packen. Die GameLoop gehört in einen Controller und nich in einen View (MVC Pattern)

Warum machst du das ?

Gruss

Claus


----------



## kaoZ (13. Jul 2014)

Zum testen hielt ich es nicht die nötig es nach einem pattern umzusetzen,  bzw. den thread  auszulagern,  ob ich es später so umsetze ist natürlich möglich, sobald ich das graphics Problem gelöst habe 

Mal angesehen davon hab ich mich an einen tutorial orientiert welches ebenfalls den gameloop im panel realisiert hat,  allerdings erzeugt und startet er diesen in einer überschrieben addNotify() Methode,  was ja an und für sich keinen Unterschied machen sollte


----------



## kaoZ (13. Jul 2014)

Ich bin erst Mittwoch wieder Zuhause, turtle hat du damit schon Erfahrung? bzw. Steht darüber vielleicht was in dem Buch welches du erwähnt hattest ?

Oder ich muss dann wirklich erst den Lösungsansatz mit der Referenz auf ein JPanel ausprobieren


----------



## turtle (14. Jul 2014)

Also, in dem angesprochenen Buch, finde ich nichts über ein Problem mit der Realization von JPanels gelesen

Ich glaube aber auch, das dein Code maximal von Swing und der JVM entkoppelt sein sollte. 

Dies bedeutet, das du das JPanel "normal" erzeugen solltest und dann darauf anschliessend deinen GameLoop-Thread anwenden solltest. 

Hier wird im Buch, zu Recht, angemerkt, das du dir den Graphics-Kontext jedesmal neu holen solltest, um nicht mit Swing/OS in Konflikt zu kommen.

In Pseudo-Code sieht das so aus:

```
public void run() {
	running = true;
	while (running) {
	    gameUpdate(); // game state is updated
	    gameRender(); // render to a buffer
	    paintScreen(); // draw buffer to screen
	}
    }
private void paintScreen( ) {
 Graphics g;
 try {
 g = this.getGraphics( ); // get the panel's graphic context
...
}
```


----------



## kaoZ (14. Jul 2014)

Ich werd mir das ganze Mittwoch mal in Ruhe anschauen und den gameloop dann mal auslagern, das Jpanel mal nicht ableiten sondern normal instanzieren und mal schauen wohin mich das führt.
Was ich mich natürlich Frage, ist Immernoch warum funktioniert es im Tutorial reibungslos und warum gibt's beim Debuggen das Problem des nicht vollständig initialisieren graphics Objekts nicht 

Ansonsten geb ich dann nach meinen versuchen mal Rückmeldung


----------



## Thallius (14. Jul 2014)

Da es sich um zwei Task handeld ist es wahrscheinlich eine timing Sache ob es klappt oder nicht. Beim Debuggen wird wahrscheinlich der UI Task schon fertig sein bevor die GameLoop anläuft.

Gruß

Claus


----------



## kaoZ (15. Jul 2014)

Ich hab jetzt mal spaßeshalber zum testen folgendes gemacht :


```
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JPanel;

public class GamePanel extends JPanel implements Runnable {
    private static final long serialVersionUID = 1L;
    
    
    //pos
    int x = 10;
    int y = 10;
    
    //properties
	static final int WIDTH	 = 320;
	static final int HEIGHT	 = 240;
	static final int SCALE	 = 2;
	
	//gameloop
	Thread thread;
	boolean running;
	static int FPS = 60;
	
	//background
	Background image;
	
	public GamePanel() {
		setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		setFocusable(true);
		requestFocus();
		
		image = new Background("/background/background.png");
		
		running = true;
	}
	
	private void update()	{
		
		image.update();
	}
	private void render()	{
		
		image.render();
	}
	private void draw()		{
		
		Graphics g = null;
		
		try {
	        g = this.getGraphics();
	        
	        image.draw(g);
        } catch (Exception e) {
	        e.printStackTrace();
        } 
		finally {
        	g.dispose();
        }
	}
	
	@Override
	public void addNotify(){
		super.addNotify();
		
		if(thread == null){
			thread = new Thread(this);
			thread.start();
		}
	}
	
	
	@Override
	public void run() {
	    
		while (running) {
	        
			update();
			render();
			draw();
			
			try {
	            Thread.sleep(100);
            } catch (Exception e) {
	            // TODO: handle exception
            }
        }
	}
}
```

hier die Klasse Background ( nicht fertig )


```
import java.awt.Graphics;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;

public class Background {
	
	BufferedImage image;
	
	public Background(String path) {
	   try {
	    image = ImageIO.read(getClass().getResourceAsStream(path));
    } catch (Exception e) {
    	e.printStackTrace();
    	}
    }
	
	public void update()	{}
	public void render()	{}
	
	
	public void draw(Graphics g){
		g.drawImage(image, 0, 0, GamePanel.WIDTH, GamePanel.HEIGHT, null);
	}

}
```

und der Aufruf :


```
public class Game {

	public static void main(String[] args) {
	    JFrame f = new JFrame("GameTest");
	    f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	    
	    GamePanel gamePanel = new GamePanel();
	    
	    
	    f.add(gamePanel);
	    f.pack();
	    f.setLocationRelativeTo(null);
	    f.setVisible(true);
    }
	
}
```

und siehe da keine NPE mehr....., es scheint also tatsächlich an dem vorher noch nicht initialisierten Graphics Objekt es Jpanels, ich habe auch mal das erzeugen des Threads wie im Tutorial in die addNotify() Methode ausgelagert, die so wie auch paintComponent() von Swing / System. selbstständig ausgeführt wird.

nun könnte ich ja Theoretisch hergehen und den Loop noch in eine eigene Klasse auslagern, diesem dann nur das Runnable übergeben, und den panel Referenzieren anstatt  von JPanel abzuleiten, oder wie würdet ihr an die Sache herangehen ? , direkt nach dem MVC Pattern umsetzen ? Möchte es anfangs auch nicht zu unübersichtlich gestalten.

[EDIT]Allerdings fliegt nach schließen des Frames noch eine / mehrere NPE's da der GameLoop noch versucht die update/render/draw methoden aufzurufen das Graphics object aber wieder == null ist[/EDIT]


----------



## kaoZ (16. Jul 2014)

So ich habs so wie es aussieht, ich schreibe den Code nochmal um morgen früh und poste einfach mal das Ergebnis falls es wen interessiert ^^


----------



## kaoZ (16. Jul 2014)

So schauts zzt. aus , ich probiere aber auch noch rum, ich hab nun auch nachvollziehen können warum hier der Thread in einer überschriebenen addNotify() Methode erzeugt wird, da diese standardmäßig vom toolkit aufgerufen wird und hier auch gleich der Listener gesetzt wird.

Da hier liefert mir eine einfach Scroll-Animation mit einem hochskallierten Hintergrund, ich fuchse mich langsam rein, und hab es einfach mal stellenweise kommentiert um den Ablauf nochmal besser nachvollziehen zu können.

Das Problem das beim beenden der Thread noch läuft und drawToScreen() called, obwohl schon kein Graphics objekt mehr da ist, besteht allerdings weiterhin , ich werd wohl noch etwas tüfteln / optimieren / refactoren 



```
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.JPanel;

public class GamePanel extends JPanel implements Runnable {
    private static final long serialVersionUID = 1L;
    
    
    //pos
    int x = 10;
    int y = 10;
    
    //properties
	static final int WIDTH	 = 320;
	static final int HEIGHT	 = 240;
	static final int SCALE	 = 2;
	
	//gameloop
	Thread thread;
	boolean running;
	static int FPS = 60;
	
	//rendergraphics
	Graphics renderObject;
	
	//background
	BufferedImage image;
	
	//test
	Background b;
	
	public GamePanel() {
		setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		setFocusable(true);
		requestFocus();
		
		//rendertest
		////////////////////////////////////////////////////////
		b = new Background("/background/background.png", 1);
		b.setVector(-0.1, 0);
		
		///////////////////////////////////////////////////////
		
	}
	
	private void init(){
		
		image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
		
		running = true;
	}
	
	private void update()	{
		
		b.update();
		
	}
	private void draw()		{

		//get graphics object from empty BufferedImage
		renderObject = image.getGraphics();
		//draw background to BufferedImage
		b.draw(renderObject);
		
	}
	
	private void drawToScreen()		{
		
		Graphics g = null;
		
		try {
	        g = this.getGraphics();
	        //draw Bufferedimage ( rendered + scaled ) to screen(Jpanel)
	        g.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);   
        } catch (Exception e) {
	        e.printStackTrace();
        } 
		finally {
        	g.dispose();
        }
	}
	
	
	//called by toolkit , addListener / create Thread
	@Override
	public void addNotify(){
		super.addNotify();
		
		if(thread == null){
			thread = new Thread(this);
			addKeyListener(new GameListener());
			thread.start();
		}
	}
	
	
	@Override
	public void run() {
		
		init();
	    
		while (running) {
	        
			update();
			draw();
			drawToScreen();
			
			try {
	            Thread.sleep(10);
            } catch (Exception e) {
	            e.printStackTrace();
            }
        }
	}
	
	class GameListener extends KeyAdapter{

		@Override
        public void keyPressed(KeyEvent e) {
	        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
	        	
            }
	        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
	        	
            }  
        }
	}
}
```


----------



## Thallius (16. Jul 2014)

Ist es eigentlich so abwegig in Java einfach ein


```
g = this.getGraphics();
if(g!=null)
{
    g.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
}
```

zu machen um eine Exception zu vermeiden?

Gruß

Claus


----------



## kaoZ (16. Jul 2014)

Nein natürlich nicht, nur da ich vorher schon Probleme hatte herauszufinden wo genau die NPE auftritt, ist es einfacher dies mit stacktrace herauszufinden als wenn einfach nichts passiert 

Das ist hier auch lange noch keine finale Version^^ es ging erst einmal darum zu vermeiden das hier der EDT startet bevor der gameloop / Thread läuft und eben das Graphics Objekt noch nicht vollständig initialisiert ist.

Mal davon abgesehen ist es 
a) keine Ausnahmebehandlung hier nur den Stack ausgeben zu lassen, und 
b) viel zu allgemein hier nur Exceptions abzufangen


----------

