Android用アニメーションの考察メモ

自分メモ。

UIに単純なアニメーションエフェクトを付けるだけであればandroid.view.animationパッケージあたりの機能を使えばいい。Androidアプリで“アニメーション”するための基礎知識あたりを参考。

フリックやピンチイン/アウトでのズームイン/アウトなどを滑らかにアニメーションさせようとするなら独自実装。この辺をどう実装するかという話になると最近のUIプログラミング事情を鑑みて、といったことになるんじゃないか。

2007年にFlex/AIR, Silverlight, JavaFXの3つのRIAの基盤が登場した。当時はこれからのUIはRIAだぜー!みたいに盛り上がっていたけど、2011年現在の感触からするとなんかイマイチ不発な気もする。スマホとかが注目されて影に隠れているのか、AJAXが想定以上に頑張っちゃってRIAの流れが弱まっちゃったのか。

そんなRIAだけども、UIプログラミングとしては進化がみられて、

  • デザイン部分の記述に宣言的な記法を採用し、デザインとプログラムを分離する
  • アニメーションへの対応

といったところが特徴的かなあ。90年代のWindowsプログラミングがC言語で、オブジェクト指向という武器が使えなかった時代からすると、言語もオブジェクト指向を前提とした言語になっているし、やりやすくなったというのはある。

しかし、近年ではUIにアニメーションによる装飾とかが求められはじめて、プログラミング技術的に要求レベルがより高くなった感がある。というか、2000年前後のHTMLでのUIプログラミングがゆとりすぎたという気もするが。

で、アニメーションの部分の設計方針の話。基本的にWindowsのUIプログラミングなどはイベント駆動方式なのでイベントループを作って待ちの姿勢で処理するわけで、アニメーションさせる場合は定時でイベントを発生させるようにして処理をするという感じになっていた。

Javaの場合、アニメーションとかやろうとしてThreadを作って定期的に呼び出しをする設計にしちゃう人が続出してマルチスレッドプログラミングの落とし穴に大量に嵌るという感じだった。JavaのSwingのライブラリはUI関連の処理イベントはキューにいれられ、シングルスレッドで順次処理されるモデルをとっているのだけど、このEDT(イベントディスパッチスレッド)の扱い方を理解している人は相変わらず少ない。近年は良書がでてきたからマシにはなったけど、下手なところにUI作らせるとひどい事になる。

結局、アニメーションをさせる場合は、ゲームプログラミングで使われているように定期的に(ゲームの場合は主に1/60秒だがこれはモニタのリフレッシュレートに依る。これ以上の速度で絵を作ってもモニタには描画されないから意味が無い)描画フレームを描く方針が作り易い。WindowsでのUIプログラミングとかでゲームなどを作る場合、結局これを独自にみなが実装してたってわけだ。

AdobeFlex/AIRの場合はFlashからの流れか、アニメーションフレームを管理するフレームワークとなっていて、やはりアニメーションをさせるならゲームプログラミングで広く取られている手法が無難であることが伺える。

固定フレームと可変フレーム

アニメーションをさせる場合に、その描画フレームは時間を固定とするべきか、可変とするべきか。

古典力学で考えるなら、描画対象オブジェクトが配置されたら、未来の状態は決定している。だから時間を引数に与えたらその状態がとれるようにプログラムし、それを描画すればいいじゃないか、という考えを持ってしまう。このアイデアは一般にうまくいかない。

ユーザの入力系による外部撹乱があるわけで、未来は決定的ではないということ。それと、もしも衝突判定によってバウンドするとか爆発するとかそういう仕掛けを入れたい場合、可変時間でやるとかなり大変なことになる。

1フレームの時間を固定するというのは「妥当な手抜き」であると思う。弊害としてはゲームにおける「処理落ち」のような状態が発生してしまうこと。しかし、1フレームの時間を可変とした場合の力学の計算のあまりの複雑化と天秤にかけたならば、処理落ちぐらいいいじゃないかと言いたくなる。

それぐらい、衝突判定がやっかいになるということだ。逆に衝突とか計算しないなら別にそれほど問題はない。等速直線運動しかしないオブジェクトしかないなら問題はないと思うよ。

衝突

衝突についてはそれだけで多くの書籍が出されているし、山のような研究論文がみつかることだろう。真面目に衝突判定をやると難しい。

固定フレームにしたところで、動きの速いオブジェクト同士がぶつからずにすり抜けるという現象がよく生じる。もし、時間に最小単位があって、あらゆる物体が空間の最小単位でしか動かないのであればすり抜けとかあまり気にしなくていいんだけどね。それでもゼノンのパラドックスの「競技場」のような状況で最小単位の物体同士がすり抜けるとかはあるか。いわんや、最小単位ではない処理フレームをや。

そのへんの「難しい」部分に真っ向から取り掛かったのがいわゆる物理演算エンジンと呼ばれる代物で、ヘビィなものだということ。2次元で矩形(くけい。専門用語だがつまるところ四角形のことだよ)だけを扱ったとしても、回転する剛体の四角形の衝突とその動きをシミュレーションしようとしたら泣きながら数式と闘うはめになる。やってられっか。

というわけで、Androidとかのスマートフォンのアプリで、指でスライドさせるUIを作った場合にちょっと力学を再現したっぽい感触にしようとしたとしても、大真面目に物理演算エンジンなんかに手を出すと大掛かりになりすぎるので、どこでうまく手抜きしてそれっぽく見せるかという話になって。

ああ、これってまんまゲームプログラミングだなあ、と思うに至るわけですよ。

相互作用のない、つまり衝突しない幽霊のような物体であれば、等速直線運動や等加速度運動を表現するのはさして難しくない。摩擦の効いた物体の運動、例えるなら机の上でおはじきを滑らせたような。おはじきを衝突させようとすると急に難しくなる。円と円の衝突ならまだ難易度は低いとはいえね。

だから、スマホでよく用いられている、背景を指でスライドスクロールするUIというのは、それだけではそこまで難しくない。一番端まできたらバウンドするぐらいもどうにかなるだろう。相互作用を及ぼすオブジェクトが2つに増えるともうダメだ。そのぐらいを落とし所としておくのがよいと思う。

オブジェクトの座標と時間の更新

ゲームプログラミングによるフレームってのは、漸化式のようなモノにみえてくる。「今」の状態。そしてユーザの入力。これから次のフレームの状態を求める。

この「系」に存在するオブジェクトをプログラムのクラスとそのインスタンスで表現したとしよう。オブジェクトは座標を持つ。加速度も持つとしよう。座標と加速度から、次のフレームの状態に推移するプログラムを、このクラスのメンバ関数として定義してよいだろうか?

オブジェクト同士が相互作用のない「系」ならこれはアリだと思う。

ところが、オブジェクト同士が相互作用、つまり衝突するなどする場合、このメンバ関数の内部では「系」のあらゆるオブジェクトへの参照を行えなくてはならない。「見えすぎる」状態じゃないといけなくなる、と思う。

とすれば、オブジェクトは状態だけを持って、状態の更新(もっとも状態を普遍にして「系」から次のフレームの「系」を作るほうが関数型言語的でセーフティな設計だろうが)は「系」が行うという設計にするのが妥当だろう。

…という結論にいたるまで妙にハマって設計をなんども書きなおしたりしてた。ので、未来の僕が同じことを悩んで時間をムダにしないためにこのメモを残す。