AppStoreにアップする(9)

 今回はサイコロでオーナーを決める部分の話っす。
 純粋に計算ばっかで、あんまiPhoneのAPIと関係ないんだけど…

 前回で任意の数の写真をクルクル回せるようになったわけですが、最終的にはオーナーになる写真で止まるようにしないといけないわけです。

 どうやってオーナーを決めるかというと
 オーナー = rand() % 人数

 以上。
 な、わけですな。

 rand()てのは、これまでさんざん使いたおしてきた0~RAND_MAX(Xcodeだと0x7fffffffL)までの数をランダムに発生させる関数っすね。
 で、
 % 人数

 というのは、人数で割った余り、ちゅー事です。こうすればどんな数字でも
 0 ~ (人数 - 1)

 の範囲の数字になるわけですわ。
 写真の配列は0番から始まるんで、うまく範囲が一致するでしょ。

 で、オーナー(写真の番号)がこれで決まったから、後はこの番号の写真で止まればいいわけです。
 サイコロは90度ごとに別の面が正面を向くわけだから
 止まるべき角度 = オーナー番号 * 90

 という計算で止まるべき角度が導きだされ、後はその角度に向かって回り続ければいいと。
 そういうわけで、前回はmovingPhase_RotateかmovingPhase_Idleの2つの相しか使ってなかったけど、今回はこれにmovingPhase_Stopつー相が加わるわけですね。

テン*シー*シー-1

 でも、ギュルンギュルン回ってて、その角度になったらいきなり止まるってのも芸が無いわけですよ。

テン*シー*シー-2
こんな感じでずっと同じスピードで回ってて、突然止まるってのもちょっとね

 できれば、コマのようにゆっくりと回転速度が落ちて、最後には止まるって動きを演出したいわけです。

テン*シー*シー-3

 欲をいうと、たまには行き過ぎて戻ってくるとかの演出も欲しいぞっと。

テン*シー*シー-4

 そうなると、movingPhase_Rotateの他にmovingPhase_Turnって相も必要になるわけですな。
 これでようやくDiceGL.hで定義してる
enum {
movingPhase_Idle, // オーナーが決まっていなくてアイドル状態
movingPhase_Rotate, // 回転中
movingPhase_Turn, // 行き過ぎたので戻っている最中
movingPhase_Stop // オーナーを表示中
};

 を全部使う状況になったわけです。

 というわけで、movingPhase_Rotate時の初速度、だんだん遅くするための負の加速度、最終的な停止角度、ここらへんを計算してるのがDiceGL.mのprepareRotate:メソッドになります。

 ちなみにパラメータでもらってる(double)inDeltaは今のところ1.0です。
 こいつはEAGLViewのthrowDice:メソッド呼び出しのところで1.0として渡してるわけですが、リリース版ではユーザーのスワイプの方向や強さで値を変化させとります。
 今回のdrawメソッドではglRotatefにangleじゃなくrotationDirectionてインスタンス変数をかけた値を渡してるけど、こいつが回転方向を決定するわけですな。
 rotationDirectionはinDeltaが正の値、負の値で1.0か-1.0の値をとるようにしてます。
 これでinDeltaが負の場合は、画面上の回転が反転するわけです。
 ただし結局画面上の回転が反転するだけで、写真の送りは順送りなままってのがすステキに手抜き。

 resultIndexってのが、くじで決まるオーナーさんの番号なわけで、こいつに90度かけたのが、止まるべき角度ってことになるんですな。
 ただし停止角度は、一度オーナーを決めた状態から再度回転させられた場合を考えて、現在の角度を考慮したりもしてます。
 あと、行き過ぎる角度をturnangleに設定。こいつは0~89度までの範囲でランダムに決めてます。
 で、45度以上だと行き過ぎで戻るんじゃなく、到達角度に届かないので、もう少し進ますって動作にするわけっす。
 しかも、単純に等加速で減速するのも面白くないってんで、止まるちょっと前くらいから止まりそうで止まらないって演出入れるためのmarginIndexってのも使ったりしてるので、かなりごちゃごちゃした処理になっとります。

 まあ、ここらへんはiPhoneのAPIと関係ないんで細かくは説明しません。興味ある人は、ソースのコメントをたよりに調べてみて下さい。
 ちなみにprepareRotate:メソッドでは、3秒間くらいで止まるように計算してたつもりだけど、実際は10秒以上回転しちゃう事に…
 なんか、根本的に間違ってるっぽいけど、修正せずにそのまま使う事にしますた。
 prepareRotate:メソッドで
 deltaAngle:初速度
 deltaDeltaAngle:加速度

 を決めて、rotationメソッドで
 deltaTime:回転開始時からの経過時間

 を更新しつつ
double append = deltaAngle + deltaDeltaAngle * deltaTime;

 という計算式で
angle

 を更新してるわけですな。
 marginIndexを使ってのじらし作戦もここでやっとります。余計時間かかるわ。
 
 rotationメソッドの先頭でやってるのが、到達角度を行き過ぎたら、最終的な角度
adjustedFinalAngle

 を計算して0.5秒でその角度になるように
deltaAngle

 を計算し直してmovingPhase_Turnの段階に移行するちゅー作業。
 たまにピッタリの位置で止まる場合があるんで、その時はmovingPhase_Turnはすっ飛ばしてmovingPhase_Stopに移ったりもしてます。
 実はこいつに気づいたのは確認作業中。
 たまに、サイコロは止まってるのに、いつまでも確定にならない事があって気づいたんだよね。

 今回は、前回からの変更部分に
#if THROW_ANIMATION

 を使ってます。これだと
#ifdef THROW_ANIMATION

 と違ってTHROW_ANIMATIONが定義されているかではなく、値が0かそれ以外かで判断するようになるんですな。
#if THROW_ANIMATION == 2

 なんて書き方もできるので、#ifdefよりきめ細やかな制御ができます。
 といっても、このソースでは0かそれ以外かでしか判断してないけどね。
 まあ、こういうコンパイルの分岐方法もあるよということで、ひとつ。
 例によってプロジェクトの設定を編集メニューでプリプロセッサマクロで定義しとります。
 詳しくはAppStoreにアップする(7)を読みましょう。


 今回は、まったくiPhoneのAPIとは関係ない話でした~。
 ここらへんが3月21日までにやったこと。
 これでサイコロがグルグル回せるってのが確認できたんで、22日からは個人設定画面

Person/Person.xcodeproj

$テン*シー*シー-5
↑こいつね

 の制作に入ったわけです。
 というわけで、解説も次回はPerson/Person.xcodeproj

------------
サンプルプロジェクト:Dev20100321_2.zip