Exception bei BufferStrategy

skappler

Aktives Mitglied
Hallo,
ich bin gerade dabei mit einem Tutorial eine Art 2D Spiele Engine zu bauen.
Ich habe den gesamten Code wie im Tutorial abgetippt, aber erhalte eine Exception:

Exception in thread "Thread-3" java.lang.IllegalStateException: Component must have a valid peer
at java.awt.Component$FlipBufferStrategy.createBuffers(Component.java:3843)
at java.awt.Component$FlipBufferStrategy.<init>(Component.java:3817)
at java.awt.Component$FlipSubRegionBufferStrategy.<init>(Component.java:4358)
at java.awt.Component.createBufferStrategy(Component.java:3699)
at java.awt.Canvas.createBufferStrategy(Canvas.java:166)
at java.awt.Component.createBufferStrategy(Component.java:3623)
at java.awt.Canvas.createBufferStrategy(Canvas.java:141)
at spritesheet.State.render(State.java:60)
at spritesheet.MenuState.render(MenuState.java:26)
at spritesheet.State.run(State.java:79)
at java.lang.Thread.run(Thread.java:680)


Das ganze Konstrukt ist relativ komplex deshalb ist es einiges an Code.
Main.java
Java:
package spritesheet;

import java.awt.Graphics;
import javax.swing.JFrame;

public class Main extends JFrame {
    
    public static final int GAME_WIDTH = 800, GAME_HEIGHT = 600;
    
    StateManager sm;
    
    public Main(){
        init();
        setSize(GAME_WIDTH, GAME_HEIGHT);
        setVisible(true);
        setResizable(false);
        setTitle("Canvas");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }
    
    private void init(){
        sm = new StateManager(this);
        sm.setCurrentState("MenuState");
        sm.startCurrentState();
    }
    
    
    
    public static void main(String[] args) {
        Main main = new Main();
    }
}

State.java
Java:
package spritesheet;

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;


public class State extends Canvas implements Runnable {
	
	public InputHandler inputManager;
    
    private volatile boolean running = false;
    
    private long ticks = 0;
    
    
    public BufferedImage screen = new BufferedImage(Main.GAME_WIDTH, Main.GAME_HEIGHT, BufferedImage.TYPE_INT_RGB);
    private Graphics2D g = screen.createGraphics();
    
    public String name;
        
    public State(String s){
    	
        name = s;
        inputManager = new InputHandler(this);
        setVisible(true);
    }
    
    public void start(){
        if(!running){
            running = true;
            new Thread(this).start();
        }
    }
    
    public void stop(){
        if(running){
            running = !running;
        }
    }
    
    public Graphics2D getGraphics2D(){
        return g;
    }
    
    public boolean isRunning() {
        return running;
    }

    public long getTicks() {
        return ticks;
    }
    
    public void render(){
        BufferStrategy b = getBufferStrategy();
        if(b == null){
            createBufferStrategy(3);

            return; 
        }
        Graphics g = b.getDrawGraphics();
        g.drawImage(screen, 0, 0, Main.GAME_WIDTH, Main.GAME_HEIGHT, this);
        g.dispose();
        b.show();
    }
    
    public void update(){
        ticks++;
    }
    
    @Override
    public void run() {
        while(running){
            
            update();
            render();
            
        }
    }
    
}

MenuState.java
Java:
package spritesheet;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;


public class MenuState extends State {
	
	int x = 100, y = 100;
    
    public MenuState(){
        super("MenuState");
        inputManager.addMapping("Up", KeyEvent.VK_UP);
        inputManager.addMapping("Down", KeyEvent.VK_DOWN);
        inputManager.addMapping("Left", KeyEvent.VK_LEFT);
        inputManager.addMapping("Right", KeyEvent.VK_RIGHT);
    }
    
    @Override
    public void render(){
        Graphics2D g = getGraphics2D();
        g.setColor(Color.red);
        g.fillRect(x, y, 50, 50);
        g.dispose();
        super.render();

    }
    
    @Override
    public void update(){
    	 super.update();
    	if(inputManager.isPressed("Up"))
    		y--;
    	if(inputManager.isPressed("Down"))
    		y++;
    	if(inputManager.isPressed("Left"))
    		x--;
    	if(inputManager.isPressed("Right"))
    		x++;
       
    }
    
}

StateManager.java
Java:
package spritesheet;

import java.util.ArrayList;
import javax.swing.JFrame;


public class StateManager {
    
    private ArrayList<State> states;
    
    private int currentState;
    
    private JFrame gameFrame;
    
    public StateManager(JFrame j){
        gameFrame = j;
        states = new ArrayList<State>();
        initAllStates();
    }
    
    private void initAllStates(){
        //TODO Add states
        states.add(new MenuState());
    }
    
    public void startCurrentState(){
    	
        states.get(currentState).start();
        gameFrame.add(states.get(currentState));

    }
    
    public void stopCurrentState(){
        gameFrame.remove(states.get(currentState));
        states.get(currentState).stop();
    }
    
    public void setCurrentState(String s){
        for(int i = 0; i < states.size(); i++){
            if(s.equals(states.get(i).name)){
                currentState = i;
            }
        }
    }
    
}

Auf den InputHandler habe ich verzichtet, da er für das Problem unrelevant ist.

Das einzige, was ich anders gemacht habe als im Tutorial ist, dass ich den init(); Aufruf in der Main Klasse ganz oben im Konstruktor habe. Im Tutorial war er ganz unten, dann hat mein Code aber nicht funktioniert. (Wobei ich hier auch nicht weiß warum, da im Tutorial alles geklappt hat)

Ich hoffe mir kann jemand helfen.

Gruß skappler
 
An der Position von init() wird es aber vermutlich liegen. Dadurch das init() vor setVisible(true) aufgerufen wird, versucht der State zu zeichnen bevor was zum Zeichnen da ist.
Was heißt denn, dass dein Code nicht funktioniert, wenn init ganz unten aufgerufen wird?
 
Als ich die Klassen geschrieben habe hatte ich zum testen ein Rotes Rechteck und einen Text ausgegeben.
Wenn init(); ganz unten stand wurde einfach nichts angezeigt. Der Bildschirm blieb leer.
Das Problem habe ich beseitigt indem ich init(); nach oben geschrieben habe.
Dann habe ich den InputHandler implementiert und den String der gezeichnet wieder entfernt und seitdem kommt dieser Fehler erst überhaupt. Wenn ich jetzt init() wieder nach unten schreibe bleibt der Fehler gleich.
 
So weit ich weiß und auch grade nochmal gelesen hab tritt die Exception auf, falls die entsprechende Component keinen sichtbaren Parent hat.

Du könntest also nochmal versuchen das hier so zu machen:

Java:
    public void startCurrentState(){
        gameFrame.add(states.get(currentState));
        states.get(currentState).start();       
 
    }

Damit das Canvas aufjedenfall geaddet ist bevor das Rendering losgeht.
 
Ok hab das jetzt geändert. Vielen Dank schonmal.
Wenn ich jetzt init() oben im Konstruktor habe bleibt die Fehlermeldung gleich, wenn init() ganz unten hinschreibe kommt zwar keine Exception, aber der Bildschirm bleibt grau und nichts wird gezeichnet.

EDIT: Ok das hab ich gelöst. ich hab init() direkt unter setVisible(true) geschrieben.
Ein Problem bleibt aber. Der Bildschirm wird nicht neu gezeichnet. Wenn ich die Pfeiltasten drücke werden die Koordinaten zwar korrekt verändert (das habe ich mit System.out gecheckt), aber der Würfel wird nicht neu gezeichnet
 
Zuletzt bearbeitet:
Du disposed im render() vom MenuState das Graphics vom BufferedImage und machst es nie wieder auf.
Entweder du lässt es offen oder erstellst es neu. Wie auch immer du vorgehst, du musst auch das Image noch leer machen, sonst bleiben die alten Rechtecke da(sieht aber lustig aus 😀 )
 
Stimmt das dispose hatte ich eingefügt um was zu probieren 😛
Das mit dem löschen kommt als nächstes im Tutorial also das passt so^^

Ich hab grad erst mit Grafikprogrammierung angefangen un blick da noch nicht ganz so durch^^

Vielen Dank auf jeden fall für die Hilfe =)

PS: Noch eins: kann ich einstellen, dass das Fenster direkt fokusiert ist, sobald ich das Programm starte? Weil im Moment muss ich immer zuerst in die Zeichenfläche klicken, bevor ich das Rechteck bewegen kann.
 
Kein Problem 🙂 Dann einfach viel rumprobieren, dadurch lernt man immer am besten. Das Fenster ist direkt fokussiert denk ich, du musst aber das Canvas/den State fokussieren, weil der InputManager das benutzt.
Die entsprechende Methode heißt requestFocus. Wo du das einbauen musst lass ich dich mal selber ausprobieren 😉
 

Zurück
Oben