Murayama blog.

プログラミングと、その次の話

イージング


本日から第8章イージングとバネに入ります。


イージング、なかなか聞き慣れない言葉だと思います。
特にサーバサイドJavaばっかりやってきた人にとっては。
そういう人は、インピータンスミスマッチに匹敵する用語だと思ってください。
内容は全く関係ないですが。。


イージングっていうのは、アニメーションでよくある、
スーッと動いて、スッと止まる動きのことです。


例えるなら、車が高速をギュイーンっと走って、
家の近所になったら減速して、車庫入れするとこはさらにゆっくりになる、そんな話。



億泰のザ・ハンドで空間を切り取って、グンって動く、、それは違うか。



さて、次のサンプルでは、ボールをドラッグして、好きなところでドロップすると、
ボールが中心へ向かって、スーッと動いてスッと止まります。


イージングを実現するには、

  • 目標位置を設定する
  • 目標位置までの距離を求める
  • 距離に小数をかけて速度を求める ※この速度でオブジェクトを動かす


目標位置に着くまで、

  • 目標位置までの距離を求める
  • 距離に小数をかけて速度を求める ※この速度でオブジェクトを動かす

を繰り返すことになります。


具体的にはこんなかんじ。

	// 目標までの距離を求める ※targetX、targetYが目標位置
	var dx:Number = targetX - ball.x;
	var dy:Number = targetY - ball.y;
	// 目標までの距離に係数を掛ける
	var vx:Number = dx * easing; 
	var vy:Number = dy * easing
	// 移動
	ball.x += vx;
	ball.y += vy;


あと、こんなふうに距離をeasingの係数を利用して縮めていくと、
最終的には目標には辿り着かない。


たとえば、100メートル先に目標があったとして、そこまで移動したいとする。
1フレームすつ、距離の半分を移動した場合、
1フレーム目で50メートル、2フレーム目で75メートル、3フレーム目で87.5メートル、、、
というように最終的には100メートルに限りなく近づく、という結果になって、
100メートルに達することはない。
これをゼノンのパラドックスというらしい。Flashの世界でもそうなる。


じゃー、どうするかというと、オブジェクトの移動が、目標までラスト1ピクセルを切ったら、
もうゴールと見なしてあげましょう、ってのがFlashのテクニック。
1ピクセルの移動は体感的に全く問題はない、とKeith Petersさんは言っている。あ、著者の方ね。


で、コードのラストの部分はこんなふうにする。

			var distance:Number = Math.sqrt(dx * dx + dy * dy);
			if(distance < 1 ){
				ball.x = targetX;
				ball.y = targetY;
				removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			}else{
				// 目標までの距離に係数を掛ける
				var vx:Number = dx * easing; 
				var vy:Number = dy * easing;
				// 移動
				ball.x += vx;
				ball.y += vy;
			}


要点は以上です。


コードを掲載。

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	public class Easing2 extends Sprite
	{
		private var ball:Ball;
		private var easing:Number = 0.2;
		private var targetX:Number = 200;
		private var targetY:Number = 200;
		
		public function Easing2()
		{
			init();
		}
		
		private function init():void{
			ball = new Ball();
			addChild(ball);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			ball.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown_ball);
		}

		private function onEnterFrame(evt:Event):void
		{
			// 目標までの距離を求める ※targetX、targetYが目標位置
			var dx:Number = targetX - ball.x;
			var dy:Number = targetY - ball.y;
			
			var distance:Number = Math.sqrt(dx * dx + dy * dy);
			if(distance < 1 ){
				ball.x = targetX;
				ball.y = targetY;
				removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			}else{
				// 目標までの距離に係数を掛ける
				var vx:Number = dx * easing; 
				var vy:Number = dy * easing;
				// 移動
				ball.x += vx;
				ball.y += vy;
			}
		}

		private function onMouseDown_ball(evt:MouseEvent):void
		{
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			ball.startDrag();
			ball.addEventListener(MouseEvent.MOUSE_UP, onMouseUp_ball);
		}
		
		private function onMouseUp_ball(evt:MouseEvent):void
		{
			ball.stopDrag();
			ball.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp_ball);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
	}
}


import flash.display.Sprite;

class Ball extends Sprite
{
	private var radious:Number;
	private var color:uint;
	public var vx:Number;

	public function Ball(radius:Number = 40, color:uint = 0xff0000)
	{
		this.radious = radius;
		this.color = color;
		init();
	}

	private function init():void{
		graphics.beginFill(color);
		graphics.drawCircle(0, 0, radious);
		graphics.endFill();
	}
}


あと、イージングの考え方を透明度(alpha)とかに適用すれば、
フェードインやフェードアウトを実現できる。
回転や、カラーの変化などいろいろ使えるね。


以上、おなか減ったから帰る。