はじめに
少し前になりますが、東京メトロでオープンデータ活用コンテストがありまして、そこに出した下記Androidアプリの一部を担当しました。
これのTOP画面について、「オープンデータを使って動的にアニメーションするものを、いい感じに作って」という無茶ぶりからはじまり、2日くらいで色々やって、デザイナーの方にもお忙しい中手伝って頂き(ありがとうございます!)、下記の形に落ち着きました。
動きに関しては実際インストールするか、動画もありますのでご覧ください。
これは現在走行中の各路線の列車数を取得し、その分だけ路線色の円をぐるぐる動かしてます。
今回はこの実装方法の簡易版を用意したので、それについて説明します。
libGDX
今回制作するにあたって、libGDXを利用しました。
これはゲーム用ライブラリなのですが、描画に関してはActivityかViewに対して行えて、今回他の方が作る部分にマージしなければならなかったので採用しました。
あのIngressもこのlibGDXを使って作られてるらしいです、ただ海外だと結構流行ってるのですが、日本だとそうでもなくて、日本語資料があまりないのが辛い所でした。
実装
プロジェクトの作り方については、下記を参考にして下さい、基本的にはlibGDXをダウンロードして、プロジェクトに入れるだけです。
まず、libGDXのviewを表示するために、Activityのlayout.xmlに下記を追加します。
1 2 3 4 5 |
<RelativeLayout android:id="@+id/gdx_view" android:layout_width="fill_parent" android:layout_height="fill_parent" > </RelativeLayout> |
次に表示するActivityの継承クラスをAndroidApplicationに変更して、先ほどの追加したレイアウトにlibGDXのViewを貼り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 |
public class TopActivity extends AndroidApplication { ... 略 ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.top); RelativeLayout r = (RelativeLayout) findViewById(R.id.gdx_view); _drawListener = new MainListener(this); View view = initializeForView(_drawListener, new MainConfigration()); r.addView(view); } |
次にassetsフォルダに表示する画像を入れます、今回はこのブログのキャラクターである下記画像を入れました。
そしてMainConfigrationとMainListenerを実装します。
MainConfigrationはlibGDXの設定です、今回はほぼ意味のないものですが、デフォルトの設定を変えたい場合はここをいじります。
1 2 3 4 5 |
public class MainConfigration extends AndroidApplicationConfiguration { public MainConfigration() { this.hideStatusBar = false; } } |
MainListenerで描画を行います、本来であればステージの制御だったり、シーンの制御だったりと色々ゲームの制御をしますが今回は描画だけです。
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 |
public class MainListener implements ApplicationListener{ private final int DRAW_COUNT = 3; private SpriteBatch _batch; private Texture _drawTextture; private ArrayList<CalcDrawer> cdList; public MainListener(Activity activity) { // ディスプレイサイズを取得し、表示位置を決める DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); int width = metrics.widthPixels; int height = metrics.heightPixels; float cx = width / 2; float cy = height / 2; cdList = new ArrayList<CalcDrawer>(); Random rnd = new Random(); // DRAW_COUNT分ランダム配置 for (int j = 0 ; j < DRAW_COUNT ; j++) { float r = rnd.nextInt(500) + 30f; float rad = rnd.nextInt(360); float size = rnd.nextInt(60) + 10; float speed = rnd.nextInt(3) + 1; float moveCX = rnd.nextInt(250); if (rnd.nextInt(2) == 1) { moveCX *= -1.0f; } float moveCY = rnd.nextInt(250); if (rnd.nextInt(2) == 1) { moveCY *= -1.0f; } cdList.add(new CalcDrawer(r, cx - moveCX, cy - moveCY, rad, size, size, speed)); } } @Override public void create() { // テクスチャ等の準備 _drawTextture = new Texture(Gdx.files.internal("techun.png")); _batch = new SpriteBatch(); } @Override public void render() { // 描画を行う、renderメソッドは繰り返し呼び出れる Gdx.gl.glClearColor(0, 0, 0.0f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); _batch.begin(); for (int i = 0 ; i < cdList.size() ; i++) { CalcDrawer cd = cdList.get(i); cd.calcWithDelta(Gdx.graphics.getDeltaTime()); Color c = _batch.getColor(); CalcData cData2 = cd.getDelayCD(2); _batch.setColor(c.r, c.g, c.b, cd.cd.alpha * 0.5f); _batch.draw(_drawTextture, cData2.x, cData2.y, cData2.width, cData2.height); CalcData cData1 = cd.getDelayCD(1); _batch.setColor(c.r, c.g, c.b, cd.cd.alpha * 0.7f); _batch.draw(_drawTextture, cData1.x, cData1.y, cData1.width, cData1.height); _batch.setColor(c.r, c.g, c.b, cd.cd.alpha * 1.0f); _batch.draw(_drawTextture, cd.cd.x, cd.cd.y, cd.cd.width, cd.cd.height); cdList.set(i, cd); } _batch.end(); } @Override public void dispose() { } @Override public void pause() { } @Override public void resize(int arg0, int arg1) { } @Override public void resume() { } } |
そして、描画の位置を計算するクラスを実装します、色々やってますが簡単にいうと、描画の度に経過した時間がもらえるので、それを元に位置をずらしています。
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 |
public class CalcDrawer { float _r; float _rad; float _cx = 0; float _cy = 0; float _maxSpeed = 1; float _currentSpeed = 1; float _alpha = 0.0f; int idx = 0; CalcData cd = new CalcData(); public CalcDrawer(float r, float cx, float cy, float rad, float width, float height, float speed) { _r = r; _cx = cx; _cy = cy; _rad = rad; cd.width = width; cd.height = height; _maxSpeed = speed * (1 - (_r / 500) + 0.5f); } public void calcWithDelta(double dlt) { // dlt: 描画までにかかった時間 idx++; if (idx > 1000) { idx = 0; } // sin波で回転スピードに緩急をつける _currentSpeed = (float) (_maxSpeed * Math.cos(2 * Math.PI * (double) idx / 1000)) + _maxSpeed + 0.2f; _rad -= Gdx.graphics.getDeltaTime() * _currentSpeed; if (_rad < 0f) { _rad = 360f; } float tx = (float) (_cx + _r * Math.cos(_rad)); float ty = (float) (_cy + _r * Math.sin(_rad)); cd.x = tx; cd.y = ty; if (_alpha < 1.0f) { _alpha += 0.01f; } cd.alpha = _alpha; } // index分だけ前の位置を返す public CalcData getDelayCD(int index) { CalcData calcData = new CalcData(); float rad = _rad + Gdx.graphics.getDeltaTime() * _currentSpeed * index * 1.5f; float tx = (float) (_cx + _r * Math.cos(rad)); float ty = (float) (_cy + _r * Math.sin(rad)); calcData.x = tx; calcData.y = ty; calcData.width = cd.width; calcData.height = cd.height; calcData.alpha = cd.alpha; return calcData; } // 描画の際に利用するデータクラス public class CalcData { public float x = 0; public float y = 0; public float width = 0; public float height = 0; public float alpha = 1.0f; } } |
これらを実装するとキャラクターをぐるぐると表示することができます。
動かないのでわかりづらいですが、実行結果のスクリーンショットです。
最後に
私はOpenGLは詳しくないのですが、ligGDXを使うと色々と小難しい部分はlibGDXが吸収してくれるので、今回は大分助かりました。
この記事では画面が更新される度に位置をずらしていますが、iOSのように(0, 0) から (100, 0)まで、何秒で移動みたいな関数もあって楽しいライブラリですので、ぜひやってみてください。