Androidでゲームアプリをcanvasで作るときの注意点
このたび「三目くらげ」というAndroidアプリのゲームを制作して公開しました。
そのときにここはポイントかなと思ったところをまとめておきます。
制作の動機など
最近はUnityやAdobe Air、html5とマルチプラットフォームな環境が人気ですが、パフォーマンスや機能的にネイティブなものが必要となることも多く、たとえばAirではActionScript Native Extensions(ANE)を使ったり、html5では表示するラッパーはjavaで作っておいて中のhtml5ページからAPIを呼び出したりとまだまだjavaは必要ですし、おそらく今後もそうでしょう。
それに他の環境で開発するにもAndroidアプリの機能やライフサイクルといった基本を押さえておく必要があり、それのは実際に作ってみるのが一番です。
また、単純に開発が無料で、html5やAirにくらべるとパフォーマンスが十分出るという利点もあります。
ということで、私が最初に作った「三目くらげ」というFlashゲームが、さすがに初めて作っただけあって今見るといまいちなできな上、Flashプレイヤーのバージョンのせいで表示がおかしくなっているので、素材もそろっていることだしを移植してみることにしました。
注意:以下の内容はある程度プログラムのできる人を想定して書いています。そうでない人は世の中には初心者向けのサイトや書籍があふれているのでそちらをどうぞ。
canvasとOpenGL
Androidには3Dにも対応したOpenGLを使った高速な描画方法と比較的簡単だが負荷の大きいcanvasでの描画があります。しかし、実は高速と言ってもOpenGLとcanvasは描画領域が少なければ大して速さは変わらないようです。
こちらのサイト(Androidアプリで高速描画チューニングをするコツ)の比較が正しければ、描画オブジェクトの数が数十になると差が出始めるので、今回の「三目くらげ」のようなゲームではOpenGLを使うことにあまり意味はなさそうです。
一般的な2Dのパズルゲームの多くもcanvasで実装すれば十分だと思います。
ただ、後述するようにすべての解像度で問題なく実行するにはそれなりに描画パフォーマンスを上げる工夫が必要ですし、拡大縮小などをなめらかにしたいのであればOpenGLの方が良いでしょう。
ちなみにhtml5のcanvasとandroidのcanvasは実装方法が似ているので、html5でcanvasを使ったことがある人はわりとすんなり使い方が理解できると思います。
SurfaceView
描画にはただのViewとSurfaceViewとがあります。ゲームでは高速に実行されるSurfaceViewを使うべきです。
SurfaceViewを使った描画とメインスレッドの実装方法はこちらの2冊の書籍を参考にしました。
スレッドの処理は「Android~」の方がきっちりしていたのでそちらを、負荷軽減のためsleepを入れたりする部分は「OpenGL~」の方を参考にして実装しました。
本としては「OpenGL~」は日本の本にありがちなソースコードばかりで解説が少なく、とりあえずコピペで済ませたい人にはすぐに役立つタイプの本です。「Android~」の方は海外の本らしく解説が多くて読むのが大変ですが、細かな実装の理由なども書いてあるのであとあと応用が利きやすいと思います。一長一短あるのでどちらが良いともいえません。ともに2D,3Dゲーム両方について解説があります。ある程度技術がある人なら、この手の専門書は飛ばし読みすればすぐ読めると思うので何冊か読んでみると良いと思います。そのときには書き方が結構違うので、国内、海外、両方の書籍に当たることをおすすめします。
解像度
Androidは解像度がいろいろで、縦横比もかわるのでなかなかやっかいです。端末によっては横長を基本としている場合もあります。ちなみにエミュレータ上でのことなので実機では違うかもしれませんが、横長レイアウトの端末でスクリーンのwidthをとると長い方の値が返ってくるけど、表示はportraitでちゃんと縦長表示になったりしました。widthの取り方によるのかもしれませんが、注意が必要です。
通常、ゲームの場合は縦横比を柔軟に変えるのは難しいので全体をできるだけ拡大して、無理な部分は余白として黒または背景で埋めてしまうという方法がとられます。
これは一度ゲームのオブジェクトをビットマップに描画し、それを拡大・縮小して実際のスクリーンのcanvasに描画する方法で実現可能です。
ただし、この方法ではタブレットのような大きな画面では文字などがやたらと大きくなっていささか間の抜けたものになってしまうので「三目くらげ」では相対的な座標を使って解像度によってレイアウトを変えるようにしてみました。
複数の解像度の違う端末を持っている方はそれぞれに「三目くらげ」をインストールしてみて比べてみるとどうなっているかがわかると思います。描画オブジェクト毎に拡大・縮小するかも変えているので、単純に拡大するよりは見栄えが良くなっているはずです。
たとえば右上のサウンドボタンはどの端末でも右上隅に出ます。タイトルのボタンはxhdpi以上の解像度なら端末にかかわらず同じ大きさとなり余白の大きさが変わります。タイトルロゴは解像度によって大きさが変わりますが、他のページのテキストは同じ大きさのままです。
相対座標を使ったレイアウトの調整は「三目くらげ」では行き当たりばったりで結構手間がかかってしまいました。楽をしたければすべての描画オブジェクトを1つのクラスから派生してその元クラスで座標と拡大縮小をうまく処理できるように共通化するなどというように、最初の設計からしっかりと考えておく必要があります。
現状、まだまだタブレット端末の普及率が低いので、単純な拡大縮小の方が手間がかからず効果的でしょうが、将来的にはこのようにゲームでも相対的なレイアウトが増えていくのではないかと思っています。
BitmapかDrawableか
Androidでcanvasに描画する方法は2つあり、一つはビットマップをそのまま描画するやりかたで
canvas.drawBitmap(bitmap, srcRect, distRect, paint)
と、このようにキャンバスにビットマップ画像と元画像の位置、描き出す位置、設定などを与えます。
もう一つはBitmapDrawableというオブジェクトを使って描く方法で
drawable.draw(canvas)
とします。位置の調整はmatrixを使ってあらかじめcanvasに設定しておきます。
二つの違いは、ゲーム制作に特に関係する部分を一言で言うと
・Bitmap 高速・拡大縮小に弱い
・Drawable 低速・拡大縮小に強い
となります。
特に描画領域が大きいとDrawableは格段に遅くなります。pngの透明領域も描画領域に入りますので、できるだけ余白の少ない画像の方が良いようです。OpenGLと違って画像サイズは2の倍数でなくて良いので、削れる限り削りましょう。
拡大縮小するとDrawableはなめらか、Bitmapではぎざぎざになります。もちろん、Drawableはなめらかと言っても少しぼやけます。
「三目くらげ」では、パフォーマンスのためにできうる限り拡大縮小せずにBitmpの描画を使い、背景は描画領域が大きく、荒れても目立たないデザインなのでBitmapで、文字は荒れると目立つのでDrawableで、アニメーションしている描画オブジェクトは荒れが目立たないのでBitmapで、と使い分けています。
とはいえ、「目立たない」といってもわからなくなるわけではないので、こだわるならOpenGLを使うか、代表的な解像度にターゲットを絞って複数の画像データを用意して切り替えるのが良いでしょう。
画像データ
canvasはOpenGLと違って画像のサイズが2の倍数の正方形でなくて良いので画像データを用意するのは楽です。ただし、大きな画像データは、たとえその一部しか描画しなくても初回描画時になぜか遅くなります。たとえば「三目くらげ」では、最初はクラゲのアニメのための巨大なpngファイルを用意していたのですが、かならず初回描画時に一瞬処理が止まってしまうため、結局複雑な変形などコマアニメがどうしても必要なところ以外のアニメーションはプログラム的に実装して、pngファイルの大きさを削ることによってその問題を解決しました。データの読み込みはゲーム立ち上げ時に行っておりファイル読み出しのせいではないので、ガーベージコレクタの都合か、それとも何らかの初期化をAndroidが行っているのかはわかりませんが、大きな画像ファイルはパフォーマンスに影響するので、削れるところはどんどんけずりましょう。
ちなみにcanvasへの線や円などの描画とそれに対するフィルター処理は比較的高速です。使えそうなところではそちらを使うと画像ファイルも減らせて便利です。「三目くらげ」でもクラゲの赤いマルや青いペケはそれで描画しています。
Androidでは描画ファイルはresというフォルダに入れると、自動的に解像度とかを選んでくれるので便利ですが、フォルダ構造がプログラマの都合で決められないし、自動的に選んでくれる、というのはゲーム制作のように、画像の解像度など細かなところまで自分で管理したいと言う場合には今ひとつです。assetsというフォルダの下なら自由に階層などを決められます。
テキスト
「三目くらげ」のゲーム中のテキストは基本的にビットマップ画像で用意しています。ただ、「きろく」ページはフォントを用意してそれで書いています。アンチエイリアスを有効にしておかないとかなりぎざぎざになるので、注意が必要です。アンチエイリアスをすると小さなフォントが見えにくくなるので、ボールド書体を使っています。ボールドかどうかはAndroidのデフォルトフォントでは設定で変えられますが、オリジナルのフォントを用意する場合は用意したフォント自体がボールド書体であることが必要です。
終わりに
上記のようないくつかの注意点を守ればOpenGLを使わずにcanvasでも「三目くらげ」のようにアニメーションがそれなりに入ったAndroidのゲームアプリは作ることができます。
もちろん、OpenGLの方が高速なのでいろいろエフェクトを入れたいとか描画オブジェクトが多くなるとcanvasでは無理がありますが、個人制作のパズルゲームなんかだとcanvasの方が簡単に開発できるのではないかと思います。
上記のパフォーマンスの調整が面倒、と思うかもしれませんが、OpenGLでも頂点データの使い回すなど高速化に手間をかけないと、あっとゆうまにガーベージコレクタが走ってカクカクになるので、どんな描画方法でもある程度の調整は必要です。
ということで、なんだかOpenGLの本とか記事とかよく見るけど、canvasも手軽だし十分使えるよと言うお話でした。
でも適材適所であって、どちらがどうと言うこともなく、実際私も今後OpenGLでもたぶんなんかつくりますけどね。