Java 无法呈现菜单状态(Java GUI)

Posted

技术标签:

【中文标题】Java 无法呈现菜单状态(Java GUI)【英文标题】:Java cannot render Menu state (Java GUI) 【发布时间】:2021-08-11 17:30:06 【问题描述】:

请阅读下方的最新更新

所以我正在尝试制作一个砖块破坏游戏,虽然在游戏玩法方面一切正常,但我在添加菜单系统时遇到了麻烦。基本上这就是我的代码的工作方式

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();
 

当我渲染 STATE GAME 中的所有内容时,它工作得很好,但是当我尝试添加执行 menu.draw(g) 的 else if 语句时,一切都崩溃了,我只是得到一个空白帧

这是我的渲染方式

 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();
  

而我的Menu类在draw方法方面没有区别

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);
  
  


知道为什么会发生这种情况,我正在做同样的渲染,但不断得到 线程“Thread-0”java.lang.ClassCastException 中的异常:无法将类 sun.java2d.NullSurfaceData 转换为类 sun.java2d.d3d.D3DSurfaceData 错误或 g 为 null 错误

我该如何解决这个问题?

更新 ----------------------------------

当我删除线条时,渲染中的 menu.draw() 工作

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

而是添加类似的东西

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

这确实有效,但为什么 setfont、setColor 和 drawString 不起作用我不明白我也想添加按钮,但它们也不起作用。是不是因为我在渲染中使用g.fillRect(0, 0, WIDTH, HEIGHT); 行设置了整个框架,但是我可以添加像桨、球、砖这样的对象,但不能添加字符串或按钮?

【问题讨论】:

我的第一直觉是你应该评估“Font("arial", Font.BOLD, 50)”。由于“arial”似乎不是一个有效的字体名称,这可能导致没有文本被呈现 我还建议查看BufferStrategy JavaDocs,因为他们有一个很好的例子来说明如何使用 api @MadProgrammer 我删除了字体行仍然没有生命迹象 :( 感谢您建议文档,我将尝试像那里那样的循环结构。我的直觉是它与@有关987654322@ 但不明白,也许我会为菜单等创建新框架,然后如果游戏实际开始并且我们需要移动东西,就使用线程 难道我不使用可运行类中的 ticks 方法,也许我应该更改运行工作,以便刻度也必须在那里渲染它? @MadProgrammer minimal reproducible example 只是猜测工作 【参考方案1】:

以下示例似乎运行良好。

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());
        

    

如果您仍然有问题,请继续提供一个可运行的示例来演示您的问题

【讨论】:

以上是关于Java 无法呈现菜单状态(Java GUI)的主要内容,如果未能解决你的问题,请参考以下文章

Python Qt GUI设计:菜单栏工具栏和状态栏的使用方法(拓展篇—2)

如何将Java ChangeListener添加到方法返回

生命游戏 Java

有限状态机(FSM)的Java 演示

有限状态机(FSM)的Java 演示

热点处于活动状态时无法关闭模态呈现的 AVPlayerViewController