Android始めました
と言っても、何かアプリをリリースした訳じゃないですが、
宿題を出されたので、ライブ壁紙を一生懸命作ってみました。
ライブ壁紙でライフゲーム
最初は「日経ソフトウェア」の記事を写経してたんですが、
まぁ、それでうまくいったんですが、例外が起きてちょっとアレでして。
そこで、googleの公式サイトにもサンプルソースが上がってたので、
今度は、そっちからコードをパクって仕上げました。
まずは、ライフゲームのクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | package your.domain.LifeGameWallpaper; public class LifeGame { private final byte STATE_DEAD = 0 , STATE_STAY = 1 , STATE_BORN = 2 ; private int _width; private int _height; private byte [] _buf; public LifeGame( int w, int h) { _width = w; _height = h; _buf = new byte [ _width * _height ]; for ( int i= 0 ; i<_buf.length; i++) { _buf[i] = STATE_DEAD; } } public int getWidth() { return _width; } public int getHeight() { return _height; } public void setPattern( byte [][] pattern, int offsetX, int offsetY) { int x0 = offsetX; int y0 = offsetY; int x1 = offsetX + pattern[ 0 ].length; int y1 = offsetY + pattern.length; // 上下左右の端の行と列は計算対象外なので、 // データを書き込まないようにクリップ(LifeGameの仕様) if ( x0 <= 0 ) { x0 = 1 ; } if ( y0 <= 0 ) { y0 = 1 ; } if ( _width <= x1 ) { x1 = _width - 1 ; } if ( _height <= y1 ) { y1 = _height - 1 ; } // 上下左右の端の行と列にかぶらない部分を初期化 for ( int iy=y0; iy<y1; iy++) { for ( int ix=x0; ix<x1; ix++) { int idx = (iy * _width) + ix; if ( pattern[(iy - offsetY)][(ix - offsetX)] != 0 ) { _buf[idx] = STATE_STAY; } } } } public void exec( byte [] dst) { // dstに結果を格納 execLifeGame( dst ); // 次の準備 for ( int i= 0 ; i<_buf.length; i++) { _buf[i] = dst[i]; } } private int getAroundOf( int index) { int ret = 0 ; index -= _width; // 1つ前のラインから走査するためのデクリメント for ( int i= 0 ; i< 3 ; i++) { if ( _buf[index - 1 ] != STATE_DEAD ) { ret++; } if ( _buf[index ] != STATE_DEAD ) { ret++; } if ( _buf[index + 1 ] != STATE_DEAD ) { ret++; } index += _width; } return ret; } private void execLifeGame( byte [] dst) { int iy = 1 ; int lineOffset = _width * iy; for (; iy<(_height- 1 ); iy++) { for ( int ix= 1 ; ix<(_width- 1 ); ix++) { int idx = lineOffset + ix; byte state = _buf[idx]; int cnt = getAroundOf( idx ); if ( state == STATE_DEAD ) { state = ( cnt == 3 ) ? STATE_BORN : STATE_DEAD; } else { state = ( (cnt- 1 ) <= 1 || 4 <= (cnt- 1 ) ) ? STATE_DEAD : STATE_STAY; } dst[idx] = state; } lineOffset += _width; } } } |
そもそも、ライフゲームって何かというと、
自分の中心に3×3の計9マスを対象として、
その中心が死滅状態で周囲に3匹の生存していれば誕生、
もしくは中心に1匹と周辺に2〜3匹入れば生命を維持、
中心の他に生命が1匹以下、もしくは4匹以上居れば
過疎、もしくは過密により死滅するというプログラムです。
興味のあるヒトは、「ライフゲーム」検索してください。
例えば、こんな感じ。
で、最初は120×120くらいで計算してたのですが、
Androidシミュレータ上だと100ms以上時間が掛かってて、
描画処理も描画する長方形の数依存で気持ち悪いことになってて、
結果的に80×80を採用することにしました。
あと、初回execが時間掛かるので、
Engineのコンストラクタであらかじめ一回呼ぶようにしました。
一応、実測なんですが、実機にAcerのA500を使って計測したところ、
Androidシミュレータより画面(*1)が広くなって不安だったのですが、
結果的にはライフゲームが10ms以下で、描画処理は3〜5msくらい。
8fpsなら安定して動いてるようです。
あと、描画に必要な座標計算をonSurfaceChangedで行っていたので、
端末の向きが変わっても問題なく動作してました。
そんなこんなで、未だにソースコードを晒すときは
お酒を呑まないと恥ずかしくてやってられないネコでした。
おしまい。
(*1) シミュレータは320×240, A500は1280×800
Leave a Comment