2016-07-29 166 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