ライブ壁紙でライフゲーム
前回はぶっきらぼうにライフゲームのコードだけ載せたので、
今回はライブ壁紙のコードも晒します。
ポイントは以下の通り
- LifeGameWallpaperEngineのコンストラクタで_lifeGameを初期化
- onSurfaceChangedで描画設定を初期化
- drawMainでイロイロしてる
あと、端末の向きが変わるとonSurfaceChangedが呼ばれるので、
ライフゲームの計算結果は引き継いだまま続行されるのがミソです。
ちなみに、このソースコードはgoogleの公式サイトからパクってます。
サンプルコード一覧のここ。
で、サンプルコードにあったタッチイベントを利用して、
そのタッチした場所に生命を誕生させるなんていうのは、
touchProcessに実装しています。
package your.domain.LifeGameWallpaper; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.os.Handler; import android.service.wallpaper.WallpaperService; //import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; public class LifeGameWallpaper extends WallpaperService { private final Handler _handler = new Handler(); @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } @Override public Engine onCreateEngine() { // Log.d( "lifegame", "onCreateEngine" ); return new LifeGameWallpaperEngine(); } class LifeGameWallpaperEngine extends Engine { private final long DRAW_INTERVAL = 1000 / 8; // 描画間隔 private int _screenW, _screenH; private int _nx, _ny; private int _x0, _y0; private int _size; private Point _offset = new Point( 0, 0 ); private Paint _paintBorn, _paintStay; private LifeGame _lifeGame; private byte[] _dst; private float _touchX, _touchY; private final Runnable _drawLifeGame = new Runnable() { public void run() { drawMain(); } }; private boolean _visible; public LifeGameWallpaperEngine() { // ライフゲームの計算量に直結するので、動作を見ながらもっさりしないように決める _lifeGame = new LifeGame( 80, 80 ); _dst = new byte[ _lifeGame.getWidth() * _lifeGame.getHeight() ]; { // 初期パターンのコピー final byte[][][] patterns = { { // ドングリ { 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 1, 0, 0, 0 }, { 1, 1, 0, 0, 1, 1, 1 } }, { // ダイハード { 0, 0, 0, 0, 0, 0, 1, 0 }, { 1, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 1, 1, 1 } } }; byte[][] pattern = patterns[0]; int offsetX = (_lifeGame.getWidth() - pattern[0].length) / 2; int offsetY = (_lifeGame.getHeight() - pattern.length) / 2; _lifeGame.setPattern( pattern, offsetX, offsetY ); } _lifeGame.exec( _dst ); _paintBorn = new Paint(); _paintBorn.setColor( Color.RED ); _paintStay = new Paint(); _paintStay.setColor( Color.YELLOW ); // 無効な座標で初期化 _touchX = _touchY = -1f; } @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate( surfaceHolder ); setTouchEventsEnabled( true ); } @Override public void onDestroy() { super.onDestroy(); _handler.removeCallbacks( _drawLifeGame ); } @Override public void onVisibilityChanged(boolean visible) { _visible = visible; if ( _visible ) { drawMain(); } else { _handler.removeCallbacks( _drawLifeGame ); } } @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { super.onSurfaceChanged( holder, format, width, height ); _screenW = width; _screenH = height; final int SIZE_MIN = 8; { // 端数切り上げしないと、 // 描画可能な要素数の計算がライフゲームの幅、もしくは高さを越えてしまう int w = (int)Math.ceil( (float)_screenW / _lifeGame.getWidth() ); int h = (int)Math.ceil( (float)_screenH / _lifeGame.getHeight() ); // 長い方を正方形の高さとして採用する _size = ( w < h ) ? h : w; // 高さのクリップ if ( _size < SIZE_MIN ) { _size = SIZE_MIN; } } // ライフゲーム空間のどの範囲を描画するか確定する _nx = _screenW / _size; _ny = _screenH / _size; _x0 = (_lifeGame.getWidth() - _nx) / 2; _y0 = (_lifeGame.getHeight() - _ny) / 2; // 描画対象の中心に描画するための描画開始座標を計算 _offset = new Point( (_screenW - (_nx * _size)) / 2, (_screenH - (_ny * _size)) / 2 ); /* Log.d( "onSurfaceChanged", "_screenW = " + _screenW ); Log.d( "onSurfaceChanged", "_screenH = " + _screenH ); Log.d( "onSurfaceChanged", "_size = " + _size ); Log.d( "onSurfaceChanged", "_nx = " + _nx ); Log.d( "onSurfaceChanged", "_ny = " + _ny ); Log.d( "onSurfaceChanged", "_x0 = " + _x0 ); Log.d( "onSurfaceChanged", "_y0 = " + _y0 ); Log.d( "onSurfaceChanged", "_offset.x = " + _offset.x ); Log.d( "onSurfaceChanged", "_offset.y = " + _offset.y ); */ drawMain(); } @Override public void onSurfaceCreated(SurfaceHolder holder) { super.onSurfaceCreated( holder ); } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { super.onSurfaceDestroyed( holder ); _visible = false; _handler.removeCallbacks( _drawLifeGame ); } @Override public void onOffsetsChanged( float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) { } @Override public void onTouchEvent(MotionEvent event) { if ( event.getAction() == MotionEvent.ACTION_DOWN ) { _touchX = event.getX(); _touchY = event.getY(); // Log.d( "touch", "X = " + _touchX + ", Y = " + _touchY ); } super.onTouchEvent( event ); } private void drawMain() { final SurfaceHolder holder = getSurfaceHolder(); Canvas c = null; long begin = 0, endDraw = 0, endLifeGame = 0; try { touchProcess(); c = holder.lockCanvas(); if (c != null) { begin = System.currentTimeMillis(); _lifeGame.exec( _dst ); endLifeGame = System.currentTimeMillis(); drawRectangles( c ); endDraw = System.currentTimeMillis(); } } finally { if ( c != null ) { holder.unlockCanvasAndPost( c ); } } // Log.d( "lifegame", "becnch = " + (endLifeGame - begin) ); // Log.d( "draw", "becnch = " + (endDraw - begin) ); // Reschedule the next redraw _handler.removeCallbacks( _drawLifeGame ); if ( _visible ) { long delay = DRAW_INTERVAL - (endDraw - begin); if ( delay < 0 ) { delay = 0; // clip } _handler.postDelayed( _drawLifeGame, delay ); } } private void drawRectangles(Canvas c) { c.drawColor( Color.BLACK ); // Clear for (int iy=0; iy<_ny; iy++) { final float n = _size; float offsetY = (float)( _offset.y + (iy * _size) ); float offsetX = (float)( _offset.x ); int lineOffset = _lifeGame.getWidth() * (_y0 + iy); for (int ix=0; ix<_nx; ix++) { int status = _dst[ lineOffset + _x0 + ix ]; if ( status != 0 ) { final Paint paint = ( status == 1 ) ? _paintStay : _paintBorn; c.drawRect( offsetX, offsetY, (offsetX + n - 1), (offsetY + n - 1), paint ); } offsetX += n; } } } private void touchProcess() { if ( _touchX < 0f || _touchY < 0f ) { return; } final byte[][] pattern = { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } }; float x = (_touchX - (float)_offset.x) / (float)_size; float y = (_touchY - (float)_offset.y) / (float)_size; int offsetX = _x0 + (int)x - (pattern[0].length / 2); int offsetY = _y0 + (int)y - (pattern.length / 2); _lifeGame.setPattern( pattern, offsetX, offsetY ); // タッチした座標の破棄 _touchX = _touchY = -1f; } } }
こんな時間なので、まだ呑んでないよ。
おしまい。
Leave a Comment