高速描画?BufferStrategyを使ってみる
Javaには、非常にたくさんのクラスライブラリがあります。
いったいどれくらい覚えたらいいのか、いくつ理解すればプログラミングが
できるようになるのか不安になる人もいるようです。
毎度、管理人イガジーです。
知らないもの/ことは、あって当たり前。
必要に応じて、調べて、試して、使えばよいのです。
辞書に載ってる単語を全て覚えてから、英会話を始めようとするのが無謀なのと同じです。
少しでも覚えたら、まずは使ってみましょう。
という訳で、高速描画用の仕掛けらしいBufferStrategy というモノを使ってみました。
使い方としては
Frame または Canvas で、createBufferStrategy() して
getBufferStrategy() で得たBufferStrategy に対して描画し、
show()とすれば、表示される。という感じです。
※ ちなみに、イガジーが試した環境は Linux なので
Windows や MAC だと多少異なっているかもしれません。
最初にハマったのが、createBufferStrategy() しても例外が起きて
BufferStrategyを生成できないトラブルです。
どうやらFrameなり、Canvasを実際に表示(setVisble(true))して
画面上に窓が現れた状態にする必要があるようです。
BufferStrategy に対する描画は、getDrawGraphics()で得た
Graphics を使って行います。
show() した後は、BufferStrategy が切り替わるので Graphics を取り直す必要があります。
管理人イガジーが試したコードを掲載しておきます。
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.Timer;
import java.util.TimerTask;
public class BufStrCanvas {
final int interval=100; //ms
final int bww=30, bhh=30;
final int bufw=204,bufh=181;
final int speed=9;
int vx=speed;
int vy=2;
int x,y;
Frame f=new Frame("BufferStrategy Canvas");
GCanvas cv;
BufferStrategy bs=null;
Timer tmr;
BufStrCanvas() { // constructor
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
});
cv=new GCanvas();
x=100;
y=80;
// f.setIgnoreRepaint(true);
// cv.setIgnoreRepaint(true);
f.add(cv,BorderLayout.CENTER);
Button btn=new Button("Stop");
btn.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent arg0) {
tmr.cancel();
if (cv.g!=null) cv.g.dispose();
cv.g=null;
// if (bs!=null) bs.dispose();
}
});
f.add(btn,BorderLayout.SOUTH);
f.setSize(300,300);
f.setVisible(true);
sleep(1000);
try {
cv.createBufferStrategy(2);
}catch(IllegalStateException e){
System.out.println("Fail createBufferStrategyh");
// e.printStackTrace();
System.exit(1);
}
bs=cv.getBufferStrategy(); // GCanvas
tmr=new Timer();
tmr.schedule(new MyTimer(), 1000, interval);
}
void sleep(long msec) {
try {
Thread.sleep(msec);
} catch (InterruptedException e) {}
}
class GCanvas extends Canvas {
private static final long serialVersionUID = 1L;
Graphics g=null;
void draw(){
if (g!=null) g.dispose();
bs.show();
Toolkit.getDefaultToolkit().sync();
g=bs.getDrawGraphics();
}
/*
@Override
public void update(Graphics g0) {
paint(g0);
}
@Override
public void paint(Graphics g0){
if (bs!=null) {
if (g==null) bs.show();
}
}
*/
}
class MyTimer extends TimerTask {
int busycount=0;
boolean busy=false;
public void run() {
if (busy) {
System.out.print("Busy");
System.out.println(busycount++);
}else{
busy=true;
cv.draw(); // GCanvas
if (bs.contentsLost()) {
tmr.cancel();
bs.dispose();
cv.g.dispose();
System.exit(2);
}
cv.g.setColor(Color.BLACK);
// cv.g.fillOval(x, y, bww, bhh);
cv.g.fillRect(0,0,bufw+50,bufh+50);
x+=vx;
y+=vy;
if ((x<=10)||(x>=bufw)) vx=-vx;
if ((y<=10)||(y>=bufh)) vy=-vy;
cv.g.setColor(Color.YELLOW);
cv.g.fillOval(x, y, bww, bhh);
busy=false;
}
}
}
public static void main(String[] args) {
new BufStrCanvas();
}
}
ざざっと作ってみたレベルなので、改良の余地は色々あると思います。
stopボタンをクリックしてタイマータスクを止めると、通常のpaint()では
描画されません(即ち、窓を隠して再表示させた時に画面が戻りません)。
その対処をしたい場合は、コメントにしている GCanvasのupdate()〜
paint()部分を活かしてください。
これが実際に速いのかどうか、(Linux上では)よくわかりません。
intervalを20(ms)などに小さくしても、処理は充分間に合っているようですが
実際の表示は間引かれているように見えます。
※ 追記
Toolkit.getDefaultToolkit().sync();
を追加すると表示の間引きがなくなったような感じがします。
後日、Windowsなどでも試してみようと思っていますが、とり急ぎ、参考まで。
ご意見、ご質問などは、お気軽に メールフォーム からどうぞ。
この記事へのコメントはこちら