MoveEx效果

  Flex有一个Move效果,播放该效果时,目标对象会沿直线运动。有时候我们需要更丰富一些的运动效果,比如抛物线运动,Move就显得无能为力了。基于这种需求,我创建了一个MoveEx类,可以播放大多数的运动效果。
  通过查看Flex源代码可以看出,Move以及MoveInstance分别继承自TweenEffect与TweenEffectInstance,Move效果实际就是创建Tween对象,并通过它来改变目标的x与y属性。因此可以考虑只用Tween来改变目标的x属性,而y属性则根据一个函数得出结果,那样一来,如果在函数里指定为直线方程,那么效果就是直线运动,指定为抛物线方程,效果就是抛物线运动…… 这样会灵活许多。
  有人可能会问了,如果实现垂直运动效果,x属性是不变的,所以也就没办法播放垂直运动效果了。其实针对这种情况,可以设计让Tween来改变目标的y属性,然后根据一个函数得出x属性的值。甚至我们需要加一个额外属性,指定Tween改变目标的哪个属性:x或y。因为我只想要一个简单的开口向下的抛物线效果,所以就不考虑这种情况了。
  实现代码如下所示:

MoveEx.as
package com.thornbird.effects
{

import com.swoondazzle.plantsVSAnimals.effects.effectClasses.MoveExInstance;

import mx.effects.IEffectInstance;
import mx.effects.TweenEffect;

/**
 *  移动扩展效果
 */
public class MoveEx extends TweenEffect
{
	
	//--------------------------------------------------------------------------
	//
	//  Class constants
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 *  影响的属性
	 */
	private static var AFFECTED_PROPERTIES:Array = ["x", "y"];
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  Constructor
	 */
	public function MoveEx(target:Object = null)
	{
		super(target);
		instanceClass = MoveExInstance;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private 
	 */
	private var beelineParameters:Array = null;
	
	/**
	 *  @private 
	 */
	private var parabolaParameters:Array = null;
	
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  xFrom
	//----------------------------------
	
	/** 
	 *  起点x坐标
	 */
	public var xFrom:Number = NaN;
	
	//----------------------------------
	//  xTo
	//----------------------------------
	
	/** 
	 *  终点x坐标
	 */
	public var xTo:Number = NaN;
	
	//----------------------------------
	//  track
	//----------------------------------
	
	/** 
	 *  运动轨迹函数
	 */
	public var track:Function = null;
	
	//--------------------------------------------------------------------------
	//
	//  Overridden methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	override public function getAffectedProperties():Array
	{
		return AFFECTED_PROPERTIES;
	}
	
	/**
	 *  @private
	 */
	override protected function initInstance(instance:IEffectInstance):void
	{
		super.initInstance(instance);
		var moveExInstance:MoveExInstance = instance as MoveExInstance;
		moveExInstance.xFrom = xFrom;
		moveExInstance.xTo = xTo;
		moveExInstance.track = track;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  创建直线运动轨迹函数
	 *  @param startX 起点x坐标
	 *  @param startY 起点y坐标
	 *  @param endX 终点x坐标
	 *  @param endY 终点y坐标
	 */
	public function createBeelineTrack(startX:Number, startY:Number, 
			endX:Number, endY:Number):void
	{
		// 此时起点与终点已经确定
		xFrom = startX;
		xTo = endX;
		var x1:Number = startX;
		var y1:Number = startY;
		var x2:Number = endX;
		var y2:Number = endY;
		// 将k, b分别代入 y = k * x + b 得出k, b的值:
		var k:Number = (y1 - y2) / (x1 - x2);
		var b:Number = b = y1 - x1 * k;
		beelineParameters = [k, b];
		track = beeline;
	}
	
	/**
	 *  创建抛物线轨迹函数
	 *  @param startX 起点x坐标
	 *  @param startY 起点y坐标
	 *  @param distance 距离
	 *  @param height高度
	 */
	public function createParabolaTrack(startX:Number, startY:Number, 
			distance:Number, height:Number):void
	{
		// 此时起点与终点已经确定
		xFrom = startX;
		xTo = startX + distance;
		var x1:Number = xFrom;
		var y1:Number = 0;
		var x2:Number = xTo;
		var y2:Number = 0;
		var x0:Number = x1 + distance / 2;
		var y0:Number = height;
		// 将x1, y1, x2, y2, x0, y0分别代入 y = a * Math.pow(x, 2) + b * x + c
		// 得出a, b, c的值:
		var a:Number = ((x1 - x0) * (y1 - y2) - (x1 - x2) * (y1 - y0)) 
				/ ((x1 - x0) * (Math.pow(x1, 2) - Math.pow(x2, 2)) 
				- (x1 - x2) * (Math.pow(x1, 2) - Math.pow(x0, 2)));
		var b:Number = ((y1 - y2) - (Math.pow(x1, 2) - Math.pow(x2, 2)) * a) 
				/ (x1 - x2);
		var c:Number = y1 - Math.pow(x1, 2) * a + x1 * b;
		parabolaParameters = [a, b, c, startY];
		track = parabola;
	}
	
	/**
	 *  @private
	 */
	public function beeline(x:Number):Number
	{
		var k:Number = beelineParameters[0];
		var b:Number = beelineParameters[1];
		return (x * k + b);
	}
	
	/**
	 *  @private
	 */
	public function parabola(x:Number):Number
	{
		var a:Number = parabolaParameters[0];
		var b:Number = parabolaParameters[1];
		var c:Number = parabolaParameters[2];
		var y:Number = parabolaParameters[3];
		var h:Number = parabolaParameters[4];
		return (y - ((Math.pow(x, 2) * a + x * b + c) 
				- (Math.pow(xFrom, 2) * a + xFrom * b + c)));
	}
	
}

}


MoveExInstance.as
package com.thornbird.effects.effectClasses
{

import flash.events.Event;

import mx.core.Container;
import mx.core.EdgeMetrics;
import mx.core.IUIComponent;
import mx.core.mx_internal;
import mx.effects.EffectManager;
import mx.effects.effectClasses.TweenEffectInstance;
import mx.events.MoveEvent;

use namespace mx_internal;

/**
 *  移动扩展效果实例
 */
public class MoveExInstance extends TweenEffectInstance
{
	
	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private 
	 */
	private var forceClipping:Boolean = false;
	
	/**
	 *  @private 
	 */
	private var checkClipping:Boolean = true;
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  Constructor
	 */
	public function MoveExInstance(target:Object)
	{
		super(target);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  xFrom
	//----------------------------------
	
	/** 
	 *  起点x坐标
	 */
	public var xFrom:Number = NaN;
	
	//----------------------------------
	//  xTo
	//----------------------------------
	
	/** 
	 *  终点x坐标
	 */
	public var xTo:Number = NaN;
	
	//----------------------------------
	//  track
	//----------------------------------
	
	/** 
	 *  运动轨迹函数
	 */
	public var track:Function = null;
	
	//--------------------------------------------------------------------------
	//
	//  Overridden methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	override public function initEffect(event:Event):void
	{
		super.initEffect(event);
		if (event is MoveEvent && event.type == MoveEvent.MOVE)
		{
			if (isNaN(xFrom) && isNaN(xTo))
			{
				xFrom = MoveEvent(event).oldX;
				xTo = target.x;
			}
		}
	}
	
	/**
	 *  @private
	 */
	override public function play():void
	{
		super.play();
		EffectManager.startBitmapEffect(IUIComponent(target));
		tween = createTween(this, [xFrom], [xTo], duration);
		var targetParent:Container = target.parent as Container;
		if (targetParent != null)
		{
			var yFrom:Number = calculateYCoord(xFrom);
			var yTo:Number = calculateYCoord(xTo);
			var vm:EdgeMetrics = targetParent.viewMetrics;
			var l:Number = vm.left;
			var r:Number = targetParent.width - vm.right;
			var t:Number = vm.top;
			var b:Number = targetParent.height - vm.bottom;
			if (xFrom < l || xTo < l 
					|| xFrom + target.width > r 
					|| xTo + target.width > r 
					|| yFrom < t || yTo < t 
					|| yFrom + target.height > b 
					|| yTo + target.height > b)
			{
				forceClipping = true;
				targetParent.forceClipping = true;
			}
		}
		applyTweenStartValues();
	}
	
	/**
	 *  @private
	 */
	override public function onTweenUpdate(value:Object):void
	{
		EffectManager.suspendEventHandling();
		var x:Number = value[0];
		var y:Number = calculateYCoord(x);
		if (!forceClipping && checkClipping)
		{
			var targetParent:Container = target.parent as Container;
			if (targetParent != null)
			{
				var vm:EdgeMetrics = targetParent.viewMetrics;
				var l:Number = vm.left;
				var r:Number = targetParent.width - vm.right;
				var t:Number = vm.top;
				var b:Number = targetParent.height - vm.bottom;
				if (x < l || x + target.width > r 
						|| y < t || y + target.height > b)
				{
					forceClipping = true;
					targetParent.forceClipping = true;
				}
			}
		}
		target.move(x, y);
		EffectManager.resumeEventHandling();
	}
	
	/**
	 *  @private
	 */
	override public function onTweenEnd(value:Object):void
	{
		EffectManager.endBitmapEffect(IUIComponent(target));
		if (forceClipping)
		{
			var targetParent:Container = target.parent as Container;
			if (targetParent != null) 
			{
				forceClipping = false;
				targetParent.forceClipping = false;
			}
		}	
		checkClipping = false;
		super.onTweenEnd(value);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 *  根据x坐标计算y坐标
	 *  @param x x坐标
	 */
	private function calculateYCoord(x:Number):Number
	{
		var y:Number = 0;
		if (track != null)
			return track(x);
		return target.y;
	}
	
}

}


示例代码
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	xmlns:effect="com.thornbird.effects.*" 
	layout="absolute">
	
	<mx:Script>
		<![CDATA[
			private function beginMove():void
			{
				moveEx.createParabolaTrack(0, 100, 800, 100);
				moveEx.play([button]);
			}
		]]>
	</mx:Script>
	
	<effect:MoveEx id="moveEx" xFrom="0" xTo="700" duration="1000" />
	<mx:Button id="button" x="0" y="100" label="Button" click="beginMove()" />
	
</mx:Application>


  这样一个简单的抛物线效果就完成了。这是我山寨植物大战僵尸游戏时用到的,代码很简单,主要是初中的几何知识几乎都忘光了,解方程还算了半天。不胜唏嘘。
  另外,MoveInstance类的onTweenUpdate()方法中,有这么一行代码:EffectManager.suspendEventHandling(); 这行代码屏蔽了move,需要注意一下。

你可能感兴趣的:(游戏,C++,c,Flex,C#)