2017-10-06 8 views
0

私は単純な2Dゲームで作業しています。それぞれのティック、特定のエフェクト(フェードトランジション、オーディオフェードイン/アウトなど)のためにスレッドを開始するエフェクトキューをチェックしたいと思います。たとえば、メニュー画面で「再生」を押すと、このキューに「FadeOut」というメッセージが追加され、処理され、GamePanel上でアルファ値が増加する黒い四角形を描画するスレッドが開始されます。PaintComponent()の外でJPanelに描画されたBufferedImageをgetGraphics()で上書きする方法

私はpaintComponent()をオーバーライドし、私のグラフィックスを送信していますが(現在の州のドローにGraphicsオブジェクトに沿って通る私のGameStateManager、)に反対します。私は現在、paintComponent()グラフィックスオブジェクトをルーティングするエフェクト状態(おそらく私は)を持っていませんが、自分のゲームパネルを私のエフェクトスレッドに渡して、getGraphics()を使って描くことができます。 gameloopはまだゲームをレンダリングしているので、GamePanelに直接四角形を描画するだけでちらつきが発生します。

私は、私がされたBufferedImageにアルファの増加に伴って黒の四角形を描画することができた、ゲームのパネルの上にBufferedImageを描く(古いを置き換えるために新しい描画を引き起こし)AlphaComposite.Srcに複合を設定します。問題は、ゲームパネルに描画されたBufferedImagesがそれぞれの描画をオーバーライドしないため、さまざまなアルファの黒いBufferedImageが互いに重なり合っているため、フェードアウトが実際に迅速に行われることです。

私は、複合設定をテストし、上書きなっているかを確認するには、この短いプログラムを書きました。すべての描画はdraw()で行われ、これはエフェクトスレッドのrun()です。 「oPanelGraphics」に複合を設定している面白いの

import java.awt.AlphaComposite; 
import java.awt.Color; 
import java.awt.Graphics2D; 
import java.awt.image.BufferedImage; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class ScratchPad extends JPanel implements Runnable 
{ 
    private JFrame  oFrame   = null; 
    private Thread  oGameThread = null; 
    private Graphics2D oPanelGraphics = null; 
    private Graphics2D oImageGraphics = null; 
    private BufferedImage oImage   = null; 

public static void main(String args[]) throws Exception 
{ 
    new ScratchPad(); 
} 


public ScratchPad() 
{ 
    createFrame(); 
    initPanel(); 
    addAndShowComponents(); 

    oGameThread = new Thread(this, "Game_Loop"); 
    oGameThread.start(); 
} 


private void addAndShowComponents() 
{ 
    oFrame.add(this); 
    oFrame.setVisible(true); 
} 


private void initPanel() 
{ 
    this.setOpaque(true); 
    this.setBackground(Color.cyan); 
} 


private void createFrame() 
{ 
    oFrame = new JFrame("Fade"); 
    oFrame.setSize(700, 300); 
    oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    oFrame.setLocationRelativeTo(null); 
} 


public void run() 
{ 
    oImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB); 

    while(true) 
    { 
    try 
    { 
     draw(); 
     Thread.sleep(100); 
    } 
    catch(InterruptedException e) 
    { 

    } 
    } 
} 


private void draw() 
{ 
    oPanelGraphics = (Graphics2D)this.getGraphics(); 
    oImageGraphics = oImage.createGraphics(); 

    oImageGraphics.setComposite(AlphaComposite.Src); 

    oImageGraphics.setColor(new Color(0,0,0,90)); 
    oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight()); 
    oPanelGraphics.drawImage(oImage, 10, 10, null); 

    oImageGraphics.setColor(new Color(0,0,0,60)); 
    oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight()); 
    oPanelGraphics.drawImage(oImage, 220, 10, null); 

    oImageGraphics.setColor(new Color(0,0,0,30)); 
    oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight()); 
    oPanelGraphics.drawImage(oImage, 430, 10, null); 

// Drawing this image over location of first image, should overwrite first 
// after setting composite to 'Src' 

oPanelGraphics.setComposite(AlphaComposite.Src); 
oImageGraphics.setColor(new Color(0,0,0,10)); 
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight()); 
oPanelGraphics.drawImage(oImage, 10, 10, null); 

oImageGraphics.dispose(); 
oPanelGraphics.dispose(); 
} 
} // end class 

はBufferedImageのに任意のアルファは、以前にあった画像の上に描画されている完全に不透明な黒画像が得られ、離れて行くようになります。黒以外の色に設定しても効果はありません。

何も面白いことにBufferedImageのための複合を設定されています

oImageGraphics.setComposite(AlphaComposite.SrcIn); 

は何も示されていないことが原因となります。 「SRCIN」の2Dでの状態でこれをグラフィクスを合成するには、Oracleのマニュアル:

「は、送信元と宛先の重複の画素場合、重複領域内の唯一のソースピクセルがレンダリングされます。」

だから、私はこれは私がAlphaComposite.Srcと得る同じ振る舞いを持つことが期待されます。

たぶんそこに誰かがこれらの複合体で起こっていただきました上でいくつかの光を当てることができ、そしてどのように私は私の所望の効果を達成できます。

+0

このdraw()メソッドではなく、パネルのonDraw(Graphics g2d)をオーバーライドしますか? –

+0

@MarcosVasconcelosはい、それは私が現在やっていることです。しかし、私のゲームでの効果(トランジション、スクリーン、オーディオフェードインとフェードアウト)を扱う私のアイデアは、チェックされたキューにメッセージを追加するアクション(メニューで「Play」をクリックするか、キャラクターが特定のタイルを歩く)各ゲームのティック。これらのエフェクトはスレッドから発火して処理され、ゲームループは継続して実行されます。私は現在、私のpaintComponent()のGraphicsオブジェクトをMyEffectクラスにルーティングしていません。これは、私がエフェクトスレッドでgetGraphicsを使う理由です。 – WhitJackman

+0

あなたのコードに基づいて、あなたは 'paintComponent'をオーバーライドしていませんが、' getGraphics'を使用しています。これは推奨されません。 Swingで定義されたペイントサイクルのコンテキスト作成していない 'Graphics'コンテキストも決して処理しないでください。私は 'BufferedImage'のポイントも見ません。また、アニメーションがSwingでどのように動作するかについてのコンセプトはないようです。 – MadProgrammer

答えて

1

あなたは

  • コンポーネント上getGraphicsを呼び出さないでくださいをやろうとすることが、「思われる」ものとの数の問題があります。これはnullを返し、Swingペイントサイクル中に最後に描画されたもののスナップショットのみを返します。あなたはそれにペイントものはまた、あなたは
  • 絵画が配合されたスイングによって描かれている他のコンポーネントに影響を与える可能性がありそう、作成していないGraphicsコンテキストを処分することはありません次のペイントサイクル
  • に消去され、これがあることを意味します同じGraphicsコンテキスト(またはBufferedImage)に繰り返しペイントすると、以前に描画されたものの上にこれらの変更が適用され続けます。
  • また、アニメーションの仕組みについての概念もないようです。フェードエフェクトを1回のパスでペイントするのではなく、結果を画面に適用できない場合は、各サイクルでフェーズを適用し、次のパスが実行されるまで画面にフェーズを更新する必要があります。

以下は、私が話していることの本当の基本的な例です。それは "基本"画像(これはゲームの "基本"の状態かもしれませんが、私は静的な画像を使用しました)と、塗料の効果が上にかかります。

Fade to black

import java.awt.AlphaComposite; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Image; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.List; 
import javax.imageio.ImageIO; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class Test { 

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

    public Test() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new TestPane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel { 

     private Engine engine; 
     private Image frame; 

     public TestPane() { 
      engine = new Engine(); 
      engine.setEngineListener(new EngineListener() { 
       @Override 
       public void updateDidOccur(Image img) { 
        frame = img; 
        repaint(); 
       } 
      }); 
      engine.start(); 

      addMouseListener(new MouseAdapter() { 
       @Override 
       public void mouseClicked(MouseEvent e) { 
        engine.addEffect(new FadeOutEffect(Color.BLACK)); 
       } 
      }); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return engine.getSize(); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      if (frame != null) { 
       Graphics2D g2d = (Graphics2D) g.create(); 
       g2d.drawImage(frame, 0, 0, null); 
       g2d.dispose(); 
      } 
     } 

    } 

    public interface EngineListener { 

     public void updateDidOccur(Image img); 
    } 

    public class Engine { 

     // This is the "base" image, without effects 
     private BufferedImage base; 
     private Timer timer; 
     private EngineListener listener; 

     private List<Effect> effects = new ArrayList<Effect>(25); 

     public Engine() { 
      try { 
       base = ImageIO.read(new File("/Volumes/Big Fat Extension/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg")); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 

      timer = new Timer(10, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        int width = base.getWidth(); 
        int height = base.getHeight(); 
        BufferedImage frame = new BufferedImage(width, height, base.getType()); 
        Graphics2D g2d = frame.createGraphics(); 
        g2d.drawImage(base, 0, 0, null); 
        Iterator<Effect> it = effects.iterator(); 
        while (it.hasNext()) { 
         Effect effect = it.next(); 
         if (!effect.applyEffect(g2d, width, height)) { 
          it.remove(); 
         } 
        } 
        g2d.dispose(); 
        if (listener != null) { 
         listener.updateDidOccur(frame); 
        } 
       } 
      }); 
     } 

     public void start() { 
      timer.start(); 
     } 

     public void stop() { 
      timer.stop(); 
     } 

     public void addEffect(Effect effect) { 
      effects.add(effect); 
     } 

     public void setEngineListener(EngineListener listener) { 
      this.listener = listener; 
     } 

     public Dimension getSize() { 
      return base == null ? new Dimension(200, 200) : new Dimension(base.getWidth(), base.getHeight()); 
     } 

    } 

    public interface Effect { 
     public boolean applyEffect(Graphics2D context, int width, int height); 
    } 

    public class FadeOutEffect implements Effect { 

     private int tick = 0; 
     private Color fadeToColor; 

     public FadeOutEffect(Color fadeToColor) { 
      this.fadeToColor = fadeToColor; 
     } 

     @Override 
     public boolean applyEffect(Graphics2D context, int width, int height) { 
      tick++; 
      float alpha = (float) tick/100.0f; 
      if (alpha > 1.0) { 
       return false; 
      } 
      Graphics2D g2d = (Graphics2D) context.create(); 
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 
      g2d.setColor(fadeToColor); 
      g2d.fillRect(0, 0, width, height); 
      g2d.dispose();   
      return true; 
     } 

    } 

} 

、これはスイングがスレッドセーフではありませんので、あなたは、実際には、複数のスレッドを持つべきではないこと、すべての効果や変更は、同じ「メインループ」内に適用されなければならない、あなたを忘れないでください可能であれば追加のスレッドを避けるべきです。この例では、ActionListeractionPerformedメソッドがEDTのコンテキスト内で呼び出され、UIの更新を安全に行うため、Swing Timerを「メインループ」として使用しています。 actionPerformedメソッドが呼び出されている間にUIをペイントできないため、単純な同期メソッドも提供されます

+0

ありがとうございます。だから、あなたは効果リストをチェックするタイマーを常に持っていますか?そして、現在の背景をBufferedImageに描画し、その後にエフェクトを描画し、メインパネルに戻って画面に再描画しますか?このようなタイマーを、私がすでに持っているガメロップと共に使用できますか?それは、どちらか一方が使用できるように思われる。 – WhitJackman

+0

基本的に - ここのベースイメージは動的に作成できますが、各ペイントパスはゼロから開始します – MadProgrammer

+0

したがって、paintComponent()から開始し、そのグラフィックスオブジェクトを適切な方法でルーティングする必要があります。 – WhitJackman

関連する問題