Androidアプリ開発したい!

IT屋がGalaxySを手に入れてからAndroidに興味が湧き、いろいろと開発したりしてます。 GalaxyNexusがメイン機となり、ようやく普及し始めたAndroid4.0への関心が高いです。 仕事では、iPhone向けとかのソシャゲ作ってます。

OpenGL

OpenGLで徹底的に性能改善して30FPS超えを目指すで!!!

今回からは、
「OpenGLで作るAndroidSDKゲームプログラミング」本を読みながら
OpenGLでの性能改善のについて学びます。

そもそもAndroidアプリケーションが遅い理由は?
最新のAndroid端末に搭載されているCPUやGPUのスペックはかなり高性能なのにもかかわらず・・・
以下の理由が大きいようだ。
  • AndroidアプリはVM上で動作するため
  • メモリ管理をガベージコレクション(GC)に任せているため
C言語かC++でNDK開発すれば違うんだろうけど、
まだまだJavaの恩恵を受けたい今日この頃。


では、どう改善すればよいか?

と、その前にまずはFPS(Frame Per Second)を表示させて、
改良の基準にするようです。
1秒間に何回画面を描画したかを表す数字で、一般的に30FPS程度で合格。


【ハエたたきでFPSを表示する】
とりあえず表示したんだが・・・すでに40〜50FPSという現状。

現在時刻 - 計測開始時間が1000ミリ秒を超えた時点で、
それまでに計測したフレーム数をFPS値とするやり方。

以下はスクリーンショットを取る処理を含めて40FPS。
シャッター時に10くらい減る気がする。

SC20120623-124355.png

さすがGalaxySさんやで!
Android OSのバージョン2.3くらいから性能改善あったからなぁ〜

しかし、まあ30FPSは達成しているものの可能な限り高いFPSを出したいので、
本書の流れにそって最適化していきます。
  • 無駄な処理、コストの大きい処理をなくす
  • GCをなるべく動かさないようにする
  • OpenGLの機能を活用する
というわけでとりあえず性能改善手法として挙げられていて以下を行った結果。
いわゆるJavaのデザインパターンであるシングルトンパターンを適用する感じです。

・頂点座標の再利用

SC20120623-135128.png

う〜ん誤差の範囲の変化としか思えない・・・

・ハッシュテーブルの活用
・バッファの再利用

SC20120623-152345.png

これも多少は改善・・・?したと思うが劇的な変化はしないから難しい。


パーティクルシステムも同様に
先ほど性能改善版として用意したメソッドを呼び出すように修正。

SC20120623-161113.png

Maxが57FPSでスクリーンショット時でも50FPSを超えた!?
性能改善しているようだ。
確かにパーティクルシステムは100個くらいテクスチャを表示しているから効果は抜群というわけか。

【その他の性能改善方法】
AndroidはJava言語がベースということで、基本的にJavaにおける性能改善方法が有効。

  • ローカル変数にキャッシュ
    • ループの条件で直接list.size()とかやらないで一旦int変数に格納して参照することで取得コストを減らす

  • インスタンス変数のキャッシュ
    • 何度も呼び出されるRandomとかTargetsとかローカル変数にキャッシュすることで取得や格納コストを減らす

  • staticメソッドの利用
    • DalvikVMでは、インスタンスメソッドよりstaticメソッドの呼び出しのほうがコストが小さくなるらしい

  • 定数はstatic finalで定義する
    • static finalで定義しないとDalvikVM上で定数として扱われないため、アクセスコストが増える

  • 圧縮テクスチャを使用する
    • メモリ効率がよくなるが、画質が悪くなる※取り扱い注意

  • JNIを使ってネイティブで処理する
    • 呼び出しコストは大きいが、ネイティブで処理するため演算速度が速い
    • 計算量の多いメソッドをネイティブ化すると効果が得られる

ってな感じで性能改善編の4章はこれにて終了。
しかし、この1FPSを削る作業で、
ゲーム業界の方々は日々頑張っているのだろうと思うとすごい気がしてきた。
(もしや他人事じゃない・・・?)

次回は、いよいよ3D?と思ってたが、
いきなり3Dはハードルが高いので、まずは2Dで原型を作ってから3D化するようです。




パーティクルを動かして破棄する

前回に引き続き
「OpenGLで作るAndroidSDKゲームプログラミング」本を読みながら
OpenGLでのパーティクルシステムについて学びます。

今回は以下を行うことでフェードインとフェードアウトを実現し、
ハエたたきゲームにパーティクルを実装します。

・パーティクルを動かす
・パーティクルを破棄する

そのためには、パーティクルの生存を管理する必要があり、
フレーム数で生成からの時間と寿命をパーティクルごとに保持します。

フェードアウト、フェードイン効果を加えたので、
ようやくそれっぽい動きになりました。
(フェードインとフェードアウトの仕組みは表示の寿命と現在の表示時間の比率から透過率を決めているだけ)

SC20120623-020941.png

そんでもって
パーティクルをハエたたきに適用したのが、
以下になります。

・ハエの移動の軌跡表現
・ハエの撃墜時の放出表現

SC20120623-092355.png

GitHub:

というわけでパーティクルの3章はサクッと終了。
まだ何とか理解できるレベルで助かる。

次の4章は性能改善。
ここまで性能無視して実現が簡単な方法でやってきたので、
そろそろ本番ですかね・・・

派手なエフェクト効果を簡単に表現するパーティクルシステム

しばらくMacでのObjective-Cの勉強をしていたので、
OpenGLストップしてました。 

というわけで、
「OpenGLで作るAndroidSDKゲームプログラミング」本を読みながら
OpenGLでのゲーム開発を学びます。

今回から3章のパーティクルシステムになります。

パーティクルシステムとは・・・・
煙、炎、霧、爆発など手作業で描こうとすると手間がかかる表現を簡単に行うための技術
パーティクル(粒子)と呼ばれる細かいテクスチャを大量に描画することで、不定形な要素を描画する。

メリット:
手作業では難しい複雑な動きが表現できる

デメリット:
処理に多くのリソースが必要
⇒パーティクルの数が増えると、表示がカクカクしたり入力に対する反応が悪くなる


パーティクルの描画時には、アルファブレンディングを有効にして加算合成します。
やってることは、前回のハエたたきで1つのテクスチャを元に複数のハエを表示したのと同じ要領でやってます。

1つのテクスチャを100個くらいアルファブレンディングで加算合成させて、
大きさ・位置などをランダムに設定して表示してるというわけです。


元のテクスチャ

particle.png

パーティクルシステムで表示

device-2012-06-22-200322.png


GitHub:


いわゆるエフェクト効果ってやつですかね。
これをうまく使えるようになれば、
キャラクターが攻撃されたときとかの動きがいろいろ表現できて面白そうだ。




Android OpenGLハンズオンその6(2章完)

【音楽の再生】

サウンドを再生する方法、主に以下の2つ。
  • MediaPlayer(BGM向け)
    データをストリームで管理し、必要に応じてバッファに読み込んでは破棄していくため、
    BGMのように比較的大きいファイルを扱うのに有利

  • SoundPool(SE向け)
    データをメモリに保持するため、メモリ負担が大きいがその分、
    呼び出しから再生までの遅延が少ないため、サウンドエフェクト(SE)の再生に適している。

/res/raw/フォルダ下にサウンドファイルを配置する。

撃墜時にSEを鳴らすのとゲーム開始時にBGMを再生する処理を追加するだけで、
とくに難しいこともなかった。

【バックグラウンドからの復帰時にゲームを再開】
これもOpenGLとか関係なく、まずAndroidのライフサイクルを理解している必要があります。

ゲームプレイ中にほかのアプリケーションの割り込み(電話など)で起動中のゲームがバックグラウンドになることを考慮する。
このハエ叩きに関しては、バックグラウンドになっても制限時間が減り続けてしまうので、
まずそれを改善します。

・バックグラウンドになったタイミングの時刻を覚えておく
・バックグラウンドから復帰したタイミングでバックグラウンドの時間を計算
・ゲーム内で経過時間を計算する際に、バックグラウンドの時間を除外する
・OSによって破棄された場合、Activityを作り直す

Androidではバックグラウンドで動いているアプリケーションはOSによって勝手に破棄される可能性があるため、一度破棄されてしまうとActivityを作り直す必要がある
⇒onSaveInstanceStateを使ってActivityが破棄される前にアプリの状態を保持しておく。

【ハードウェアボタンを制御】
ハードウェアボタンの誤操作によるゲームの停止とかで厄介なものとなる。

・Homeボタン
・Backボタン
・Menuボタン
・Searchボタン

Homeボタンを無視するのは難しいらしい。
とりあえずBackボタンだけ無視します。

スクリーンショット 2012-06-19 10.53.50.png

GitHub:

今回はアプリの見た目とかはかわらない修正なので、
画像なしです!
とりあえずこれで長かった2章も終わって。
サンプルだけど一応ゲームアプリ開発は完了です。



Android OpenGLハンズオンその5(2章-3)

前回に引き続き
「OpenGLで作るAndroidSDKゲームプログラミング」本を読みながら
OpenGLでのゲーム開発を学びます。

今回は以下の内容になります。
  • 得点を表示する
  • 残り時間を表示する
  • ゲーム状態を管理してゲームオーバー後の処理を行う
  • リトライボタンを追加

【得点を表示・残り時間を表示】

撃墜ごとに100点を変数を保持し、
1章の時に作った数字を表示する処理で左上に得点を描画します。

残り時間も得点とほとんど同じで、
起動時に保持した時刻から描画毎に時刻を減算した結果を元に
残り時間を計算して表示しているだけです。

SC20120616-195114.png

【ゲームオーバー後の処理】
さっきの残り時間の計算で0になったらゲームオーバーフラグをONにして、
ゲームオーバーのテクスチャを表示する。
また、ゲームオーバー後は当たり判定を無効にする処理をtouched()に入れるだけ。

とても簡単です。

SC20120616-200419.png

【リトライボタンを追加】
ゲームオーバー後にゲームを再開できるようにするボタン。
ボタンを押した時の処理としては、ゲーム開始時の処理を呼び出すだけ。

ボタンはテクスチャではなく、FraneLayoutでButtonのviewを配置して、
ゲームオーバーになるまで非表示する方法で実装。

SC20120616-220729.png


実はこのリトライボタンの表示/非表示で
ちょっとハマったのでメモ。

書籍通りに記載しても表示/非表示がうまくいかないので、色々試してみたら、
Renderer側のonSurfaceChanged()が呼び出される前にsetVisibility()でView.INVISIBLEを指定し、
ボタンを非表示にすると、その後HandlerなどでUIスレッドにてsetVisibility()でView.VISIBLEを指定し、
ボタンを表示しようとしても出てこない(押せるから、見えないだけ?)

GlSurfaceViewのバグなのか?それとも重ねる順番なのか・・・?
ちょっと難しいので、とりあえずカッコイイ方法とは言えないが、
Activity生成時の初回起動時は、画面外にボタンを配置して擬似的に見えないようにする。
ゲームーオーバー後は、表示する際は画面内にボタンを配置してから
setVisibility()で表示/非表示を切り替えるように対応。

以下にソースコードの一部を記載。



GitHub:


とりあえずサウンドとライフサイクル周りの実装がまだだけど、
ようやくゲームらしくなってきたので、再び動画です。
録画しながらだから700点しかとれてないw

持ってる端末
・Galaxy S root化済    (2.3.6)
・Galaxy Nexus          (4.1.1)
・GALAPAGOS A01SH (4.0.4)
・ONDA Vi10              (4.0.3) 
・iPod Touch           (iOS 6.0)
記事検索
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

  • ライブドアブログ