時間軸を変動させて、動きを変化させる
例えば、放物線運動を物理方式どおり作ったとして、どうしてもぽわーんとした動きに感じられるケースがあったとします。
重力加速度などを弄っても、上手くいかないと。
そういう時、運動の時間軸の方を伸縮させて、解決する方法があります。
紹介している方法は、時間軸を調整して、放物線運動の頂点部分をスローモーションに感じさせるものです。
おそらく、他にもいろいろと応用出来る方法だと思います。
もっとも、運動自体が、y=f(x)みたいな式で出来ている場合に限ります。
差分方程式的に、y += add; みたいな形式では行えません。
時間軸を伸縮させるというと分かりにくいのですが、通常の時間と仮想の時間に分けて、通常の時間から、伸縮させた仮想の時間を求め、その時間を運動の式に与えるという方式です。
ますますややこしい。(汗)
下のものは、ジャンプした時の加速運動の高さ求めるものです。
// 重力加速度、放物線移動する全体時間の長さと、現在の時間からY座標を求める関数 CGFloat turuGetYPositionWithGravityAndTotalTimeAndTime(CGFloat gravity, NSInteger totaltime, CGFloat now) { return gravity * now * (now - totaltime); }
で、普通はこのnowにフレーム毎に+1した値を与えると、放物線運動のYの値が得られます。
それだと、状況によっては、ぽや〜んとした少し間抜けな動きになる事があります。
そこで、実際のフレーム数をカウントする_currentMovingFrame から、仮想の_currentVirtualFrame を求め、それを先の関数に入れてやる、という方式です。
頂点部分の時間経過をゆっくりにする変換式と、その使い方です。
@property (readwrite, nonatomic) NSInteger totalMovingFrame; @property (readwrite, nonatomic) NSInteger currentMovingFrame; @property (readwrite, nonatomic) CGFloat currentVirtualFrame; // ここから下は、1フレーム毎の割り込み処理での記述 const CGFloat bottom = 0.2f; // 0~1の範囲。この値を小さくするほど、頂点部分がゆっくりになります。1だと時間の伸縮効果はありません。 const CGFloat top = 2 - bottom; const CGFloat sub =top - bottom; if (_currentMovingFrame < _totalMovingFrame) { _hoge.position = CGPointMake(0,turuGetYPositionWithGravityAndTotalTimeAndTime(_gravity,otalMovingFrame,_currentVirtualFrame)); _currentVirtualFrame += fabs(-sub/((CGFloat)_totalMovingFrame/2.0f)*(CGFloat)_currentMovingFrame + sub)+bottom; _currentMovingFrame++; }
下のへっぽこな図は、左が普通の時間経過。実1フレームあたり、仮想時間が等しい関係のグラフです。
右が伸縮させた時の、実1フレーム当たりの仮想時間量が変化しているグラフです。
tが実フレームで、vtが仮想時間量です。
この緑色の面積が、経過した時間を現します。
同じように、オレンジ色の面積が経過した仮想時間の累積になります。
概念的には、先に、仮想時間の微分的な変化量を計算で割り出して、それを積算して、運動方程式に与えています。
その時、緑色の面積と、オレンジ色の面積が同じになるように設定すると、双方の運動量は等しくなります。
経過時間が同じですからね。
仮想時間の変化量を微分的に割り出して、それを積算(積分)、という方法でした。
例のプログラムは、経過時間が等しくなるように調整しています。