Search code examples
javaswinguser-interfaceawtclasscastexception

Java cannot render Menu state (Java GUI)


Please read the latest update down below

So I am trying to make a brick breaker game and while everything works in terms of gameplay I am having trouble adding a menu system. Basically this is how my code works

public class Game extends Canvas implements Runnable{
    public Graphics g;
    private Menu menu;
    protected Ball ball;
    protected Player player;
    protected BufferedImage image;
    protected BufferStrategy bufferStrategy;
    protected Thread thread;
    protected JFrame frame;
    protected volatile boolean running, gameOver;

private enum STATE{
  MENU,
  GAME,
  ABOUT,
  OPTIONS,
 };
 private STATE State = STATE.MENU;

public Game(){
   //Set the Jframe

}

private void init()
 {
  image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);   
  requestFocus(); 
  menu = new Menu();
  //init everything else aswell
  }
 public void update()
 {
   //Update every moving object 
 }

 @Override
 public void run()
 {
  init();
  
  long initialTime = System.nanoTime();
  double timePerFrame = 1000000000/FPS;
  double delta = 0;
  int ticks = 0;
  long timer = 0;
  while (running)
  {
     long currentTime = System.nanoTime();
     long elapsedTime = currentTime - initialTime;
     delta += elapsedTime/timePerFrame;
     timer += elapsedTime;

     if (delta >= 1)
     {
        update();
        delta--;
        ticks++;
     }

     render();
     initialTime = currentTime;

     if (timer >= 1000000000)
     {
        currentFPS = ticks;
        ticks = 0;
        timer = 0;
     }

  }

  stop();
 }

And when I render everything that is in the STATE GAME it works just fine but when I try to add an else if statement that does menu.draw(g) it all falls apart and I just get a blank frame

Here is how I render

 public void render()
  {
  bufferStrategy = getBufferStrategy();
  if (bufferStrategy == null)
  {
     createBufferStrategy(3);
     return;
  }

  g = bufferStrategy.getDrawGraphics();

  g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
  g.setColor(BG_COLOR);
  g.fillRect(0, 0, WIDTH, HEIGHT);
 
  if(State == STATE.GAME){
     player.draw(g);
     ball.draw(g);
     blockController.draw(g); **THESE WORK JUST FINE**
  }
  else if(State == STATE.MENU){
    
     menu.draw(g);  **DOES NOT WORK**

  }

  bufferStrategy.show();
  g.dispose();
  }

And my Menu class has no difference in terms of the draw method

public class Menu implements GUI
{
  @Override
  public void draw(Graphics g)  {

  g.setFont(new Font("arial", Font.BOLD, 50));
  g.setColor(Color.black);
  g.drawString("MENU", Game.WIDTH / 2, 100);
  
  }

}

Any idea why this might be happening I am doing the same render litteraly but keep getting Exception in thread "Thread-0" java.lang.ClassCastException: class sun.java2d.NullSurfaceData cannot be cast to class sun.java2d.d3d.D3DSurfaceData error or g is null error

How can I fix this?

UPDATE ----------------------------------

The menu.draw() in the render works when I remove the lines

g.setFont(new Font("arial", Font.BOLD, 50));
g.setColor(Color.black);
g.drawString("MENU", Game.WIDTH / 2, 100);

And instead add something like

  g.setColor(Color.CYAN);
  g.fillRect(5, 5, 200, 200);

This does work but why the setfont, setColor and drawString don't work I don't understand I wanted to add buttons aswell but they don't work either. Is it because I set the entire frame with a rectangle in the render with the line g.fillRect(0, 0, WIDTH, HEIGHT); but then can I add objects like paddle,ball,bricks but not a string or a button?


Solution

  • The following example seems to work fine.

    Menu

    import java.awt.Canvas;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.image.BufferStrategy;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends Canvas {
    
            private Thread thread;
            private volatile boolean render = false;
    
            public TestPane() {
            }
    
            @Override
            public void addNotify() {
                super.addNotify();
                start();
            }
    
            @Override
            public void removeNotify() {
                super.removeNotify();
                stop();
            }
    
            protected void start() {
                if (thread !=  null) {
                    render = false;
                    try {
                        thread.join();
                    } catch (InterruptedException ex) {
                    }
                }
                render = true;
                thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (render) {
                            render();
                            try {
                                Thread.sleep(16);
                            } catch (InterruptedException ex) {
                            }
                        }
                    }
                });
                thread.start();
            }
    
            protected void stop() {
                render = false;
                if (thread == null) {
                    return;
                }
                try {
                    thread.join();
                } catch (InterruptedException ex) {
                }
                thread = null;
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            private Menu menu = new Menu();
    
            protected void render() {
    
                BufferStrategy strategy = getBufferStrategy();
                if (strategy == null) {
                    createBufferStrategy(3);
                    strategy = getBufferStrategy();
                }
    
                if (strategy == null) {
                    return;
                }
    
                do {
                    // The following loop ensures that the contents of the drawing buffer
                    // are consistent in case the underlying surface was recreated
                    do {
                        // Get a new graphics context every time through the loop
                        // to make sure the strategy is validated
                        Graphics graphics = strategy.getDrawGraphics();
                        graphics.setColor(Color.BLACK);
                        graphics.fillRect(0, 0, getWidth(), getHeight());
    
                        menu.render(graphics, getSize());
    
                        graphics.dispose();
    
                        // Repeat the rendering if the drawing buffer contents
                        // were restored
                    } while (strategy.contentsRestored());
    
                    // Display the buffer
                    strategy.show();
    
                    // Repeat the rendering if the drawing buffer was lost
                } while (strategy.contentsLost());
            }
    
        }
    
        public class Menu {
    
            public void render(Graphics g, Dimension bounds) {
                // This is probably going to cost you a lot of
                // performance and it might be better to 
                // pre-create the font instead
                g.setFont(new Font("Arial", Font.BOLD, 50));
                g.setColor(Color.WHITE);
    
                String text = "MENU";
                FontMetrics fm = g.getFontMetrics();
    
                g.drawString("MENU", (bounds.width - fm.stringWidth(text)) / 2, (bounds.height / 2) - fm.getAscent());
            }
    
        }
    }
    

    If you continue to have issues, continue providing a runnable example which demonstrates your issue