【问题标题】:JLabel and JLayeredPane - How to display an image over another image?JLabel 和 JLayeredPane - 如何在另一个图像上显示图像?
【发布时间】:2015-07-24 02:43:17
【问题描述】:

我尝试用java创建一个小游戏,但我遇到了麻烦。

当我绘制地图时,如果不覆盖正方形的标题集,我将无法显示字符。

我的目标是能够在同一个方块上显示多张图片(比如草、人物和树的标题集),所以我必须处理图片的透明度(这不是问题)和层(这是问题所在)。

那么如何在另一张图片上显示一张图片呢?

如何向 java 解释我需要在另一个图像上或下显示此图像?

这是我的源代码。不知道能不能帮到你。如果您给我一个线索或能够管理图层的功能,您的帮助对我真的很有帮助。为我重写所有代码是没用的 x) 这个程序不完整,我现在只用它来测试我的程序。我知道他刷新了两次地图,所以他覆盖了角色的正方形(而且他还有很多其他的小故障),但这不是我问题的目的。我尝试一步一步完成我的游戏!

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

我已经找到了这个主题:How to use JLayered Pane to display an image on top of another image? 和这个 Best practice for creating a composite image output for Java Swing 但他们并没有真正帮助我......

我继续寻找答案。如果我找到它,我会回来在这里发布它。

【问题讨论】:

  • 将标题图渲染到BufferedImage,可以是完整的,也可以是基于可用的可视区域,哪个更有效。将此绘制到屏幕上,然后在其上绘制您的角色
  • 在 15 年以上的专业 Java/Swing 开发中,我从未发现需要使用 SwingUtilities.updateComponentTreeUI(window);,相反,只需在负责渲染输出的组件上调用 repaint,我很确定,你会发现这样更有效率。
  • Swing 也是单线程环境并且不是线程安全的,您不应该从事件调度线程的上下文之外更新 UI,因为这会设置竞争条件并可能导致不需要且难以解决的图形问题。
  • 提示。 JLabelContainer 的后代,这意味着它可以包含其他组件;)
  • 非常感谢 MadProgrammer!所以我用 window.repaint() 替换了 SwingUtilities.updateComponentTreeUI(window)。您有权使用 Thread 保险箱,我的地图有一些错误,但我无法找到它们的来源。那么 BufferedImage 呢?如果我创建两个BufferedImage,最后一个可以自动在第一个上面吗?或者我只是想将标题图渲染为 BufferedImage(所以我被 2 层限制)?

标签: java jframe jpanel jlabel jlayeredpane


【解决方案1】:

解决了。

感谢 MadProgrammer 的 cmets。

将标题图渲染为一个 BufferedImage,可以是完整的,也可以是基于可用的可视区域,哪个更有效。把它画到屏幕上,然后在上面画你的角色——MadProgrammer

在 15 年以上的专业 Java/Swing 开发中,我从未发现需要使用 SwingUtilities.updateComponentTreeUI(window);,相反,只需在负责渲染输出的组件上调用 repaint,我很肯定,你会发现这更有效。 – 疯狂程序员

Swing 也是一个单线程环境并且不是线程安全的,您不应该从事件调度线程的上下文之外更新 UI,因为这会设置竞争条件并可能导致不需要且难以解决图形问题。 – 疯狂程序员

提示。 JLabel 是 Container 的后代,这意味着它可以包含其他组件 ;) – MadProgrammer

非常感谢 MadProgrammer!所以我用 window.repaint() 替换了 SwingUtilities.updateComponentTreeUI(window)。您有权使用 Thread 保险箱,我的地图有一些错误,但我无法找到它们的来源。那么 BufferedImage 呢?如果我创建两个BufferedImage,最后一个可以自动在第一个上面吗?或者我只想将标题图渲染为 BufferedImage(所以我受限于 2 层)? ——席琳

这将取决于您想要实现的目标。使用 BufferedImages 可以让您完全控制图像的位置,是的,一个可以渲染另一个,绘画就像艺术家的画布,当您向其中添加内容时,它们会添加到已经存在的内容之上,但是,您可能会发现将 JLabel 添加到另一个 JLabel 更容易 - 请记住,JLabel 默认没有布局管理器 - MadProgrammer

代码示例:

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

屏幕截图:

1

另一种解决方案是使用 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);

代码示例:

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

屏幕截图:

编辑:我还发现了一个名为 Slick2D 的库,它与 TiledMapEditor 一起使用:

如何设置 Slick2D:How to install Slick2d?

如何使用 Slick2D 和 TiledMapEditor:Slick2D + Tiled cant load map

从哪里开始:https://thejavablog.wordpress.com/2008/06/08/using-slick-2d-to-write-a-game/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-11
    • 2014-06-08
    • 1970-01-01
    相关资源
    最近更新 更多