JButton 仅在悬停后出现,同时使用 CardLayout 处理场景

问题描述 投票:0回答:1

我正在尝试用 Java 创建一个简单的游戏,但我一直坚持创建一个系统来处理不同的场景以及它们之间的过渡。

这些是我的课程:

使用 cardLayout 控制当前显示什么场景的场景管理器。

import javax.swing.*;
import java.awt.*;

public class SceneManager extends JFrame{
    private static SceneManager single_instance = null;

    public final CardLayout cardLayout;
    private final Scene mainMenuScene;
    private final Scene gameScene;

    private SceneManager() {
        super("My Game");
        setSize(new Dimension(1000,350));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        cardLayout = new CardLayout();
        this.setLayout(cardLayout);

        mainMenuScene = new MainMenuScene(this);
        mainMenuScene.setName("mainMenuScene");
        this.add(mainMenuScene, "mainMenuScene");

        gameScene = new GameScene(this);
        gameScene.setName("gameScene");
        this.add(gameScene, "gameScene");

        setVisible(true);
    }

    public static synchronized SceneManager getInstance()
    {
        if (single_instance == null)
            single_instance = new SceneManager();

        return single_instance;
    }

    public void switchToMainMenu() {
        cardLayout.show(getContentPane(), "mainMenuScene");
    }

    public void switchToGame() {
        cardLayout.show(getContentPane(), "gameScene");
    }

    public static void main(String[] args) {
        SceneManager sc = SceneManager.getInstance();
    }
}

Scene 父类作为所有场景的模板。它使用线程游戏循环系统,因为我想即使是看似静态的场景也有动画,比如主菜单。但是,我不知道我这样做是否正确,因为这是我第一次使用线程和一般的 java。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

public abstract class Scene extends JPanel implements Runnable{
    private boolean isPaused = true;
    private int FPS;
    protected SceneManager sceneManager;

    private Thread thread;

    public Scene(SceneManager sceneManager){
        this.FPS = 30;

        this.sceneManager = sceneManager;

        this.setPreferredSize(new Dimension(1000, 350));
        this.setBackground(Color.black);
        this.setDoubleBuffered(true);
        this.setFocusable(true);
        this.setVisible(true);
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentHidden(ComponentEvent evt) {
                pause();
            }
            @Override
            public void componentShown(ComponentEvent evt) {
                resume();
            }
        });

        startThread();
    }

    public void startThread(){
        this.thread = new Thread(this);
        this.thread.start();
    }

    public void pause() {
        this.isPaused = true;
    }

    public void resume() {
        this.isPaused = false;
    }

    public boolean isPaused(){
        return isPaused;
    }

    @Override
    public void run() {
        double drawInterval = 1_000_000_000. / FPS;
        double delta = 0;
        long lastTime = System.nanoTime();
        long currentTime;
        long timer = 0;

        while (thread != null){
            currentTime = System.nanoTime();
            delta += (currentTime - lastTime) / drawInterval;
            timer += (currentTime - lastTime);
            lastTime = currentTime;

            if (delta >= 1) {
                // Only update and repaint if the game is not paused
                if(!isPaused) {
                    repaint();
                    update();
                }
                delta --;
            }
            if(timer >= 1_000_000_000){
                timer = 0;
            }
        }
    }

    public abstract void update();

    public abstract void draw(Graphics2D g2d);

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        draw(g2d);

        g2d.dispose();
    }
}

然后是两个场景:

主菜单:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class MainMenuScene extends Scene {
    private JButton button;

    public MainMenuScene(SceneManager sceneManager) {
        super(sceneManager);
        button = new JButton("Click Me");
        button.addActionListener(this::onButtonClick);
        button.setLocation(100,100);
        button.setSize(200,100);
        this.add(button, "button1");
        this.validate();
        this.setLayout(new BorderLayout());
    }

    public void update() {}

    public void draw(Graphics2D g2d) {}

    private void onButtonClick(ActionEvent event) {
        sceneManager.switchToGame();
    }
}

以及游戏场景:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class GameScene extends Scene {

    private int x;

    JButton button;

    public GameScene(SceneManager sceneManager) {
        super(sceneManager);
        button = new JButton("Click Me");
        button.addActionListener(this::onButtonClick);
        button.setLocation(100,100);
        button.setSize(200,100);
        this.add(button, "button1");
        this.validate();
        this.setLayout(new BorderLayout());
    }

    public void update(){
        x += 2;
    }

    public void draw(Graphics2D g2d) {
        g2d.fillRect(x,50,200,200);
    }


    private void onButtonClick(ActionEvent event) {
        sceneManager.switchToMainMenu();
    }
}

我遇到的问题是,当我启动程序时,按钮是不可见的,直到我将鼠标悬停在它上面。然后,如果我调用 resume(),按钮在悬停时会闪烁(当我触摸它时它变得可见,然后下一个“帧”它再次变得不可见,直到我再次移动鼠标)。我不知道是什么导致了这种行为,所以任何帮助将不胜感激。

P.S.:我发现的一件事是,当我将 mainMenu 的布局设置为 SceneManager 的 cardLayout(我调用 this.setLayout(sceneManager.cardLayout); 在 MainMenu 构造函数中)时,按钮从开始直接显示,但它覆盖了整个窗口,而不是指定的大小。

P.P.S:我还尝试将 revalidate() 放在代码中的各种位置(update() 方法、菜单构造函数,两者……),但没有成功。可能是因为我不太确定它的作用,所以我可能一直在错误地使用它。

java swing layout jbutton
1个回答
0
投票

问题是在Scene类的PaintComponent()方法中调用了g2d.dispose()引起的。 kleopatra 在评论中发现了这个错误。

© www.soinside.com 2019 - 2024. All rights reserved.