JLabel and JLayeredPane - How to display an image over another image?

拜拜、爱过 提交于 2019-12-21 05:16:10

问题


I try to create a little game in java but I'm in trouble.

When I draw a map, I'm not able to display the characters without overwrite the titlesets of the squares.

My goal is to be able to display many pictures on the same square (like the titleset of the grass, the character and a tree), so I have to deal with the transparency of my pictures (this is not the problem) and the layer (it is the problem).

So how can I display an image on another image?

How can I explain to java that I need to display this image on or under another image?

This is my source code. I don't know is that can help you. Your help can be really helpfull for me if you give me a clue or a function who is able to manage the layers. That is useless to rewrite all the code for me x) This program is not complete, I use it only for test my program right now. I know that he refresh two times the map so he overwrite the square of the character (and he have many others littles glitchs), but that is not the purpose of my question. I try to done my game by step!

import javax.swing.JFrame;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;


public class Window extends Thread
{
    private static JFrame window = new JFrame("game");

    public void run()
    {
        Map map = new Map();
        Characters characters = new Characters();

        window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        window.setSize(Settings.sizeX, Settings.sizeY);
        window.setLocationRelativeTo(null);
        window.setResizable(false);
        window.setVisible(true);

        map.start();
        characters.start();
    }

    private static void reload() throws Exception
    {
        SwingUtilities.updateComponentTreeUI(window);
    }

    private static class Map extends Thread
    {
        private int numberSquareX = Settings.sizeX / 20 + 1;
        private int numberSquareY = Settings.sizeY / 20 + 1;
        private JLabel square[][] = new JLabel[numberSquareX][numberSquareY];

        public void run()
        {
            for (int x = 0, y = 0; y < numberSquareY; x++)
            {
                square[x][y] = new JLabel(new ImageIcon("grass_1.png"));
                square[x][y].setBounds(x * 20, y * 20, 20, 20);
                window.add(square[x][y]);
                if (x == numberSquareX - 1)
                {
                    y++;
                    x = -1;
                }
            }
            square[numberSquareX - 1][numberSquareY - 1] = new JLabel(new ImageIcon("grass_1.png"));
            square[numberSquareX - 1][numberSquareY - 1].setBounds(numberSquareX * 20, numberSquareY * 20, 20, 20);
            window.add(square[numberSquareX - 1][numberSquareY - 1]);
            try
            {
                reload();
            }
            catch (Exception e)
            {

            }
            return;
        }
    }

    private class Characters extends Thread
    {
        private JLabel square[][] = new JLabel[1][1];

        public void run()
        {
            square[0][0] = new JLabel(new ImageIcon("character_1.png"));
            square[0][0].setBounds(Test.posX, Test.posX, 20, 20);
            window.add(square[0][0]);
            try
            {
                reload();
            }
            catch (Exception e)
            {

            }
            return;
        }
    }
}

I have already find this subjects: How to use JLayered Pane to display an image on top of another image? and this one Best practice for creating a composite image output for Java Swing but they haven't really help me...

I continue to search the answer. If I find it, I will come back for post it here.


回答1:


Solved.

Thanks to MadProgrammer for his comments.

Render the title map to a BufferedImage, either in it's entirety or based on the available viewable area, which ever is more efficient. Paint this to the screen, then paint your character on top it – MadProgrammer

In 15+ years of professional Java/Swing development, I've never found a need to use SwingUtilities.updateComponentTreeUI(window);, instead, simply call repaint on the component which is responsible for renderer the output, I'm pretty sure, you'll find this more efficient. – MadProgrammer

Swing is also a single threaded environment AND is not thread safe, you should NOT be update the UI from outside of the context of the Event Dispatching Thread, as this will setup a race condition and could result in unwanted and difficult to resolve graphical issues. – MadProgrammer

Hint. JLabel is a descendent of Container, which means it can contain other components ;) – MadProgrammer

Thanks a lot MadProgrammer! So I have replace SwingUtilities.updateComponentTreeUI(window) by window.repaint(). You had right for the Thread safe, my map had some bugs but I wasn't able to find where they was from. And what about the BufferedImage? If I create two BufferedImage, the last one can be automatically on the top of the first one? Or I just want to render the title map to a BufferedImage (so I am limited by 2 layers)? – Celine

It will depend on what it is you want to achieve. Using BufferedImages gives you complete control over the placement of the images and yes, one can be rendered over the other, painting is like a artists canvas, as you add things to it, they are added on top of what is already there, BUT, you might find it easier to add a JLabel to another JLabel - just remember, JLabel doesn't have a layout manager by default – MadProgrammer

Code example:

import javax.swing.JFrame;
import javax.swing.ImageIcon;
import javax.swing.JLabel;


public class Window extends Thread
{
    private static JFrame window = new JFrame("game");
    private int numberSquareX = Settings.sizeX / 20 + 1;
    private int numberSquareY = Settings.sizeY / 20 + 1;
    private JLabel titlesetLayer1[][] = new JLabel[numberSquareX][numberSquareY];
    private JLabel titlesetLayer2[] = new JLabel[1];
    private JLabel titlesetLayer3[] = new JLabel[1];
    private JLabel titlesetLayer4[] = new JLabel[0];
    private JLabel titlesetLayer5[] = new JLabel[0];
    private JLabel characters[] = new JLabel[2];

    public void run()
    {
        window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        window.setSize(Settings.sizeX, Settings.sizeY);
        window.setLocationRelativeTo(null);
        window.setResizable(false);

        // draw layer5 (on the layer4)

        // draw layer4 (on the layer3)

        // draw layer3 (on the characters)
        titlesetLayer3[0] = new JLabel(new ImageIcon("tree_1.png"));
        titlesetLayer3[0].setBounds(130, 120, 126, 160);
        window.add(titlesetLayer3[0]);

        // draw the charaters
        characters[1] = new JLabel(new ImageIcon("character_1.png"));
        characters[1].setBounds(600, 500, 100, 100);
        window.add(characters[1]);

        characters[0] = new JLabel(new ImageIcon("character_1.png"));
        characters[0].setBounds(100, 100, 100, 100);
        window.add(characters[0]);

        // draw layer2 (under the characters)
        titlesetLayer2[0] = new JLabel(new ImageIcon("tree_1.png"));
        titlesetLayer2[0].setBounds(570, 400, 126, 160);
        window.add(titlesetLayer2[0]);

        // draw layer1 (under the layer2)
        for (int x = 0, y = 0; y < numberSquareY; x++)
        {
            titlesetLayer1[x][y] = new JLabel(new ImageIcon("grass_1.png"));
            titlesetLayer1[x][y].setBounds(x * 20, y * 20, 20, 20);
            window.add(titlesetLayer1[x][y]);
            if (x == numberSquareX - 1)
            {
                y++;
                x = -1;
            }
        }
        titlesetLayer1[numberSquareX - 1][numberSquareY - 1] = new JLabel(new ImageIcon("grass_1.png"));
        titlesetLayer1[numberSquareX - 1][numberSquareY - 1].setBounds(numberSquareX * 20, numberSquareY * 20, 20, 20);
        window.add(titlesetLayer1[numberSquareX - 1][numberSquareY - 1]);

        window.setVisible(true);
        // window.repaint();
    }
}

Screen capture:

1

Another solution is to use JLayeredPane!

JLayeredPane layers = new JLayeredPane();
layers.add(tilesetsUnderCharacter, 0); // Layer 0
layers.add(character, 1); // Layer 1
layers.add(tilesetsOnCharacter, 2); // Layer 2
frame.setContentPane(layers);

Code example:

private void init()
{
    frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    frame.setSize(Settings.getX(), Settings.getY());
    frame.setResizable(false);
    frame.setLocationRelativeTo(null);

    for (int i = 0; i < y ; i++)
    {
        for (int j = 0; j < x; j++)
        {
            for (int k = 0; k < tilesetsOnCharactersSize; k++)
            {
                tilesetsOnCharacters[i][j][k] = new javax.swing.JLabel(new javax.swing.ImageIcon(Resources.getTileset(Maps.getMapTileset(mapNumber, 1, k, i, j))));
                tilesetsOnCharacters[i][j][k].setBounds(j * tilesetX, i * tilesetY, tilesetX, tilesetY);
                map.add(tilesetsOnCharacters[i][j][k], 4);
            }
            for (int k = 0; k < tilesetsUnderCharactersSize; k++)
            {
                tilesetsUnderCharacters[i][j][k] = new javax.swing.JLabel(new javax.swing.ImageIcon(Resources.getTileset(Maps.getMapTileset(mapNumber, 0, k, i, j))));
                tilesetsUnderCharacters[i][j][k].setBounds(j * tilesetX, i * tilesetY, tilesetX, tilesetY);
                map.add(tilesetsUnderCharacters[i][j][k], 0);
            }
            for (int k = 0; k < mapAttributeSize; k++)
            {
                if (Maps.getMapTileset(mapNumber, 2, k, i, j) == 1)
                {
                    blocked[i][j] = true;
                }
            }
        }
    }
    for (int i = 0; i < charactersNumber; i++)
    {
        characters[i] = new Character(0, 0, 64, 64, 0, 0, 0, 0, 5);
        tilesetsCharacters[i] = new javax.swing.JLabel(new javax.swing.ImageIcon(Characters.getCharacter(characters[i].getCharacterSkin(), characters[i].getDirection())));
        tilesetsCharacters[i].setBounds(characters[i].getX(), characters[i].getY(), characters[i].getSizeX(), characters[i].getSizeY());
        map.add(tilesetsCharacters[i], 1);
        charactersRender[i] = false;
    }

    frame.addKeyListener(new java.awt.event.KeyAdapter()
    {
        @Override
        public void keyTyped(java.awt.event.KeyEvent keyEvent)
        {

        }

        @Override
        public void keyPressed(java.awt.event.KeyEvent keyEvent)
        {
            if((keyEventInt = keyEvent.getKeyCode()) == java.awt.event.KeyEvent.VK_F)
            {
                right = true;
            }
            else if(keyEventInt == java.awt.event.KeyEvent.VK_S)
            {
                left = true;
            }
            else if(keyEventInt == java.awt.event.KeyEvent.VK_E)
            {
                up = true;
            }
            else if(keyEventInt == java.awt.event.KeyEvent.VK_D)
            {
                down = true;
            }
        }

        @Override
        public void keyReleased(java.awt.event.KeyEvent keyEvent)
        {
            if((keyEventInt = keyEvent.getKeyCode()) == java.awt.event.KeyEvent.VK_F)
            {
                right = false;
            }
            else if(keyEventInt == java.awt.event.KeyEvent.VK_S)
            {
                left = false;
            }
            else if(keyEventInt == java.awt.event.KeyEvent.VK_E)
            {
                up = false;
            }
            else if(keyEventInt == java.awt.event.KeyEvent.VK_D)
            {
                down = false;
            }
        }
    });

    frame.setContentPane(map);
    frame.setVisible(true);
}

private void update()
{
    if (exit && characters[0].getX() < x * tilesetX - characters[0].getSizeX() - characters[0].getMovementSpeed() && characters[0].getX() > 0 && characters[0].getY() > 0 && characters[0].getY() < y * tilesetY - characters[0].getSizeY() - characters[0].getMovementSpeed())
    {
        exit = false;
    }
    if (right && (exit || (characters[0].getX() < x * tilesetX - characters[0].getSizeX() - characters[0].getMovementSpeed() && !blocked[characters[0].getY() / tilesetY][(characters[0].getX() + characters[0].getSizeX() + characters[0].getMovementSpeed()) / tilesetX] && !blocked[(characters[0].getY() + characters[0].getSizeY()) / tilesetY][(characters[0].getX() + characters[0].getSizeX() + characters[0].getMovementSpeed()) / tilesetX])))
    {
        characters[0].right();
        characters[0].setScaleX(5);
        if (allowExitRight && characters[0].getX() > x * tilesetX - characters[0].getSizeX() - characters[0].getMovementSpeed() - 1)
            exit = true;
        charactersRender[0] = true;
    }
    if (left && (exit || (characters[0].getX() > 0 && !blocked[characters[0].getY() / tilesetY][(characters[0].getX() - characters[0].getMovementSpeed()) / tilesetX] && !blocked[(characters[0].getY() + characters[0].getSizeY()) / tilesetY][(characters[0].getX() - characters[0].getMovementSpeed()) / tilesetX])))
    {
        characters[0].left();
        characters[0].setScaleX(-3);
        if (allowExitLeft && characters[0].getX() <= 0)
            exit = true;
        charactersRender[0] = true;
    }
    if (jumped || up && (exit || (characters[0].getY() > 0 && !blocked[(characters[0].getY() - characters[0].getMovementSpeed()) / tilesetY][characters[0].getX() / tilesetX] && !blocked[(characters[0].getY() - characters[0].getMovementSpeed()) / tilesetY][(characters[0].getX() + characters[0].getSizeX()) / tilesetX])))
    {
        if (!jump)
        {
            characters[0].up();
            characters[0].setScaleY(-3);
            if (allowExitUp && characters[0].getY() <= 0)
                exit = true;
            charactersRender[0] = true;
        }
        else if (!jumped && !falling)
        {
            jumpCurrentDuration = jumpDuration;
            jumped = true;
        }
        else if (--jumpCurrentDuration > 0)
        {
            if (exit || (characters[0].getY() > 0 && !blocked[(characters[0].getY() - characters[0].getMovementSpeed()) / tilesetY][characters[0].getX() / tilesetX] && !blocked[(characters[0].getY() - characters[0].getMovementSpeed()) / tilesetY][(characters[0].getX() + characters[0].getSizeX()) / tilesetX]))
            {
                characters[0].up();
                characters[0].setScaleY(-3);
                if (allowExitUp && characters[0].getY() <= 0)
                    exit = true;
                charactersRender[0] = true;
            }
        }
        else
        {
            jumped = false;
        }
    }
    if (((down && !jumped) || (gravity && !jumped)) && (exit || (characters[0].getY() < y * tilesetY - characters[0].getSizeY() - characters[0].getMovementSpeed() && !blocked[(characters[0].getY() + characters[0].getSizeY() + characters[0].getMovementSpeed()) / tilesetX][characters[0].getX() / tilesetX] && !blocked[(characters[0].getY() + characters[0].getSizeY() + characters[0].getMovementSpeed()) / tilesetY][(characters[0].getX() + characters[0].getSizeX()) / tilesetX])))
    {
        characters[0].down();
        characters[0].setScaleY(5);
        if (allowExitDown && characters[0].getY() > y * tilesetY - characters[0].getSizeY() - characters[0].getMovementSpeed())
            exit = true;
        if (jump)
            falling = true;
        charactersRender[0] = true;
    }
    else if (jump)
        falling = false;
}

private void render()
{
    for (int i = 0; i < charactersNumber; i++)
    {
        if (charactersRender[i])
        {
            tilesetsCharacters[i].setIcon(new javax.swing.ImageIcon(Characters.getCharacter(characters[i].getCharacterSkin(), characters[i].getDirection())));
            tilesetsCharacters[i].setBounds(characters[i].getX() + characters[i].getScaleX(), characters[i].getY() + characters[i].getScaleY(), characters[i].getSizeX(), characters[i].getSizeY());
            charactersRender[i] = false;
        }
    }
}

Screen Capture:

Edit: I also found a library called Slick2D who work with TiledMapEditor:

  • http://slick.ninjacave.com/

  • http://www.mapeditor.org/

How to setup Slick2D: How to install Slick2d?

How to use Slick2D and TiledMapEditor: Slick2D + Tiled cant load map

Where started: https://thejavablog.wordpress.com/2008/06/08/using-slick-2d-to-write-a-game/



来源:https://stackoverflow.com/questions/30204194/jlabel-and-jlayeredpane-how-to-display-an-image-over-another-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!