0

I am a noob in java and am trying to make a kind of text adventure game. I want to be able to have the program have some kind of fade ability as it transitions from one layout of the UI to another.

I really have no idea what the best approach to this problem would be or if its really even feasible, but I have so far been trying to have a Jpanel that covers the entire window and uses a timer to fade in to cover everything else in black, or fades out from black to transparency thereby revealing everything underneath.

I have been testing this idea by trying to fade in/out the program at the start just to get the logic for the fade system working before trying to have it as a transition effect. The fade-out kind of works, but I have the program output the alpha level and the screen is turning black at around alpha 50 out of 255 which is confusing me. The fade-in does not work at all.

Here is the code for the fade method:

static int opacityCounter = 0;

public void fadeOut(JPanel frame){
    System.out.println(opacityCounter);
    opacityCounter = 0;
    fadeTimer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            frame.setBackground(new Color(0,0,0,opacityCounter));
            opacityCounter++;
            gui.window.add(frame);

            if(opacityCounter >= 255){
                opacityCounter = 255;
                fadeTimer.stop();
            }
            System.out.println(opacityCounter);
        }
    });
    fadeTimer.start();
}

This is the code where the "fadePanel" that covers the window is created and deployed in the method.

    fadeScreen = new JPanel();
    fadeScreen.setBounds(0,0,800,600);
    fadeScreen.setBackground(Color.black);
    window.add(fadeScreen);

    game.visibilityManager.fadeOut(this.fadeScreen);

To clarify I want something that goes from a UI layout like this:enter image description here

fades to black, before fading back to a UI that looks like this enter image description here

This is a minimal reproducible example:

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

public class Test {
JFrame window;
JPanel fadeScreen, screen1, screen2;
JLabel text1, text2;
Timer fadeTimer;

public Test(){

    //Frame Window
    window = new JFrame();
    window.setSize(800,600);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.getContentPane().setBackground(Color.blue);

    //Screen 1
    screen1 = new JPanel();
    screen1.setBounds(100, 100, 600, 125);
    screen1.setBackground(Color.white);
    text1 = new JLabel("Text1");
    screen1.add(text1);
    window.add(screen1);

    //Screen 2
    screen2 = new JPanel();
    screen2.setBounds(100, 400, 600, 125);
    screen2.setBackground(Color.white);
    text2 = new JLabel("Text2");
    screen2.add(text2);
    window.add(screen2);

    //Cover Panel
    fadeScreen = new JPanel();
    fadeScreen.setBounds(0,0,800,600);
    fadeScreen.setBackground(Color.black);
    window.add(fadeScreen);

    window.setVisible(true);


    //Comment out which method you don't want to use
    fadeOut(this.fadeScreen);
    //fadeIn(this.fadeScreen);
}


//Fade methods
int opacityCounter = 0;

public void fadeOut(JPanel frame){
    System.out.println(opacityCounter);
    opacityCounter = 0;
    fadeTimer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            frame.setBackground(new Color(0,0,0,opacityCounter));
            opacityCounter++;
            window.add(frame);

            if(opacityCounter >= 255){
                opacityCounter = 255;
                fadeTimer.stop();
            }
            System.out.println(opacityCounter);
        }
    });
    fadeTimer.start();
}

public void fadeIn(JPanel frame){
    System.out.println(opacityCounter);
    opacityCounter = 255;
    fadeTimer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            frame.setBackground(new Color(0,0,0,opacityCounter));
            opacityCounter--;
            window.add(frame);

            if(opacityCounter <= 0){
                opacityCounter = 0;
                fadeTimer.stop();
            }
            System.out.println(opacityCounter);
        }
    });
    fadeTimer.start();
}

public static void main(String[] args){
    new Test();
}
}

Thanks in advance!

  • The foreground (text) color of a label can be set to a translucent color. Try cycling that down to transparent, changing the text then bringing it back to fully opaque. If that does not work for you, post a [mre] of the attempt. – Andrew Thompson Jan 07 '22 at 07:39
  • My apologies, my pictures didn't reveal the whole screen but just the text area, as they were some images I already had on hand. The whole screen consists of more than just a textarea so im not sure if this would work – Benjamin Keninger Jan 07 '22 at 08:12
  • *"my pictures didn't reveal the whole screen"* Post a [mre]. Hotlink to images as might be found in [this answer](https://stackoverflow.com/a/19209651/418556). – Andrew Thompson Jan 07 '22 at 08:23
  • Sorry, I'm still a beginner on this site. I have posted a minimal reproducible example that demonstrates the fade-out method semi-working and the fade-in not working at all. – Benjamin Keninger Jan 07 '22 at 09:23

2 Answers2

1

I would try these things:

  • Use the GlassPane of the top-level window and make a section of it darker where you want to cover things up, using a Swing Timer.
  • Use a CardLayout to swap the underlying components, and make the swap when the covering JPanel is darkest.
  • Then undarken the covering panel after the swap.

For example (to write more code explanation later):

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class Test2 extends JPanel {
    public static final String PANEL_1 = "panel 1";
    public static final String PANEL_2 = "panel 2";
    private CardLayout cardLayout = new CardLayout();
    private JPanel cardPanel = new JPanel(cardLayout);
    private JPanel panel1 = new JPanel();
    private JPanel panel2 = new JPanel();
    private Action fadeAction = new FadeAction(cardPanel);
    
    public Test2() {
        JLabel label = new JLabel("Panel 1");
        label.setFont(label.getFont().deriveFont(Font.BOLD, 100f));
        panel1.setLayout(new GridBagLayout());
        int gap = 40;
        panel1.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
        panel1.add(label);
        panel1.setBackground(Color.PINK);
        
        label = new JLabel("Panel 2");
        label.setFont(label.getFont().deriveFont(Font.BOLD, 100f));
        panel2.setLayout(new GridBagLayout());
        panel2.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
        panel2.add(label);
        panel2.setBackground(new Color(131, 238, 255));
        
        
        cardPanel.add(panel1, PANEL_1);
        cardPanel.add(panel2, PANEL_2);
        
        JButton startFadeBtn = new JButton(fadeAction);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(startFadeBtn);
        
        setLayout(new BorderLayout(5, 5));
        add(cardPanel, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }
    
    private static class FadeAction extends AbstractAction {
        private static final int FADE_DELAY = 20;
        private static final int UNFADE_VALUE = 255;
        private JPanel cardPanel;
        private JComponent glassPane;
        private JPanel coverPanel = new JPanel();
        private Timer fadeTimer;
        private int counter = 0;
        private boolean fade = true;

        public FadeAction(JPanel cardPanel) {
            super("Start Fade");
            putValue(MNEMONIC_KEY, KeyEvent.VK_S);
            this.cardPanel = cardPanel;
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            counter = 0;
            fade = true;
            setEnabled(false);
            CardLayout cl = (CardLayout) cardPanel.getLayout();
            cl.show(cardPanel, PANEL_1);
            Window topLevelWindow = SwingUtilities.getWindowAncestor(cardPanel);
            glassPane = (JComponent) ((RootPaneContainer) topLevelWindow).getRootPane().getGlassPane();
            glassPane.setVisible(true);
            glassPane.setLayout(null);
            coverPanel.setSize(cardPanel.getSize());
            int x = cardPanel.getLocationOnScreen().x - glassPane.getLocationOnScreen().x;
            int y = cardPanel.getLocationOnScreen().y - glassPane.getLocationOnScreen().y;
            Point coverPanelPoint = new Point(x, y);
            coverPanel.setLocation(coverPanelPoint);
            glassPane.add(coverPanel);
            fadeTimer = new Timer(FADE_DELAY, e2 -> fadeTimerActionPerformed(e2));
            fadeTimer.start();
        }
        
        private void fadeTimerActionPerformed(ActionEvent e) {
            coverPanel.setBackground(new Color(0, 0, 0, counter));
            glassPane.repaint();
            if (fade) {
                counter++;
            } else if (counter > 0) {
                counter--;
            } else {
                glassPane.remove(coverPanel);
                glassPane.setVisible(false);
                setEnabled(true);
                ((Timer) e.getSource()).stop();
            }
            if (counter >= UNFADE_VALUE) {
                fade = false;
                CardLayout cl = (CardLayout) cardPanel.getLayout();
                cl.show(cardPanel, PANEL_2);
            }
        }
    }
    
    public static void main(String[] args) {        
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new Test2());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 1
    Setting the location of the `coverPanel` isn't necessary. The glass pane comes with location information. – Gilbert Le Blanc Jan 07 '22 at 16:05
  • I tinkered around with it to make it faster and it looks great! Would this mean that I would have to add everything in my program to the cardlayout? In my example in the post when it goes from the "title screen" of sorts to the next scene, there are a lot of UI changes. Would they all need to be added manually? What if different scene changes require different UI section swaps? Would this require different versions of the fade code? Sorry for the incessant questions, I'm still pretty new to swing and have never dealt with these "glasspanes" so I'm just trying to get a good understanding.Thanks – Benjamin Keninger Jan 07 '22 at 18:20
  • 2
    @Benjamin Keninger: Create the entire GUI as a set of `JPanels` in the `JPanel` with the `CardLayout`. Create the entire GUI before you do anything else. Don't try and change just part of the `CardLayout`. Oracle has a helpful tutorial, [Creating a GUI With Swing](https://docs.oracle.com/javase/tutorial/uiswing/index.html). Skip the Learning Swing with the NetBeans IDE section. – Gilbert Le Blanc Jan 07 '22 at 18:30
  • @GilbertLeBlanc: I wanted to cover only the CardLayout-using JPanel, the cardPanel, though and nothing else on the GUI, hence my use of a coverPanel – Hovercraft Full Of Eels Jan 07 '22 at 18:58
0

Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Concurrency in Swing and the Laying Out Components Within a Container sections.

This is Hovercraft Full Of Eels' answer. All I did was clean up the GUI creation and demonstrate how this would work with more than two JPanels.

I created five JPanels and displayed them in order with the fade-out/fade-in effect.

Here's the complete runnable code.

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class FadeEffectsTesting {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Fade Effects Testing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            frame.add(new FadeEffectsTesting().getMainPanel());

            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }

    public static final String[] PANEL_SEQUENCE = { "Panel 1", "Panel 2", "Panel 3", "Panel 4",
            "Panel 5" };
    
    private int sequence = 0;

    private CardLayout cardLayout;
    
    private FadeAction action;

    private JPanel cardPanel, mainPanel;

    public FadeEffectsTesting() {
        this.mainPanel = new JPanel(new BorderLayout(5, 5));
        this.cardPanel = createCardPanel();
        this.action = new FadeAction(cardPanel);

        mainPanel.add(cardPanel, BorderLayout.CENTER);
        mainPanel.add(createButtonPanel(), BorderLayout.PAGE_END);
    }

    private JPanel createCardPanel() {
        cardLayout = new CardLayout();
        JPanel panel = new JPanel(cardLayout);

        panel.add(createTextPanel(Color.PINK, PANEL_SEQUENCE[0]), 
                PANEL_SEQUENCE[0]);
        panel.add(createTextPanel(new Color(131, 238, 255), 
                PANEL_SEQUENCE[1]), PANEL_SEQUENCE[1]);
        panel.add(createTextPanel(Color.PINK, PANEL_SEQUENCE[2]), 
                PANEL_SEQUENCE[2]);
        panel.add(createTextPanel(new Color(131, 238, 255), 
                PANEL_SEQUENCE[3]), PANEL_SEQUENCE[3]);
        panel.add(createTextPanel(Color.PINK, PANEL_SEQUENCE[4]), 
                PANEL_SEQUENCE[4]);

        return panel;
    }

    private JPanel createTextPanel(Color color, String text) {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBackground(color);
        int gap = 40;
        panel.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));

        JLabel label = new JLabel(text);
        label.setFont(label.getFont().deriveFont(Font.BOLD, 72f));
        panel.add(label);

        return panel;
    }

    private JPanel createButtonPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        
        setFadeAction();
        JButton startFadeBtn = new JButton(action);
        panel.add(startFadeBtn);

        return panel;
    }
    
    public void setFadeAction() {
        action.setFromPanel(PANEL_SEQUENCE[sequence]);
        action.setToPanel(PANEL_SEQUENCE[sequence + 1]);
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }

    public class FadeAction extends AbstractAction {
        private static final long serialVersionUID = 1L;

        private static final int FADE_DELAY = 20;
        private static final int UNFADE_VALUE = 255;

        private JPanel cardPanel;
        private JComponent glassPane;
        private JPanel coverPanel;
        private Timer fadeTimer;
        private int alphaValue;
        private boolean fadeOut;
        private String fromPanel, toPanel;

        public FadeAction(JPanel cardPanel) {
            super("Start Fade");
            this.putValue(MNEMONIC_KEY, KeyEvent.VK_S);
            this.cardPanel = cardPanel;
            this.alphaValue = 0;
            this.fadeOut = true;
        }

        public void setFromPanel(String fromPanel) {
            this.fromPanel = fromPanel;
        }

        public void setToPanel(String toPanel) {
            this.toPanel = toPanel;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            alphaValue = 0;
            fadeOut = true;
            setEnabled(false);

            CardLayout cl = (CardLayout) cardPanel.getLayout();
            cl.show(cardPanel, fromPanel);
            Window topLevelWindow = SwingUtilities.getWindowAncestor(cardPanel);
            glassPane = (JComponent) ((RootPaneContainer) topLevelWindow).getRootPane()
                    .getGlassPane();
            glassPane.setLayout(null);
            coverPanel = new JPanel();
            coverPanel.setSize(cardPanel.getSize());
            glassPane.add(coverPanel);
            glassPane.setVisible(true);

            fadeTimer = new Timer(FADE_DELAY, e2 -> fadeTimerActionPerformed(e2));
            fadeTimer.start();
        }

        private void fadeTimerActionPerformed(ActionEvent event) {
            coverPanel.setBackground(new Color(0, 0, 0, alphaValue));
            glassPane.repaint();

            if (fadeOut) {
                alphaValue += 3;
            } else if (alphaValue > 0) {
                alphaValue -= 3;
            } else {
                glassPane.remove(coverPanel);
                glassPane.setVisible(false);
                ((Timer) event.getSource()).stop();
                if (++sequence < (PANEL_SEQUENCE.length - 1)) {
                    setFadeAction();
                    setEnabled(true);
                }
            }

            if (alphaValue >= UNFADE_VALUE) {
                fadeOut = false;
                CardLayout cl = (CardLayout) cardPanel.getLayout();
                cl.show(cardPanel, toPanel);
            }
        }
        
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • So in my case, I would make a coverPanel that would hold all the other panels of my program, and this coverPanel would use the cardlayout style, and the JFrame would hold this coverPanel? I already have the entire UI built using panels, so how would I add these all to this coverpanel without disturbing the UI's design. – Benjamin Keninger Jan 07 '22 at 23:44
  • @Benjamin Keninger: No. The `coverPanel` is **only** for the fade-out/fade-in effect . Nothing else. You add your `JPanels` to the `CardLayout` `JPanel`. – Gilbert Le Blanc Jan 08 '22 at 04:58
  • I see. I tried just simply adding all my panels to a cardlayout Panel and the UI got totally screwed up so I'm assuming I will have to redo the UI to make it look right again on the new cardlayout? Is there any chance that my suggested answer is possible? – Benjamin Keninger Jan 08 '22 at 08:35
  • @Benjamin Keninger: No, there's no chance that your answer is possible. Take some time and study the Oracle tutorials. The links are in the answer. – Gilbert Le Blanc Jan 08 '22 at 14:04