本文主要是介绍CCActionEase想说爱你也不难,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
尊重作者劳动,转载时请标明文章出处。
作者: Bugs Bunny
地址: http://www.cnblogs.com/cocos2d-x/archive/2012/03/19/2407032.html
本文函数图像使用GeoGebra绘制,感谢它才华横溢的作者。
为了方便用户灵活地控制精灵运动,cocos2d-x提供了CCActionEase类系的动作。它们拥有相似的名字——CCEaseXxxxIn、CCEaseXxxxOut、CCEaseXxxxInOut,同时也拥有相似的行为——速度由慢至快、速度由快至慢、速度先由慢至快再由快至慢。但是除了这些,我们对CCActionEase一无所知。就算查阅参考手册,我们能得到的信息也不过是类似Ease Sine In的简短说明。它们究竟是什么模样,我们该如何选择?
今天我们就来解决这个问题。鉴于CCActionEase类系的庞大,文章可能会分成两到三篇。
1)CCEaseSineIn
在《cocos2d-x动作系统浅析》一文中提到:
update函数接受一个百分比参数,它表示动作的完成进度。update根据这个百分比将目标对象做出相应的调整。
可以说这个update函数就是CCActionEase的灵魂。
1 void CCEaseSineIn::update(ccTime time)
2 {
3 m_pOther->update(-1 * cosf(time * (float)M_PI_2) + 1);
4 }
之前我们已经知道CCActionEase类系的动作就是调整其他动作的速度,变换出新的效果。这里的m_pOther就是那个被影响的动作,而一切魔力的源头就在它接受的参数上。CCEaseSineIn将传入的百分比参数进行了一系列变换,然后传给了m_pOther。
我们将这个变换公式提取出来,记作:
f(x)=1-cos(π/2*x) x∈[0,1]
这个就是已用时间百分比与实际完成进度的关系。在匀速运动中,它们应该是相等的,但是在变速运动中,它们的关系就会变幻莫测。
上图中的黑色曲线就是f(x)的函数图像。它的定义域从0开始,到1结束,值域也是这样。根据这条线的走势,可以粗略看出速度是越变越快的,但还是不够形象。
在运动学中,物体的位移对于时间的导数就是物体的瞬时速度。如果我们能得到这条瞬时速度的曲线,那就直观多了。上面的函数f(x)是已用时间百分比与实际完成进度的关系,这里可以近似地理解为时间与路程的关系。
所以我们对f(x)求导,得出:
f'(x)=π/2*sin(π/2*x) x∈[0,1]
它对应图中那条红色曲线。可以很明显地看出,速度越变越快,在C点达到了最高。
正如它名字说的那样,它的速度由慢至快,呈正弦变化。
2)CCEaseSineOut
我们再来看下CCEaseSineOut类。
1 void CCEaseSineOut::update(ccTime time)
2 {
3 m_pOther->update(sinf(time * (float)M_PI_2));
4 }
同理得出:
f(x)=sin(π/2*x) x∈[0,1]
f'(x)=π/2*cos(π/2*x)
同样我们更关注那条红色曲线,它从最高点C出发,一路下降到达A点。这表明在CCEaseSineOut动作中,速度是越来越慢的,它的图像也呈正弦变化。
3)CCEaseSineInOut
我们知道CCEaseXxxxInOut的速度变化是先由慢至快,再由快至慢。如果我们将上面两个图像拼在一起,然后在将横轴比例缩小一倍,那结果就是这条曲线的模样了。
一般情况下,我们需要将函数分成两段,第一段在0到0.5之间,第二段在0.5到1之间。我们来看看CCEaseSineInOut是如何实现的。
1 void CCEaseSineInOut::update(ccTime time)
2 {
3 m_pOther->update(-0.5f * (cosf((float)M_PI * time) - 1));
4 }
f(x)=-0.5*(cos(π*x)-1) x∈[0,1]
f'(x)=π/2*sin(π*x)
在CCEaseSineInOut中,这两段曲线正好是同一个函数(非分段函数)的图像。很巧妙是不是?
图中红色曲线从原点O出发,一路上升到达最高点C,然后又一路下滑降至D点。它同样也是一条正弦变化的曲线。动作的速度看起来就是由慢至快,再由快至慢的。
小结
CCEaseSineIn、CCEaseSineOut、CCEaseSineInOut这三个动作同属速度正弦变化,变化的范围是[0,π/2]。
4)CCEaseExponentialIn
有了前面的经验,后面就容易多了,先来看一下CCEaseExponentialIn的update函数。
1 void CCEaseExponentialIn::update(ccTime time)
2 {
3 m_pOther->update(time == 0 ? 0 : powf(2, 10 * (time/1 - 1)) - 1 * 0.001f);
4 }
大家可能已经注意到,这里使用了一个条件运算符,于是表达式变作了分段函数。
当x=0时,f(x)=0
当x∈(0,1]时,f(x)=2^(10*(x-1))-0.001
注意这条不是速度的曲线。
上面副绘图区中的图像就是这个函数的整体走势,我们在主绘图区给原点附近的曲线一个特写。可以看到,除了x=0的情况,曲线与x轴还有一个交点。
对2^(10*(x-1))-0.001=0求解,得出:
x=1-ln(1000)/(10*ln(2))=0.00342
现在我们开始在脑中想象一下精灵按照CCEaseExponentialIn动作移动的详细步骤。
首先,时间从零开始,精灵被设置到起始位置。这一步是正常的没有问题。
接下来,精灵猛地朝着反方向跳动了很小的一段距离。这个距离是非常非常小的,也就是图上的B点附近,大约只占整个移动距离的0.00234%
然后,精灵开始以变化的速度朝着目标点移动。经过点A时精灵回到初始位置。这时,我们设计的运动才刚刚开始。
如果我们将x=1代入公式,可以推算出:
f(x)=1-0.001=0.999
也就是说,图像最终没有到达终点,而是差了一小段距离。
简单来说,总时间的前0.342%部分以及最终的那一瞬间的运动是不太正常的。
如果你设计了一个超过1000秒的运动,那么前3秒内,精灵的准确位置不会在你设计的轨迹上。
当然如果想观察到这个问题,运动的距离也是一个关键。
假设你疯狂地设计了一个运动10万像素的精灵,并且运动时间超过1000秒,那你就能观察到这一现象了。3秒钟,反向2个像素。
但是为什么会这样呢?是引擎的bug吗?
确切来说,这应该算不上是bug,这只是精度引起的问题。
下面这段都是我自己的推测,也就是猜到,大家看看就好了。
我猜测这个公式的最初原型应该是:
f(x)=2^(10*(x-1)) x∈[0,1]
但是它有一个问题,那就是当x=0的时候,f(0)=1/1024
时间为零的时候,精灵大约就已经有了千分之一的位移,而且是在一个物体运动刚开始的时候,猛然地跳动是非常明显的。所以设计者将千分之一的误差移动到了末尾,也就是运动要结束的时候。
那公式现在的样子就是:
f(x)=2^(10*(x-1))-1/1024 x∈[0,1]
大家都知道cocos2d-x多使用单精度浮点型数字,以及写0.0009765625f比较麻烦等诸多因素,最后这个公式就简化成了现在的模样。
我的猜想说完了,我们接着来求导:
f'(x)=10*2^(10*(x-1))*ln(2) x∈[0,1]
按照最理想的那个公式绘制出图像,这里我们只看那条红色的曲线。这条曲线从D点开始一路上升,迅速到达C点。如果你对它再次求导,就能得出其加速度的变化规律。从DC曲线上应该可以看出其加速度也是越来越大的。
额,说得有点儿远了。我们把注意力先集中起来,计算出速度的最小值和最大值。
f'(0)=10*2^(-10)*ln(2)=0.006769
f'(1)=10*2^0*ln(2)=6.931472
CCEaseExponentialIn的速度由慢至快,从0.006769上升至6.931472,呈指数级变化。
5)CCEaseExponentialOut
1 void CCEaseExponentialOut::update(ccTime time)
2 {
3 m_pOther->update(time == 1 ? 1 : (-powf(2, -10 * time / 1) + 1));
4 }
CCEaseExponentialOut与CCEaseExponentialIn的实现是相似的,唯一的不同是CCEaseExponentialOut在最后一瞬间会有短距离的跳跃(千分之一的误差),而CCEaseExponentialIn是舍弃部分。个人认为CCEaseExponentialOut的处理方式更合理些。
好了直接上图
这里没有难点,我直接让工具生成的导函数图像。
我们关心的是A点(0,6.93147)和D点(1,0.00677),与CCEaseExponentialIn的速度范围是一样的。从6.93147下降至0.00677,速度为由快至慢的指数变化。
6)CCEaseExponentialInOut
在《知易游戏开发教程cocos2d-x移植版003》中有一段CCEaseExponentialInOut的演示代码,测试运行时会发现精灵最后以极快的速度飞出了屏幕,是笔者使用不当,还是别的什么原因?当时由于时间、精力的问题没有深入研究,今天借此机会将问题分析一下。
1 void CCEaseExponentialInOut::update(ccTime time)
2 {
3 time /= 0.5f;
4 if (time < 1)
5 {
6 time = 0.5f * powf(2, 10 * (time - 1));
7 }
8 else
9 {
10 time = 0.5f * (-powf(2, 10 * (time - 1)) + 2);
11 }
12
13 m_pOther->update(time);
14 }
呵呵,典型的分段函数。绘制函数图像如下:
图中这条蓝色的曲线就是CCEaseExponentialInOut使用的分段函数。很明显可以看到在A点处,曲线走向发生了90°的变化,向着点(1,-511)延伸。它没有像前面说过的函数那样逼近点C(1,1),这就解释了为什么精灵莫名其妙地飞出了屏幕。
这是一个bug,我们希望曲线的后半段能像那条绿色的曲线AC那样。(我只在Win32平台上测试的,不知其他平台上是否也存在这个问题,有兴趣的朋友可以测试下。)
我的修改如下:
1 void CCEaseExponentialInOut::update(ccTime time)
2 {
3 time /= 0.5f;
4 if (time < 1)
5 {
6 time = 0.5f * powf(2, 10 * (time - 1));
7 }
8 else
9 {
10 // 将(time - 1)变作(1 - time)
11 time = 0.5f * (-powf(2, 10 * (1 - time)) + 2);
12 }
13
14 m_pOther->update(time);
15 }
修正后,动作的行为正常了。
对新的函数求导,得出图中的红色曲线。其中点D、点E、点F的坐标分别为(0.5,6.93147)、(0,0.00677)、(1,0.00677)。
细心的朋友可能已经发现了点C没有到达(1,1)。是的,这里存在0.000488的误差,曲线的起始点也一样。即原来1/1024的误差被平分到了开头和末尾。
小结
CCEaseExponentialIn、CCEaseExponentialOut、CCEaseExponentialInOut这三个动作同属速度指数级变化,变化的范围是[0.00677,6.93147]。
本文函数图像使用GeoGebra绘制,感谢它才华横溢的作者。
相比之前的速度正弦变化动作(这个东西叫什么更好一些?渐变动画?)与速度指数级变化动作,CCEaseIn/CCEaseOut/CCEaseInOut更具灵活性。你可以设置运动的速率,甚至是在运动的过程中改变速率。它们拥有共同的基类——CCEaseRateAction。不要直接使用CCEaseRateAction,因为它没有实现任何变化效果。
7)CCEaseIn
按照惯例贴出update函数的源代码,以免版本更新导致文不对题。
1 void CCEaseIn::update(ccTime time)
2 {
3 m_pOther->update(powf(time, m_fRate));
4 }
根据此函数的实现推导出以下三个公式:
s(t)=t^r t∈[0,1]
v(t)=s'(t)=r*t^(r-1) t∈[0,1]
a(t)=v'(t)=r*(r-1)*t^(r-2) t∈[0,1]
s:路程 v:速度 a:加速度 t:时间 r:速率参数m_fRate
都是很基本的导函数推导,如果有不清楚的地方,建议先复习下导数。
下面我们着重分析下速率参数,也就是r的取值范围。因为是讨论二元函数,图像是三维的,画出后重叠在一起反而不利于理解,这里就没有作图。
1.r<0
当r<0时,v(t)将始终保持为负数,也就是说速度的方向与设定的方向正好相反。
又因为s(1)=1,这说明精灵最后移动到了目标点坐标,但它是从预设轨迹的延长线上反向移动到这一坐标点的。
这是一个很奇怪的动作行为,跟我们预想的设计不符,所以r的取值不应小于零。
2.r=0
当r=0时,s(t)恒等于1,也就说动作在开始之前就已经达到了完成的状态。我们这里说的是动作的表现,动作原本的执行时间是不受此影响的。
很明显,这与我们的设计不符,所以零也不是r的一个取值。
3.0<r<1
当r的取值范围在(0,1)时,a(t)恒为负数,加速度为负说明速度是越来越慢的,这与CCEaseXxxxIn动作应该由慢至快的设定不符,所以这也不是r应有的取值范围。
4.r=1
当r=1时,a(t)恒等于零,v(t)恒等于一,这说明此时的动作是速度为1的匀速运动。这貌似与设定的有慢至快也不相符。
5.1<r<2
当r的取值范围在(1,2)时,加速度a(t)恒大于零,但呈下降趋势。
6.r=2
当r=2是,a(t)恒等于2,也就是此时为匀加速运动。
7.r>2
当r>2时,加速度a(t)恒大于零,且呈上升趋势。
综上所述,当你使用CCEaseIn时,传入的速率参数应大于1.0f,并且根据取值范围的不同,会呈现出3种不太一样的加速运动。
8)CCEaseOut
我们再来看一下CCEaseOut的update函数。
1 void CCEaseOut::update(ccTime time)
2 {
3 m_pOther->update(powf(time, 1 / m_fRate));
4 }
第一步还是需要推导出路程、速度、加速度的公式:
s(t)=t^(1/r) t∈[0,1]
v(t)=s'(t)=1/r*(t^(1/r-1)) t∈[0,1]
a(t)=v'(t)=1/r*(1/r-1)*(t^(1/r-2)) t∈[0,1]
第二步分析r的取值范围:
1.r<0
当r<0时,CCEaseOut的情况与CCEaseIn一样,运动不在预设轨迹上,排除。
2.r=0
除数不能为零,排除。
3.0<r<1
当r的取值范围在(0,1)时,a(t)恒大于零,这说明速度是越来越快的,这与CCEaseXxxxOut由快至慢的设定不符,排除。
4.r=1
当r=1时,a(t)恒等于零,这说明是匀速运动,不符合设定,排除。
5.r>1
当r>1时,加速度a(t)恒小于零,但呈上升趋势。
综上所述,当你使用CCEaseOut时,传入的速率参数应大于1.0f,与CCEaseIn不同的是,CCEaseOut只有一类加速度变化趋势。
在继续后面的研究之前,我们来做一些额外的思考。CCEaseOut中r的取值为什么与CCEaseIn的有些不一样呢?是不是它的设计存在什么不合理的地方?
假设我们将r设定为3,同时画出CCEaseIn和CCEaseOut的图像:
颜色有点儿乱,不过没办法,里面包含3套对比数据,我直接说颜色,希望大家别迷糊。
那条红色曲线是CCEaseIn的v(t)函数,那条分数的是CCEaseOut的v(t)函数。
大家都知道CCEaseXxxxIn与CCEaseXxxxOut动作的区别是,前者由慢到快,后者由快到慢。如果说得更精确些,它们的表现应该是对称的——速度函数v(t)按照x=0.5直线轴对称。
但是显而易见的,红色曲线和粉色曲线根本不对称。
如果大家再仔细想想之前正弦变化和指数级变化的图像,这里还存在一处对称。那就是,路程函数s(t)的图像应该是按照点A(0.5,0.5)中心对称的。
那条蓝色曲线是CCEaseIn的s(t)函数,那条兰色的是CCEaseOut的s(t)函数。
可以很明显地看出,它们是按照y=x直线轴对称的,而不是按照点A中心对称。
我们将红色曲线按照x=0.5直线做轴对称镜像,得到那条黑色的抛物线。再对此抛物线求其反导函数图像,得到那条黑色曲线。瞧,它与那条蓝色曲线是不是按照点A中心对称的。
所以,我认为CCEaseOut的update函数应该修改一下。如果你有不同的见解,欢迎在评论区留言。
附上我修改后的代码:
1 void CCEaseOut::update(ccTime time)
2 {
3 m_pOther->update(1.0f - powf((1.0f - time), m_fRate));
4 }
如果按我这样修改,那么速率参数的取值范围与CCEaseIn中是一样的,大于一。
9)CCEaseInOut
好了,继续我们的研究:
1 void CCEaseInOut::update(ccTime time)
2 {
3 int sign = 1;
4 int r = (int) m_fRate;
5
6 if (r % 2 == 0)
7 {
8 sign = -1;
9 }
10
11 time *= 2;
12 if (time < 1)
13 {
14 m_pOther->update(0.5f * powf(time, m_fRate));
15 }
16 else
17 {
18 m_pOther->update(sign * 0.5f * (powf(time - 2, m_fRate) + sign * 2));
19 }
20 }
必须指出,这个函数的实现是有问题的。
第一,难道引擎的设计者只希望我们传入整数型的速率吗?
问题出在powf函数调用上。
我们知道传入的time参数范围在[0,1],即便中间做了一次乘2的操作,到了后面time-2依然是小于等于零的。所以在某些情况下,powf会出现问题。
比如,当m_fRate设置为3.5f之类的小数时,这里的powf会返回"-1.#IND000"。
于是,当动作执行到后半段时,精灵会消失,直到动作全部完成,精灵才会出现在终点上。
第二,那个sign正负标志是用来解决问题的吗?怎么感觉引入后反而将问题复杂化了。
这里其实只需将前半段的函数按照点(0.5,0.5)做一次中点对称就可以了,用不着这么麻烦。
我修改的代码如下:
1 void CCEaseInOut::update(ccTime time)
2 {
3 time *= 2;
4 if (time < 1)
5 {
6 m_pOther->update(0.5f * powf(time, m_fRate));
7 }
8 else
9 {
10 m_pOther->update(0.5f * (2.0f - powf((2.0f - time), m_fRate)));
11 }
12 }
因为CCEaseInOut与CCEaseIn使用相同的算法,所以在这里速率参数的取值范围与CCEaseIn的一样,也是大于一。
小结
到目前为止,我们对CCActionEase的学习已经完成了一半。
我们一共学习了3类,9个动作。它们分别是CCEaseSineIn、CCEaseSineOut、CCEaseSineInOut、CCEaseExponentialIn、CCEaseExponentialOut、CCEaseExponentialInOut、CCEaseIn、CCEaseOut、CCEaseInOut。
它们与之后将要学习的动作的最大区别是,在这些动作的执行过程中,精灵会严格地按照内部动作指定的路径移动,绝对不会超出起始点与终点的范围。
在CCEaseIn/CCEaseOut/CCEaseInOut中,速度的变化范围是[0,m_fRate],m_fRate>1。
但是,如果cocos2d-x需要与cocos2d-iphone从原则上保持高度一致,即便是存在缺陷也不能破坏原则的话,那么我推测官方在短时间内是不会修改这个问题的。因为,在大约6个月之前,有朋友提出过此问题,但似乎被无视了。
http://www.cocos2d-iphone.org/forum/topic/20979
http://code.google.com/p/cocos2d-iphone/issues/detail?id=1248
所以,在官方正式修正此问题之前,我建议大家只使用大于1的整数作为速率的参数,以提高兼容性。
本文函数图像使用GeoGebra绘制,感谢它才华横溢的作者。
在上一篇中我们介绍了两种反弹效果动作——Ease Back和Ease Bounce,今天我们将要介绍第三种反弹效果动作——Ease Elastic。
那么这个Elastic是什么呢?
维基百科上说,Elastic是对弹性体或者可伸缩纤维的口语说法,或者是指小孩子玩的跳皮筋游戏。
下面我们就研究一下这个Elastic和皮筋有什么关系。
16)CCEaseElasticIn
要研究动作的行为,就离不开它的update函数。
1 void CCEaseElasticIn::update(ccTime time)
2 {
3 ccTime newT = 0;
4 if (time == 0 || time == 1)
5 {
6 newT = time;
7 }
8 else
9 {
10 float s = m_fPeriod / 4;
11 time = time - 1;
12 newT = -powf(2, 10 * time) * sinf((time - s) * M_PI_X_2 / m_fPeriod);
13 }
14
15 m_pOther->update(newT);
16 }
这是一个分段函数,两端的时间点被独立出来了。先抛开这两点不管,我们看主要的变换代码。
首先看到:
time = time - 1;
这其实就是做了一次以x=0.5为轴的轴对称。
为了方便我们将time - 1记作u,m_fPeriod记作p,那么变换公式如下:
u=t-1 t∈(0,1)
s(u)=-1*2^(10u)*sin((u-p/4)*2π/p) u∈(-1,0)
s(u)=-1*2^(10u)*sin(2πu/p-π/2) u∈(-1,0)
s(u)=2^(10u)*sin(π/2-2πu/p) u∈(-1,0)
s(u)=2^(10u)*cos(2πu/p) u∈(-1,0)
大家都知道余弦函数是以2π为周期的,这里公式中有一个除以p的操作,这说明cos(2πu/p)的周期为p。
又因为u∈(-1,0),1/1024<2^(10u)<1,这说明2^(10u)与cos(2πu/p)相乘最多影响函数曲线的幅度,绝不会改变原来的周期。
所以s(u)的函数周期就是p。
因为这是一个分段函数,所以可能存在的最大问题就是,在实际运动过程中,精灵的动作可能不是连续的,即(0,0)和(1,1)两点不在函数曲线上。所以如果你希望让精灵完美地运动,你就需要小心地选取这个周期参数。
如何选取周期参数呢?
假设我们有这样一个函数:
f(u)=cos(2πu/p)
我们希望它能满足以下条件:
1.f(0)=1
2.f(1)=0
也就是说,我们希望点(0,f(0))落在图中点A的位置,而点(1,f(1))落在图中点B、C、D、E、F这样的位置上。
条件一是时刻满足的,我们只要针对条件二来确定p就可以了。于是我们得出等式:
2π/p=π/2+N*π N≥0
p=4/(2*N+1) N≥0
于是我们可以得出,比较合适的p的取值有:4、4/3、4/5、4/7。。。
我们选取p=4/7,绘出完整函数图像,看看是什么样子的。
这是一条振荡曲线,或者叫做波动曲线,它描述的是精灵运动的幅度。
用什么来形容这个动作呢?我也说不好。地震监测仪上的那支笔?也许吧。
17)CCEaseElasticOut
1 void CCEaseElasticOut::update(ccTime time)
2 {
3 ccTime newT = 0;
4 if (time == 0 || time == 1)
5 {
6 newT = time;
7 }
8 else
9 {
10 float s = m_fPeriod / 4;
11 newT = powf(2, -10 * time) * sinf((time - s) * M_PI_X_2 / m_fPeriod) + 1;
12 }
13
14 m_pOther->update(newT);
15 }
将CCEaseElasticIn的图像按照点(0.5,0.5)中心对称镜像,得到的就是CCEaseElasticOut的图像。
但是,如果你直接将中心对称公式套入CCEaseElasticIn的函数中,推导出的并不是上面CCEaseElasticOut使用的计算公式。不过不用特别担心,如果再考虑上正弦函数的性质,你会发现他们是相等的。
18)CCEaseElasticInOut
这是最后一个CCActionEase类动作,胜利在望,加把劲。
1 void CCEaseElasticInOut::update(ccTime time)
2 {
3 ccTime newT = 0;
4 if (time == 0 || time == 1)
5 {
6 newT = time;
7 }
8 else
9 {
10 time = time * 2;
11 if (! m_fPeriod)
12 {
13 m_fPeriod = 0.3f * 1.5f;
14 }
15
16 ccTime s = m_fPeriod / 4;
17
18 time = time - 1;
19 if (time < 0)
20 {
21 newT = -0.5f * powf(2, 10 * time) * sinf((time -s) * M_PI_X_2 / m_fPeriod);
22 }
23 else
24 {
25 newT = powf(2, -10 * time) * sinf((time - s) * M_PI_X_2 / m_fPeriod) * 0.5f + 1;
26 }
27 }
28
29 m_pOther->update(newT);
30 }
在这段代码中,首先将time扩大一倍,然后在减去一,也就是将时间分成了前后两段。
接着对前后两段时间分别套用相应的公式,进行变换。
基本都挺正常的,除了下面这一段:
11 if (! m_fPeriod)
12 {
13 m_fPeriod = 0.3f * 1.5f;
14 }
这个周期参数百分之百会被赋值,而且正常人也不会传入一个零作周期。我实在是看不出来这段代码有什么用途,所以还得请高人赐教。
小结
至此CCActionEase类系的动作就都介绍完了。其中最头痛的就是这个Ease Elastic类的动作,要把这类动作所做的运动描述清楚,感觉是挺费劲的。
到底该怎么更好的描述Ease Elastic类的动作呢?还得让大家一起动动脑子。
这类的动作用在游戏里什么地方最合适?谁想到了就积极的留言吧。
最后,总结一下本节的重点:
1.如果fPeriod的值越小,那么精灵弹的次数就越多。
2.如果想要精灵完美运动,即起始点(0,0)、结束点(1,1)以及连接点(0.5,0.5)都在函数曲线上,那么fPeriod的值应该符合下面的公式:
fPeriod=4/(2*N+1) N≥0
3.在逻辑上fPeriod的值要大于零,在实际应用中还要受float类型精度的限制。
谢谢各位的观看,如果您有什么好的建议,请给我留言。
这篇关于CCActionEase想说爱你也不难的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!