2016-07-29 298 views
7

我一直在试图为我正在开发的小游戏实现基本文本泡泡。不想去太花哨,我开始与一个基本的圆角矩形包含一些文本边框:
Basic Text Bubble在BufferedImage上绘制带有不透明度的圆角矩形

于是,我决定,文字气泡应该在预设时间之后淡出。这就是我偶然发现的一个问题:当我试图在测试窗口中显示气泡时,一切运行良好,但是当我将它们展示在游戏中时,泡泡消失时出现了扭曲。我测试了一些,调试了一下,发现两种情况之间唯一的区别是在测试窗口中我用paintComponent方法的Graphics画了泡泡,而在游戏中我使用BufferedImages来模拟图层并使用image.createGraphics中的图形。然后我可以成功复制的bug:
Gif displaying the bug

在这里,你看到的是,当左边的泡沫渐弱,其圆角右侧的圆角不改变而改变形状相比褪色之前,而泡沫。事实上,左侧的气泡是在BufferedImage上绘制的,然后在面板上绘制,而右侧的气泡直接绘制在面板上。
我已经分离出这是需要重现该问题的代码:

public static void main(String[] args) { 

    JFrame frame = new JFrame("Test"); 
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
    frame.setLocationRelativeTo(null); 
    frame.setSize(400, 400); 

    JPanel panel = new JPanel() { 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 

      BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); 
      Graphics graphics = image.createGraphics(); 

      paintExampleBubble(graphics, 50, 50); 

      g.drawImage(image, 0, 0, this); 

      paintExampleBubble(g, 250, 50); 
     } 
    }; 

    frame.getContentPane().add(panel); 
    frame.setVisible(true); 
} 

private static final Color background = new Color(1f, 1f, 1f, 0.5f); 
private static final Color foreground = new Color(0f, 0f, 0f, 0.5f); 
private static final int borderRadius = 16; 
private static final int width = 100; 
private static final int height = 50; 

private static void paintExampleBubble(Graphics g, int x, int y) { 

    g.setColor(background); 
    g.fillRoundRect(x, y, width, height, borderRadius, borderRadius); 
    g.setColor(foreground); 
    g.drawRoundRect(x, y, width, height, borderRadius, borderRadius); 
} 

这里是上面的代码产生的结果:
Minimal code bug reproduction image

不管怎么说,这表明拉伸到BufferedImage的是什么原因导致这个问题,但是暂时放弃BufferedImages不是一个选择。

我试图调试代码来查看可能会导致这种差异的原因,并且只能注意到图形对象在涉及透明度时使用不同的组件绘制,但是这并不能帮助我解决我的问题,即使有可能强制图形做我想要的,我宁愿尽可能避免黑客入侵。

有没有人知道一个相对简单而有效的方法来解决这个问题,或解决它?

不管怎么说,感谢百忙之中阅读本文时:)

PS:由于这是第一次我问一个问题,我可能已经错过了一些东西,所以随时告诉我如果是这样的案件 !这将非常感激。

编辑:正如我在评论中所说,游戏是基于像素艺术的,因此我宁愿不使用抗锯齿,但保留圆角矩形的基本像素化外观。

+0

不完全相信如果我正确地看到你的描述失真,但尝试打开反锯齿'g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON)'其中'g'是'Graphics2D'的一个实例 – copeg

+0

感谢您的快速回答!我现在已经尝试过了,它确实有效(因为在透明度启动时边框不再改变)。然而,我没有提到这一点,我正在研究的游戏只具有像素艺术图形和自动抗锯齿功能,而且由于分辨率低,看起来很模糊。我会更新我的问题来澄清 – Niss36

+0

并没有真正遵循你正在尝试做的事情,但是这里有一个可能(或可能不)给你使用不同方法的帖子:http://stackoverflow.com/问题/ 15025092/border-with-rounded-corners-transparency – camickr

回答

3

在这里,您会看到当左侧的气泡变淡时,与之前的渐变相比,其圆角改变了形状,而右侧圆角上的气泡未发生变化。事实上,左侧的气泡是在BufferedImage上绘制的,然后在面板上绘制,而右侧的气泡直接绘制在面板上。

与其每次使用不同的alpha值重绘图像,只创建一次并使用AlphaComposite来管理透明度。

下面是你的例子中有三个'气泡'的改编:每当改变前景色时,最左边的是绘制图像,右边的两个使用AlphaComposite(中间使用一次创建的图像,最右边的使用直接使用JPanel图形)。

public class Test { 

    public static void main(String[] args) { 

     JFrame frame = new JFrame("Test"); 
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     frame.setLocationRelativeTo(null); 
     frame.setSize(600, 200); 
     final BufferedImage image = new BufferedImage(600, 200, BufferedImage.TYPE_INT_ARGB); 
     Graphics2D graphics = image.createGraphics(); 
     paintExampleBubble(graphics, 250, 50, foreground); 
     graphics.dispose(); 
     final JPanel panel = new JPanel() { 

      @Override 
      protected void paintComponent(Graphics g) { 
       super.paintComponent(g); 
       Graphics2D g2d = (Graphics2D)g; 

       final BufferedImage i2 = new BufferedImage(600, 200, BufferedImage.TYPE_INT_ARGB); 
       Graphics2D graphics = i2.createGraphics(); 
       paintExampleBubble(graphics, 50, 50, alphaForeground); 
       graphics.dispose(); 
       g.drawImage(i2, 0, 0, this); 
       //use Alpha Composite for transparency 
       Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER , alpha); 
       g2d.setComposite(comp); 
       g2d.drawImage(image, 0, 0, this); 

       paintExampleBubble(g2d, 450, 50, foreground); 
      } 
     }; 
     javax.swing.Timer timer = new javax.swing.Timer(100, new ActionListener(){ 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       alpha -= 0.05; 

       if (alpha < 0){ 
        alpha = 1.0f; 
       } 
       alphaForeground = new Color(0f, 0f, 0f, alpha); 
       panel.repaint(); 
      } 

     }); 
     timer.start(); 
     frame.getContentPane().add(panel); 
     frame.setVisible(true); 
    } 

    private static float alpha = 1.0f; 
    private static final Color background = new Color(1f, 1f, 1f, 1f); 
    private static final Color foreground = new Color(0f, 0f, 0f, 1f); 
    private static Color alphaForeground = new Color(0f, 0f, 0f, alpha); 
    private static final int borderRadius = 16; 
    private static final int width = 100; 
    private static final int height = 50; 

    private static void paintExampleBubble(Graphics g, int x, int y, Color color) { 
     g.setColor(background); 
     g.fillRoundRect(x, y, width, height, borderRadius, borderRadius); 
     g.setColor(color); 
     g.drawRoundRect(x, y, width, height, borderRadius, borderRadius); 
    } 
} 

在我的系统上看到最左边(与foretground颜色管理的透明度),但不与透明度的AlphaComposite

+1

谢谢!我已经设法将你的答案移植到我的具体案例中(与我发布的代码实际上有很大不同),并使其工作。但是,它会为每个文本气泡创建一个BufferedImage,这可能会导致出现大量泡泡的性能问题,但目前这不是问题,我会将您的答案标记为已接受,直到有人提出了解决方案不涉及额外的BufferedImages。 – Niss36

+0

您可以为所有文字气泡使用单个中间缓冲区。您可以每次清除它,以完全不透明的方式绘制泡泡,并将其复合到组件上。然后下一个泡泡可以使用相同的中介缓冲区 – Falco

+0

请您详细说明一下吗?我不确定我明白你的意思。我猜的是类似静态BufferedImage的文本气泡所绘制场景的大小,然后图像在屏幕上用alpha绘制,然后清除,然后重复下一个气泡。 – Niss36