神奇的曲线

手头项目做到尾声了。 在整理代码的时候无意中想到,项目中大量的用到了 greenSock 的 TweenMax 和 TweenLite 甚至是 Loader

查询资料后发现 这个开源的缓动类库实际上不允许运用在 商业项目中的。项目要是在国内运营还好,问题是项目是运营在facebook 上的,这样就比较危险了。
于是动手自己编写了一个缓动类库进行替换(还好项目中只有4处用到了缓动)。 但是!! 由于项目中打击怪物掉落物品的这一个效果是使用 TweenMax 的俱乐部插件绘制的抛物线,这块要自行模拟就比较复杂了。

由于要实现的效果是物品上抛时减速,下落时加速的效果。
于是我先根据抛物线公式,通过x轴偏移计算当前y轴高度。

下面这个类来源:http://www.cnblogs.com/meteoric_cry/archive/2012/04/16/2452925.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  
{
    import flash.geom.Point;
    /**
     * ...
     * @author Meteoric
     */

    public class Parabola  
    {
        private var startPt:Point;
        private var endPt:Point;
        private var vertexPt:Point;
        private var a:Number;
        private var b:Number;
        private var c:Number;

        public function Parabola(pt1:Point, pt2:Point)  
        {
            startPt = pt1;
            endPt = pt2;
            parse();
        }
        public function parse(waveHeight:Number=140):void  
        {
            vertexPt = new Point(startPt.x + (endPt.x - startPt.x) / 2, endPt.y - waveHeight);
            var x1:Number = startPt.x;     
            var x2:Number = endPt.x;       
            var x3:Number = vertexPt.x;
            var y1:Number = startPt.y;     
            var y2:Number = endPt.y;   
            var y3:Number = vertexPt.y;
            b = ((y1 - y3) * (x1 * x1 - x2 * x2) - (y1 - y2) * (x1 * x1 - x3 * x3)) / ((x1 - x3) * (x1 * x1 - x2 * x2) - (x1 - x2) * (x1 * x1 - x3 * x3)); 
            a = ((y1 - y2) - b * (x1 - x2)) / (x1 * x1 - x2 * x2);
            c = y1 - a * x1 * x1 - b * x1;
            trace(a, b, c);
        }
        public function getY(posX:Number):Number   
        {
            var posY:Number = a * posX * posX + b * posX + c;
            return posY;
        }
    }  
}

曲线的效果是有了,但却是匀速运动的。 于是试着对x轴进行加速缓动。 但是效果很假,加速效果和曲线的上升和下降的过程无法契合。

接着,我想到了物理加速度算法,这个是经典的2D游戏中,人物跳跃的算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import flash.display.DisplayObject;
import flash.utils.Dictionary;
import flash.events.Event;
import flash.events.KeyboardEvent;

stage.addEventListener(Event.ENTER_FRAME, onEnter);
stage.addEventListener(KeyboardEvent.KEY_DOWN,isDown);

var jumpCache : Dictionary = new Dictionary(true);

function isDown(e:KeyboardEvent) : void
{
    jump(ball,{gravity : .98, power : 10, speed : Math.random()*6 - 3, ground : 300});
}

function jump(target : DisplayObject, params : Object) : void
{
    jumpCache[target] = {
        gravity : params.gravity,
        power   : params.power,
        speedX  : params.speed,
        ground  : params.ground,
        key     : target,
        speedY  : 0,
        hasJump : false
    };
}


function onEnter(e:Event) : void
{
    var obj : Object;
   
    for each(obj in jumpCache){
        if(!obj.hasJump){
            obj.hasJump = true;
            obj.speedY = - obj.power;
            //trace(getTimer());
        }
        obj.key.x   += obj.speedX;
        obj.speedY  += obj.gravity;
        obj.key.y   += obj.speedY;
        if(obj.key.y >= obj.ground){
            obj.key.y = obj.ground;
           
            delete jumpCache[obj.key];
            obj = null;
            //trace(getTimer());
        }
    }
}

可以说,效果完全满足了,但是却有个极大的缺陷,即无法准确的控制下坠地点及整个下落过程的持续时间。
思索了许久,查阅了很多资料后,终于发现了这货 – 贝赛尔曲线
贝塞尔曲线的特征和重力加速的一样,在靠近2点时的速度极快,在过程中减速。 重点是贝塞尔曲线的一切都是可控的。

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent;
import fl.controls.Label;

btnStart.addEventListener(MouseEvent.CLICK,onClick);

var ps : Vector.<Point> = new Vector.<Point>();
var t:Number = 0;
var index:int = 0;

for(var i : int = 0; i < 3 ; i ++){
    this["drag" + i].addEventListener(MouseEvent.MOUSE_DOWN,onDragDown);
}

function onDragDown(e:MouseEvent) : void
{
    e.target.addEventListener(MouseEvent.MOUSE_MOVE, onDragMove);
    e.target.addEventListener(MouseEvent.MOUSE_UP, onDragUp);
}

function onDragMove(e:MouseEvent) : void
{
    e.target.startDrag();
}

function onDragUp(e:MouseEvent) : void
{
    e.target.stopDrag();
    e.target.removeEventListener(MouseEvent.MOUSE_MOVE, onDragMove);
    e.target.removeEventListener(MouseEvent.MOUSE_UP, onDragUp);
}

function onClick(e:MouseEvent):void
{
    stage.removeEventListener(Event.ENTER_FRAME, onEnter);
    index = 0;
    t = 0;
   
    var p0:Point = new Point(drag0.x,drag0.y);
    var p1:Point = new Point(drag1.x,drag1.y);
    var p2:Point = new Point(drag2.x,drag2.y);

    var time:Number = Number(duration.text);
    var count:Number = time / 40;
    var speed:Number = 1 / count;
    countPs(speed,p0,p1,p2);
}

function countPs(speed : Number, p0 : Point, p1 : Point, p2 : Point):void
{
    ps.splice(0,ps.length);
    while (t <=1)
    {
        var posX : Number = Math.pow(1-t,2) * p0.x + 2 * t * (1-t) * p1.x + Math.pow(t,2) * p2.x;
        var posY : Number = Math.pow(1-t,2) * p0.y + 2 * t * (1-t) * p1.y + Math.pow(t,2) * p2.y;
        ps.push(new Point(posX,posY));
        t +=  speed;
    }
   
    stage.addEventListener(Event.ENTER_FRAME, onEnter);
}

function onEnter(e:Event):void
{
    var nextP:Point = ps[(index + 1) % ps.length];
    ball.x = nextP.x;
    ball.y = nextP.y;
    index++;
}

贝塞尔曲线的所有公式 : http://zh.wikipedia.org/wiki/%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9A

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.