2D-Grafik NPE beim Zeichnen auf Graphics g

  • Themenstarter Themenstarter peebles
  • Beginndatum Beginndatum
P

peebles

Gast
Hallo,

ich wollte mir eine Sprite-Klasse schreiben, zum "flüssigen" (ist relativ) Darstellen von Animationen,
durch widerholtes zeichnen verschiedener Bilder (hier: BufferedImage).

Habe mir auch schnell eine Klasse geschrieben, wahrscheinlich noch uneffizient aber das tut erst einmal nichts zur Sache.

Java:
public class Sprite {

	long delay;
	long animation = 0;
	int currentPic = 0;
	BufferedImage[] pics;
	BufferedImage[] aa;
	
	private int x,y, vx, vy;
	
	public Sprite(String path, int x, int y, long delay, int count) {
		this.setX(x);
		this.setY(y);
		this.delay = delay;
		currentPic = 0;
		aa = loadPics("pics/Sprite1.png", 4);
		this.setPics(aa);
	}
	
	public void setPics(BufferedImage[] pics) {
		this.pics = pics;
	}
	
	public BufferedImage[] getPics() {
		return pics;
	}
	
	public void draw(Graphics g) {
		g.drawImage(pics[currentPic], this.x, this.y, null);
	}
	
       //getter & setter weggelassen

	
	public void forceAnimation() {
		currentPic++;
		if (currentPic > pics.length-1) {
			currentPic = 0;
		}
	}
	
	public void checkNextPic(long delta) {
		animation += (delta/1000000);
		if (animation > delay)
		{
			animation = 0;
			forceAnimation();
		}
	}
	
	private BufferedImage[] loadPics(String path, int pics) {
		BufferedImage[] anim = new BufferedImage[pics];
		BufferedImage source = null;
		
		URL url = getClass().getClassLoader().getResource(path);
		try {
		source = ImageIO.read(url);
		} catch (IOException e) { }
		
		for (int x = 0; x < pics; x++) {
			anim[x] = source.getSubimage(x*source.getWidth()/pics, 0,                  //x1, y1
					                     source.getWidth()/pics, source.getHeight()); //x2, y2
		}
		
		return anim;
	}
}

In meiner Main-Loop ("Spielschleife") wird dann wie gehabt it repaint() eine Anfrage gestellt, wobei dann paintComponent aufgerufen wird:

Java:
public void paintComponent(Graphics g) {
		super.paintComponent(g);
		
		g.drawString("FPS " + Long.toString(fps), 10, 20);
		
		if (!input.getRunningState()) {
			return;
		}
		
		block.draw(g);
		sprite.draw(g);
}

Bei
Java:
sprite.draw(g);
kriege ich eine NullPointerException, obwohl (!) das Programm ordnungsgemäß startet und auch sogar zeichnet, so wie es sein soll.

Das Bild ist also garantiert nicht null und wird richtig geladen, sowie in kleine Einzelteile zerschnitten.

Jemand eine Idee?

Danke
 
Da du pics nicht initialisierst, muss bevor du ein Sprite zeichnen kannst setPicks mit einem gültigen Array als Parameter aufgerufen werden. Davon seh ich in deinem Code nix, am besten wäre, du postest ein KSKB.
 
Also ich rufe
Java:
 setPics(aa)
auf, und davor doch
Java:
 aa = loadPics(...)
und loadPics(...) liefert ein gültiges Array zurück, nämlich anim[].
Also wird doch auch "aa" initialisiert?
Dann hat pics[] den Wert von aa[].

Ich habe schon versucht zu debuggen, aber bin leider nicht fündig geworden.
Und was ist ein KSKB?
 
Mein Fehler, sorry.

Hier das KSKB, jedoch braucht ihr ein Bild im angegeben Verzeichnis, damit es funktioniert.
Anbei meine Klassen

Main:
Java:
import java.awt.Dimension;
import java.awt.Graphics;

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

public class Main extends JPanel implements Runnable { 
	
	private static final long serialVersionUID = 1L;
	JFrame frame;
	Sprite sprite;
	long delta, last = 0;
	
	public Main(int w, int h) {
		this.setPreferredSize(new Dimension(w, h));
		frame = new JFrame("Blocks2D");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(this);
		frame.pack();
		frame.setResizable(false);
		frame.setVisible(true);
		
		init();
		
		Thread t = new Thread(this);
		t.start();
	}

	@Override
	public void run() {
		while(true) {
			newDelta();	
			repaint();
			try {
			Thread.sleep(10);
			} catch (InterruptedException e) { }
		}
	}
	
	private void newDelta() {
		delta = System.nanoTime() - last;
		last = System.nanoTime();
		
		sprite.checkNextPic(delta);
	} 
	
	public void init() {
		last = System.nanoTime(); 
		sprite = new Sprite("pics/Sprite1.png", 100, 100, 200, 4);
	}
	
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		
		sprite.draw(g);
	}

	
	public static void main(String[] args) {
		new Main(800, 600);
	}
}

Sprite:
Java:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

public class Sprite {

	long delay;
	long animation = 0;
	int currentPic = 0;
	BufferedImage[] pics;
	BufferedImage[] aa;
	
	private int x,y;
	
	public Sprite(String path, int x, int y, long delay, int count) {
		this.setX(x);
		this.setY(y);
		this.delay = delay;
		currentPic = 0;
		aa = loadPics(path, 4);
		this.setPics(aa);
	}
	
	public void setPics(BufferedImage[] pics) {
		this.pics = pics;
	}
	
	public void draw(Graphics g) {
		g.drawImage(pics[currentPic], this.x, this.y, null);
	}
	
	public void setX(int x) {
		this.x = x;
	}
	
	public void setY(int y) {
		this.y = y;
	}
		
	public void forceAnimation() {
		currentPic++;
		if (currentPic > pics.length-1) {
			currentPic = 0;
		}
	}
	
	public void checkNextPic(long delta) {
		animation += (delta/1000000);
		if (animation > delay)
		{
			animation = 0;
			forceAnimation();
		}
	}
	
	private BufferedImage[] loadPics(String path, int pics) {
		BufferedImage[] anim = new BufferedImage[pics];
		BufferedImage source = null;
		
		URL url = getClass().getClassLoader().getResource(path);
		try {
		source = ImageIO.read(url);
		} catch (IOException e) { }
		
		for (int x = 0; x < pics; x++) {
			anim[x] = source.getSubimage(x*source.getWidth()/pics, 0,                 //x1, y1
					                     source.getWidth()/pics, source.getHeight()); //x2, y2
		}
		
		return anim;
	}
}

Lässt sich bei mir kompilieren, und die Bilder werden auch nach einander gemalt, jedoch kommt es manchmal zu Bildstörungen zur Laufzet, liegt wahrscheinlich an der NPE.

Blöde Spriteklasse 😛
 
Hi nochmal,

nach einem erneuten stack trace kam heraus, dass "pics" und "aa" null sind, was ich aber nicht ganz verstehe. Vielleicht kann mir dazu jemand meinen Fehler aufzeigen.

Gelöst habe ich es dann erst einmal grob und unschön so:
Java:
@Override
public void paintComponent(Graphics g) {
	super.paintComponent(g);
	
	BufferedImage[] u;
	u = sprite.loadPics("pics/Sprite1.png", 4);
	sprite.setPics(u);
	
	sprite.draw(g);
}

und mein Sprite-Konstruktor sind so aus:
Java:
public Sprite(String path, int x, int y, long delay, int count) {
	this.setX(x);
	this.setY(y);
	this.delay = delay;
	currentPic = 0;
}
"path" und "count einfach nicht beachten, sind nun unwichtig.
Also: Davor hatte ich im Konstruktor einem BufferedImage[] Array mit loadPics(...) ein gültiges Array zugewiesen, und dieses dann mit setPics() an ein neues Array "pics" übergeben. Beide sind doch somit initialisiert, oder?
In meiner draw() Methode hat sich nichts geändert, ich zeichne immernoch das Array this.pics mit dem Inhalt von "pics".

Jetzt habe ich den Zuweisungskram nicht mehr im Konstruktor, sondern direkt vor dem eigentlichen Zeichnen.

Bestimmt ist das ein blöder Fehler, bitte aber um Erklärung.
Danke
 
Hi nochmal,

konnte den Fehler nun entgültig identifizieren!

Java:
BufferedImage[] getPics()

Den eigenlichen Methodenaufruf musste ich noch auf null prüfen, obwohl eigentlich schon alles gültig initialisiert wurde.
 
in paintComponent (so spät und ständig wiederholt) solltest du keine Bilder von der Festplatte laden
 

Neue Themen


Zurück
Oben