時間軸を変動させて、動きを変化させる

例えば、放物線運動を物理方式どおり作ったとして、どうしてもぽわーんとした動きに感じられるケースがあったとします。
重力加速度などを弄っても、上手くいかないと。


そういう時、運動の時間軸の方を伸縮させて、解決する方法があります。
紹介している方法は、時間軸を調整して、放物線運動の頂点部分をスローモーションに感じさせるものです。
おそらく、他にもいろいろと応用出来る方法だと思います。


もっとも、運動自体が、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が仮想時間量です。

この緑色の面積が、経過した時間を現します。
同じように、オレンジ色の面積が経過した仮想時間の累積になります。


概念的には、先に、仮想時間の微分的な変化量を計算で割り出して、それを積算して、運動方程式に与えています。
その時、緑色の面積と、オレンジ色の面積が同じになるように設定すると、双方の運動量は等しくなります。
経過時間が同じですからね。


仮想時間の変化量を微分的に割り出して、それを積算(積分)、という方法でした。
例のプログラムは、経過時間が等しくなるように調整しています。