SurfaceViewは、他のViewと違い、取扱いが特殊である。
SurfaceViewを用いたsample codeはLunarLanderである。このsample codeは本格的なゲームであるため、(私から見て)難しくて学習向きであるようには思えない。
このsample codeで気になるのは、背景を常に書き換えていることである。
/** * Draws the ship, fuel/speed bars, and background to the provided * Canvas. */ private void doDraw(Canvas canvas) { // Draw the background image. Operations on the Canvas accumulate // so this is like clearing the screen. canvas.drawBitmap(mBackgroundImage, 0, 0, null);doDrawメソッド内の第一行目でBitmapを使って、画面全体を塗り潰している。そして、第二行目以降で、ゲームに登場するキャラクターを描画していくのである。「変化が無い領域をも再描画することは無駄ではないか」と思ってしまうが、これがSurfaceViewの流儀であるようだ。
Canvas and DrawablesのOn a SurfaceViewには次のように書かれてある。
Note: On each pass you retrieve the Canvas from the SurfaceHolder, the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the entire surface. For example, you can clear the previous state of the Canvas by filling in a color with drawColor() or setting a background image with drawBitmap(). Otherwise, you will see traces of the drawings you previously performed.
滑らかなアニメーションを実現するために、画面全体を再描画しなければならないのである。動いているのが、主人公たった一人だけであってもだ。
画面内の個々の構成要素のサイズや位置を、アニメが発生する毎に計算するのは手間や時間がかかる。
LunarLanderの場合だと、黒色一色だけで塗り潰せば良いので問題は無い。
しかし、空や太陽や山や家や木等を重ね合わせる計算処理を、アニメが発生する毎に行うのは手間と時間がかかる。
そのような手間を、ある程度緩和するために、事前に、動きの無い背景の絵をBitmapに描いて、それを再描画の度に背景に表示するようにすれば良い。
LunarLanderの場合だと、黒色一色だけで塗り潰せば良いので問題は無い。
しかし、空や太陽や山や家や木等を重ね合わせる計算処理を、アニメが発生する毎に行うのは手間と時間がかかる。
そのような手間を、ある程度緩和するために、事前に、動きの無い背景の絵をBitmapに描いて、それを再描画の度に背景に表示するようにすれば良い。
Canvasをnewする時の引数にBitmapを設定した上で、このCanvasに描画していけば、その描画がBitmapに反映されるのである。
この技法を使ったsample codeは下記のとおりである。
このサンプルではグラデ―ションを作成しているが、この作成が仮に、手間がかかり時間がかかる処理だとする。そんなことをrunが実行される度に行うと、多くの計算処理を行うことになる。
このため、最初の描画の時に、Bitmap画像として大域変数に保管しておき、必要に応じて、このBitmap画像を表示すれば良い。
public class MySurfaceView extends SurfaceView
implements
Callback,
Runnable
{
Thread t;
Bitmap bSource, bBackground;
int iY;
public MySurfaceView(Context c){
super(c);
t = null;
iY = 50;
getHolder().addCallback(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceCreated(SurfaceHolder h) {
Canvas c = h.lockCanvas();
//背景用の、空っぽのBitmapを用意する・
bBackground = Bitmap.createBitmap(c.getWidth(), c.getHeight(), Bitmap.Config.ARGB_8888);
//Bitmapに書き込むCanvasを用意する。
Canvas cBackground = new Canvas(bBackground);
LinearGradient(c);//表示用の書き込み
LinearGradient(cBackground);//背景用画像の作成
//ドロイド君の画像を表示する。
Resources r = getResources();
bSource = BitmapFactory.decodeResource(r, R.drawable.ic_launcher);
c.drawBitmap(bSource, 100, iY, null);
h.unlockCanvasAndPost(c);
t = new Thread(this);
t.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void run() {
while(t!=null){
Canvas c = getHolder().lockCanvas();
if(c!=null){
//背景画像を表示する。
c.drawBitmap(bBackground, 0.0f, 0.0f, null);
iY++;
if(iY>400) iY = 50;
c.drawBitmap(bSource, 100, iY, null);
getHolder().unlockCanvasAndPost(c);
}
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
t = null;
}
}
}
private void LinearGradient(Canvas c){
LinearGradient lg = new LinearGradient(
c.getWidth()/2, 0.0f, c.getWidth()/2, c.getHeight(),
Color.BLUE, Color.RED, Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setShader(lg);
c.drawPaint(p);
}
}
0 件のコメント:
コメントを投稿