The next JAVA library(5)
今度は、描画周りについて考えてみます。
まず、表画面の扱いについてですが、これもDoCoMoとMIDPで、仕様が違います。
似ているのは、どちらも同じCanvasを継承しているということぐらいですね。
で、何が違うかというと、paint()の引数として渡される、Graphicsが違います。
DoCoMoの場合、このグラフィックスに描画すると、即表画面に反映されます。
しかし、au,Vodafoneは、isDoubleBuffered()でtrueを返す機種は、paint()を抜けた後に表画面に反映されて、falseを返す機種は、即表画面に反映されます。
なにこの中途半端な仕様(;´Д`)
まあ、これを解決するには、ダブルバッファリングを手動で行うだけで良いです。
つまり、コンストラクタなどの初期化部分で、
Image _img = new Image.createImage(getWidth(), getHeight());
これで出来上がりです。
これはこれで良いんですが、しかし、getWidth()とgetHeight()は、画面の解像度を返すので、機種ごとに大きさが違います。ゲームを1つ作るために、似たような解像度の違いを意識して書くのは馬鹿げています。
なので、ここは大きさを固定してイメージを作ります。
Image _img = new Image.createImage(QVGAx2(120), QVGAx2(120));
QVGAx2()というのは、次のように定義してます。
#if QVGA #define QVGAx2(a) ((a)*2) #else #define QVGAx2(a) (a) #endif
これにより、QVGAのフラグを立てた場合はQVGAサイズのイメージが、立てなかった場合は、通常サイズのイメージが作成されるわけです。
これでダブルバッファリングの準備は整いました。
あとは、こいつをpaint()内で描画するだけです。
public void paint(Graphics g){ int x = (getWidth()-_img.getWidth())/2; int y = (getWidth()-_img.getHeight())/2; #if DOCOMO g.drawImage(_img, x, y); #else g.drawImage(_img, x, y, Graphics.LEFT|Graphics.TOP); #endif }
画面の中心に来るようにxとyを計算して、機種ごとに別々のdrawImageを呼び出しているだけです。
xとyについては、計算のコストが馬鹿にならないので、別のところで計算しておいてください(^^ゞ
で、機種ごとに別々のdrawImageを呼び出すと言いましたが、さすがにこれを描画のたびに分けるのは非常に面倒です。
なので、普通はプリプロセッサを使って、
#if DOCOMO #else #define drawImage(a,b,c,d) drawImage(a,b,c) #endif
このように、au,Vodafoneの、最後の引数を取り除いて使用します。
こうすれば、上手く動くのですが、自分的には、オフセットを使用して描画する機能って結構便利なので、残しておきたいです。
というか、マクロで解決するのはあまりスマートではないし、よく分からないバグとかを発生させたりするので、あんまり使いたくないです(;´Д`)
それに、自分は、基本的にJAVAのクラスを直接扱わせたくないです。なぜなら、それこそが機種依存だの何だのを引き起こしている原因だからです。
この部分さえライブラリで吸収してやれば、メイン側では機種の違いを意識せずに書くことが出来るのです。
ということで、drawImage()をライブラリ側に実装してみました。
protected void _drawImage(Image dst, Image src, int x, int y, int point){ /* 0 1 2 3 4 5 6 7 8 */ int w=src.getWidth(); int h=src.getHeight(); switch(point){ case 0: break; case 1: x-=w/2; break; case 2: x-=w; break; case 3: y-=h/2; break; case 4: x-=w/2; y-=h/2; break; case 5: x-=w; y-=h/2; break; case 6: y-=h; break; case 7: x-=w/2; y-=h; break; case 8: x-=w; y-=h; break; } #if KDDI|VODA dst.getGraphics().drawImage(src, x, y, Graphics.LEFT|Graphics.TOP); #endif #if DOCOMO dst.getGraphics().drawImage(src, x, y); #endif }
pointというのは、描画のオフセットを指定する部分です。0なら左上、4なら真ん中、9なら右下、という形で使います。
第1引数のImageは、転送先のImageです。Graphicsのように、複数存在するようなライブラリではないので、このような形になります。
あと、Imageではなく、Graphicsでも良いのですが、Imageが2つ並んでた方が覚えやすいので、Imageにしています。
これだけで一応出来るのですが、毎回こんなに引数を書いてやるのはめんどいので、ある程度省略したdrawImage()も追加しておきます。
protected void _drawImage(Image img, int x, int y){ _drawImage(_img, img, x, y, 0); } protected void _drawImage(Image img, int x, int y, int point){ _drawImage(_img, img, x, y, point); } protected void _drawImage(Image dst, Image src, int x, int y){ _drawImage(dst, src, x, y, 0);
基本的に、ダブルバッファのイメージに描画する場合がほとんどだと思うので、省略して書けることにします。
あと、pointの有無で分けています。
これでdrawImage()は出来上がりですが、まだ、fillRect()やdrawRect()があります。
ということでこれも実装。
protected void _fillRect(int x, int y, int w, int h, int c){ _fillRect(_img, x, y, w, h, c); } protected void _fillRect(Image img, int x, int y, int w, int h, int c){ setColor(img, c); img.getGraphics().fillRect(x, y, w, h); } protected void _drawRect(int x, int y, int w, int h, int c){ _drawRect(_img, x, y, w, h, c); } protected void _drawRect(Image img, int x, int y, int w, int h, int c){ setColor(img, c); img.getGraphics().drawRect(x, y, w, h); } private void setColor(Image img, int c){ #if DOCOMO img.getGraphics().setColor( Graphics.getColorOfRGB((c>>16)&0xff, (c>>8)&0xff, c&0xff)); #else img.getGraphics().setColor(c&0xffffff); #endif }
第1引数がImageではない方のメソッドは、ダブルバッファのイメージです。
setColor()は、DoCoMoとMIDPで仕様が違うので、これを吸収して使えるようにしようと思ったのですが、そもそも、fillRect()やdrawRect()の前にsetColor()を使用しないといけないのは、(自分的には)冗長だと思うので、これは引数の中に含めました。
あと、描画と言えば文字の描画ですが、これはまた明日にでも(;´Д`)