简体   繁体   English

如何在Swing中创建“游戏选项/暂停屏幕”?

[英]How to create a “Game Option/Pause Screen” in Swing?

Background: Making a game in Swing. 背景:在Swing中制作游戏。 It is simple turn base game. 这是简单的转弯基础游戏。 Not a whole lot going on. 不是很多。 Because of this I didn't think I would need to implement a Game Tick. 因此我不认为我需要实现Game Tick。 Rather, my thought was when a component got changed or needed to be updated just simply revalidate/repaint that component on the fly rather than repainting the whole screen. 相反,我的想法是当一个组件被更改或需要更新时,只需简单地重新验证/重新绘制该组件而不是重新绘制整个屏幕。

I have a GameJPanel which currently has all the components on it. 我有一个GameJPanel,它目前有所有组件。 This JPanel is the one that contains the components that get revalidated/repainted etc. I figured I could make JLayeredPane that holds GameJPanel and my OptionJPanel. 这个JPanel是包含重新验证/重新绘制的组件等。我想我可以制作JLayeredPane,它包含GameJPanel和我的OptionJPanel。 Have a Button on GameJPanel that when pressed causes the OptionJPanel to show on top of it and having its JPanel 50% transparent (so it gives the affect it dims the GameJPanel). 在GameJPanel上有一个按钮,当按下该按钮会导致OptionJPanel显示在它上面并使其JPanel 50%透明(因此它给它带来影响它使GameJPanel变暗)。

However, once I did this what happened was that the GameJPanel started to replace OptionJPanel components (because of the events... etc; repainting of the components). 然而,一旦我这样做,发生的事情是GameJPanel开始替换OptionJPanel组件(因为事件......等;重新绘制组件)。

So currently I am at a loss on what to do. 所以目前我不知道该怎么做。 I'm thinking if I had some sort of game tick I wouldn't be having this issue, however, I am not 100% certain. 我在想,如果我有某种游戏方式,我就不会遇到这个问题,但是,我并非100%肯定。 I'm a little worried if I implemented a gametick that the events in game will cause the GameJPanel components to show through for half a second then get replaced. 我有点担心,如果我实施了一个游戏技巧,游戏中的事件将导致GameJPanel组件显示半秒然后被替换。 There are some events that cause components to repaint themselves without manually doing it (like quick example for JLabel setText();) 有些事件会导致组件重新绘制而无需手动执行(如JLabel setText()的快速示例;)

在此输入图像描述 As an example of what I'm trying to go for. 作为我想要的例子。

I have tried with a CardLayout but I couldn't figure out how to have the OptionJPanel be on top of GameJPanel while seeing GameJPanel in the background (I tried setting background color, setOpaque(false)..., tried to limit Option JPanel size but I think the CardLayout stretches it (not 100% sure)) all I got was a gray background when doing so. 我尝试过使用CardLayout,但我无法弄清楚如何在后台看到GameJPanel时将OptionJPanel置于GameJPanel之上(我尝试设置背景颜色,setOpaque(false)...,试图限制Option JPanel大小但我认为CardLayout延伸它(不是100%肯定))我所得到的只是灰色背景。

I would prefer not to go the CardLayout route because in the future I also plan on placing components on top of the GameJPanel (like someone clicks a button, have another panel on a different layer have a component slide in or out etc). 我宁愿不去CardLayout路线,因为将来我还计划在GameJPanel上放置组件(就像有人点击按钮,在另一层上有另一个面板有一个组件滑入或滑出等)。 I use CardLayout a ton with my other components in GameJPanel to swap screens around, but haven't had the need to have the other components behind the one showing to show through. 我使用CardLayout和GameJPanel中的其他组件来交换屏幕,但是没有必要让显示的其他组件显示出来。

Any ideas on how to go about this would be great or even example code that shows this. 关于如何解决这个问题的任何想法都很棒,甚至是显示这一点的示例代码。

As noted above, you would use a JDialog, a component that is easy to make (similar to making a JFrame) and easy to place. 如上所述,您将使用JDialog,一个易于制作的组件(类似于制作JFrame)并且易于放置。 Simply place it "relative-to" the JFrame, eg, 只需将其“相对于”JFrame,例如,

myDialog.setLocationRelativeTo(myJFrame);

... and it will automatically center itself on the JFrame. ...它会自动将其自身置于JFrame上。 The tricky part is dimming the underlying JFrame, and for this you would need to use a JGlassPane added to the JFrame's rootpane, one set with a background color that uses an alpha composite value. 棘手的部分是调暗底层的JFrame,为此你需要使用添加到JFrame的rootpane的JGlassPane,一组使用背景颜色使用alpha复合值。 The tricky part with this is to draw the darker background without causing side effects, and to do this, please read Rob Camick's (StackOverflow user camickr ) excellent tutorial on drawing in Swing with alpha composites which you can find here: Java Tips Weblog: Backgrounds with Transparency 这个棘手的部分是绘制较暗的背景而不会产生副作用,为此,请阅读Rob Camick(StackOverflow用户camickr )关于使用alpha合成的Swing绘图的优秀教程,您可以在此处找到: Java Tips Weblog:Backgrounds透明度

An example of such a program is shown here: 此处显示了此类程序的示例:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.*;

public class DialogEg {
    // path to example image used as "game" background
    private static final String IMG_PATH = "https://upload.wikimedia.org/"
            + "wikipedia/commons/7/76/Jump_%27n_Bump.png";

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        // get the "game" background image, or exit if fail
        BufferedImage img = null;
        try {
            URL imgUrl = new URL(IMG_PATH);
            img = ImageIO.read(imgUrl);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }

        // pass "game" image into main JPanel so that it will be drawn
        DeMainPanel mainPanel = new DeMainPanel(img);
        JFrame frame = new JFrame("Dialog Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(mainPanel); // add main JPanel to JFrame
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
}

// main JPanel
@SuppressWarnings("serial")
class DeMainPanel extends JPanel {
    private BufferedImage img; // background image

    // JButton action that shows the JDialog and darkens the glasspane
    private PauseAction pauseAction = new PauseAction("Pause");

    public DeMainPanel(BufferedImage img) {
        super();
        this.img = img;
        add(new JButton(pauseAction));
    }

    // draw the "game" background image within the JPanel if not null
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (img != null) {
            g.drawImage(img, 0, 0, this);
        }
    }

    // size this JPanel to match the image's size
    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet() || img == null) {
            return super.getPreferredSize();
        }
        int width = img.getWidth();
        int height = img.getHeight();
        return new Dimension(width, height);
    }
}

// Action / ActionListener for JButton -- shows JDialog and darkens glasspane
@SuppressWarnings("serial")
class PauseAction extends AbstractAction {
    private static final int ALPHA = 175; // how much see-thru. 0 to 255
    private static final Color GP_BG = new Color(0, 0, 0, ALPHA);
    private DeDialogPanel deDialogPanel = new DeDialogPanel();  // jpanel shown in JDialog

    public PauseAction(String name) {
        super(name);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // comp is our JButton
        Component comp = (Component) e.getSource();
        if (comp == null) {
            return;
        }

        // create our glass pane
        JPanel glassPane = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                // magic to make it dark without side-effects
                g.setColor(getBackground());
                g.fillRect(0, 0, getWidth(), getHeight());
                super.paintComponent(g);
            }
        };
        // more magic below
        glassPane.setOpaque(false); 
        glassPane.setBackground(GP_BG);     

        // get the rootpane container, here the JFrame, that holds the JButton
        RootPaneContainer win = (RootPaneContainer) SwingUtilities.getWindowAncestor(comp);
        win.setGlassPane(glassPane);  // set the glass pane
        glassPane.setVisible(true);  // and show the glass pane

        // create a *modal* JDialog
        JDialog dialog = new JDialog((Window)win, "", ModalityType.APPLICATION_MODAL);
        dialog.getContentPane().add(deDialogPanel);  // add its JPanel to it
        dialog.setUndecorated(true); // give it no borders (if desired)
        dialog.pack(); // size it
        dialog.setLocationRelativeTo((Window) win); // ** Center it over the JFrame **
        dialog.setVisible(true);  // display it, pausing the GUI below it

        // at this point the dialog is no longer visible, so get rid of glass pane
        glassPane.setVisible(false);

    }
}

// JPanel shown in the modal JDialog above
@SuppressWarnings("serial")
class DeDialogPanel extends JPanel {
    private static final Color BG = new Color(123, 63, 0);

    public DeDialogPanel() {
        JLabel pausedLabel = new JLabel("PAUSED");
        pausedLabel.setForeground(Color.ORANGE);
        JPanel pausedPanel = new JPanel();
        pausedPanel.setOpaque(false);
        pausedPanel.add(pausedLabel);

        setBackground(BG);
        int eb = 15;
        setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
        setLayout(new GridLayout(0, 1, 10, 10));
        add(pausedPanel);
        add(new JButton(new FooAction("RESUME")));
        add(new JButton(new FooAction("RESTART")));
        add(new JButton(new FooAction("EXIT TO MAP")));
    }

    // simple action -- all it does is to make the dialog no longer visible
    private class FooAction extends AbstractAction {
        public FooAction(String name) {
            super(name);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Component comp = (Component) e.getSource();
            Window win = SwingUtilities.getWindowAncestor(comp);
            win.dispose();  // here -- dispose of the JDialog
        }
    }
}

The GUI looks like this initially: GUI最初看起来像这样:

在此输入图像描述

but then when the dialog shows and the glass pane is darkened, it looks like this: 但是当对话框显示并且玻璃窗格变暗时,它看起来像这样:

在此输入图像描述

So after about a month of working on my game I was drawn to this post once again. 所以经过大约一个月的游戏工作后,我又被这个帖子吸引了。 I implemented part of my game with what DontKnowMuchButGettingBetter's way and also implemented this by just adding the components to the GlassPane so to speak (Made a JPanel, set it to be GlassPane, did whatever on that Panel)... 我用DontKnowMuchButGettingBetter的方式实现了游戏的一部分,并且通过将组件添加到GlassPane来实现这一点(可以说是制作JPanel,将其设置为GlassPane,在该Panel上做了什么)......

The later implementation (GlassPane), isn't the best way to go about this because then you can't use the glass pane for other useful things. 后面的实现(GlassPane)并不是解决此问题的最佳方法,因为您无法将玻璃窗格用于其他有用的事情。

I came back to my original idea to use a JLayeredPane. 我回到原来的想法,使用JLayeredPane。 Having different Components on different levels and working off that. 在不同的级别上拥有不同的组件并解决这个问题。 My issue before was that when components were getting repainted, the components in the backer layers were over painting the ones in the front layer. 我之前的问题是,当组件重新绘制时,支持层中的组件过度绘制了前层中的组件。

Well I just came across a method called isOptimizedDrawingEnabled()... By making this method always return false for the JLayeredPane I was able to achieve what I wanted. 好吧,我刚刚遇到一个名为isOptimizedDrawingEnabled()的方法......通过使这个方法总是为JLayeredPane返回false我能够实现我想要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM