CCActionEase想說愛你也不難(下)


尊重作者勞動,轉載時請標明文章出處。
作者: Bugs Bunny
地址: http://www.cnblogs.com/cocos2d-x/archive/2012/03/17/2403007.html

本文函數圖像使用GeoGebra繪制,感謝它才華橫溢的作者。

我們前面介紹的動作主要是用來改變內部動作的執行速度,接下來要介紹的這幾個動作主要是用來增加表現效果的,可以看作是簡單的特效。

10)CCEaseBackIn

1 void CCEaseBackIn::update(ccTime time)
2 {
3 ccTime overshoot = 1.70158f;
4 m_pOther->update(time * time * ((overshoot + 1) * time - overshoot));
5 }

前面我們已經做過很多次了,大家也一定都是輕車熟路,推導出一下公式:
s(t)=(overshoot+1)*t^3-overshoot*t^2 t∈[0,1]
v(t)=s'(t)=3*(overshoot+1)*t^2-2*overshoot*t t∈[0,1]
a(t)=v'(t)=6*(overshoot+1)*t-2*overshoot t∈[0,1]

在GeoGebra中繪制出函數圖像:

請看圖中的藍色曲線。在動作開始執行后,精靈首先朝着y軸負方向運動,大約運動到y=-0.1的時候,開始按照正常的運動方向朝着點G(1,1)移動。

聽起來有點兒復雜,那我給大家講個故事。從前武狀元考試,有個代號1.70158的人也來參加了,他把箭搭在弓上,然后用力向后拉,可惜力氣太小了,只能把弓拉開10%,手一滑,箭就飛出去了,沒想到正中靶心點G(1,1),陰差陽錯的就當了武狀元。

這故事講完了,那您也一定聽出來了,這CCEaseBackIn其實就是一個搭弓射箭的過程。

那這個代號1.70158的武狀元是不是把弓拉開了10%呢?有興趣的朋友可以跟着我一起算一算。

圖中的紅色曲線代表速度,只要它在x軸下方,那就說明這個武狀元正在拉弓。如果紅色曲線到了x軸上方,那就表示他把箭放出去了。所以紅色曲線與x軸的交點B就是他松開手的那一刻。過點B做x軸的垂線,交藍色曲線與C點,這個C點的縱坐標就是武狀元拉弓的程度。

按照這個過程我們計算得出點B的橫坐標為:
B.X=2*overshoot/(3*(overshoot+1))=0.419897
C.X=0.419897
將此值帶入s(t)函數內,得出:
C.Y=s(C.X)=-0.1

因為這個動作的主要目的是實現特效,所以它的速度是多少就不是很重要了,有需要的朋友請自行計算紅色曲線在[0,1]范圍內的極值。

11)CCEaseBackOut

1 void CCEaseBackOut::update(ccTime time)
2 {
3 ccTime overshoot = 1.70158f;
4 time = time - 1;
5 m_pOther->update(time * time * ((overshoot + 1) * time + overshoot) + 1);
6 }

將動作CCEaseBackIn倒着播放就是CCEaseBackOut了,請看圖:

12)CCEaseBackInOut

Bug #961: fix mad behaviour in second stage of CCEaseBackInOut

cocos2d-1.0.1-x-0.12.0之前的版本中,CCEaseBackInOut動作的行為有點兒問題,如果你還在使用老的版本,先去升級一下吧。

 1 void CCEaseBackInOut::update(ccTime time)
2 {
3 ccTime overshoot = 1.70158f * 1.525f;
4
5 time = time * 2;
6 if (time < 1)
7 {
8 m_pOther->update((time * time * ((overshoot + 1) * time - overshoot)) / 2);
9 }
10 else
11 {
12 time = time - 2;
13 m_pOther->update((time * time * ((overshoot + 1) * time + overshoot)) / 2 + 1);
14 }
15 }

看到這段代碼的你,可能會無比的迷惑。這迷惑不是莫名而來的,自打你見到overshoot第一眼的時候,這種子就已經種在你的心里了。

Why does 1.70158 equal a 10% "bounce"?
What does 1.70158 mean?
這里的1.525又是什么鬼東西?!

看來,如果我不把這些講清楚,今天是收不了工了。

那我就簡單地講一講。至於對與不對,各位您可擦亮了眼睛。

還記得上面我們計算C.Y的過程嗎?如果我們不把overshoot的值代進去,再推導一次。

C.X=2*overshoot/(3*(overshoot+1))
C.Y=s(C.X)
C.Y=(2*overshoot/(3*(overshoot+1)))^2*((overshoot+1)*(2*overshoot/(3*(overshoot+1)))-overshoot)
C.Y=(2*overshoot/(3*(overshoot+1)))^2*(2*overshoot/3-overshoot)
C.Y=(2*overshoot/(3*(overshoot+1)))^2*(-overshoot/3)
C.Y=-(4*overshoot^3)/(27*(overshoot+1)^2)

因為我們要找出10%對應的overshoot是多少,並且C.Y是在x軸下方,所以我們令C.Y=-0.1。

C.Y=-0.1
-(4*overshoot^3)/(27*(overshoot+1)^2)=-1/10
40*overshoot^3=27*(overshoot+1)^2
40*overshoot^3-27*overshoot^2-54*overshoot-27=0

下面就是純數學問題了,求解一元三次方程

經過一系列運算,得出這個一元三次方程有一個實根和兩個共軛復根。這個實根就是我們想要的值——1.70154。對,你沒看錯,我算出來的就是1.70154。我也不知道為什么不是1.70158,難道是神奇的誤差?

我們再來看看這個1.525是怎么來的。

而CCEaseBackInOut其實就是把CCEaseBackIn和CCEaseBackOut的圖像縮小成一半,然后分別放入[0,0.5]和[0.5,1]區間內。因為是縮小一半,所以我們需要重新計算overshoot的值,要讓原來彈出10%變成20%,這樣縮小后才能保持彈出的幅度是一樣的。

令C.Y=-0.2,得出一元三次方程:
20*overshoot^3-27*overshoot^2-54*overshoot-27=0

求解overshoot的值為2.59239,它正好是原來的1.70154的1.52355倍。

哎呀,糗大了,好不容易算出來兩個數,跟代碼里大家用的還不一樣。

這到底是怎么回事?是誤差的問題?還是我的算法不對?等待高人指點。

13)CCEaseBounceOut

這次我們要稍微調整一下順序,先來介紹CCEaseBounceOut動作,因為CCEaseBounceIn是按照它的定義做的鏡像,所以CCEaseBounceOut才是實現的本體。

1 void CCEaseBounceOut::update(ccTime time)
2 {
3 ccTime newT = bounceTime(time);
4 m_pOther->update(newT);
5 }

這次的變換函數獨立出來了,我們跟進去看看。

 1 ccTime CCEaseBounce::bounceTime(ccTime time)
2 {
3 if (time < 1 / 2.75)
4 {
5 return 7.5625f * time * time;
6 } else
7 if (time < 2 / 2.75)
8 {
9 time -= 1.5f / 2.75f;
10 return 7.5625f * time * time + 0.75f;
11 } else
12 if(time < 2.5 / 2.75)
13 {
14 time -= 2.25f / 2.75f;
15 return 7.5625f * time * time + 0.9375f;
16 }
17
18 time -= 2.625f / 2.75f;
19 return 7.5625f * time * time + 0.984375f;
20 }

這回竟然換了4次計算公式,我們先把它畫出來。

可能你還沒有看出這是什么,為了讓大家都能看明白,我們把它按照y=0.5做一次軸對稱鏡像。

現在都看出來了嗎?

對了,CCEaseBounceOut動作就是模擬的小球掉落的彈跳運動。

這里小球一共彈起了3次。在第3次落地后,小球終於沒有足夠的力氣再跳起來了。

14)CCEaseBounceIn

前面我們說過CCEaseBounceIn動作其實就是按照CCEaseBounceOut的定義鏡像而來的。

1 void CCEaseBounceIn::update(ccTime time)
2 {
3 ccTime newT = 1 - bounceTime(1 - time);
4 m_pOther->update(newT);
5 }

鏡像的方式是按照點(0.5,0.5)做的中心對稱。

倒着播放小球掉落的畫面是個什么樣子?或者想象一下將靜止在地面上的籃球拍打起來的過程。

15)CCEaseBounceInOut

 1 void CCEaseBounceInOut::update(ccTime time)
2 {
3 ccTime newT = 0;
4 if (time < 0.5f)
5 {
6 time = time * 2;
7 newT = (1 - bounceTime(1 - time)) * 0.5f;
8 }
9 else
10 {
11 newT = bounceTime(time * 2 - 1) * 0.5f + 0.5f;
12 }
13
14 m_pOther->update(newT);
15 }

CCEaseXxxxInOut動作的實現永遠都是最沒意思的,無非就是把CCEaseXxxxIn和CCEaseXxxxOut縮小一半,然后再拼在一起。

小結

今天一共介紹了兩類動作,第一類是在模擬彈射運動,第二類是在模擬小球掉落之類的彈跳運動。它們主要不是為了修改內部動作的速度,而是為其增加特殊的顯示效果。

由於篇幅的限制,Ease Elastic類動作將放到下次介紹。

如果你去查看參考手冊,在這三類動作的描述中,會看到這樣的警告:

This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.

所以不要把CCSequence之類的動作傳遞給它們作參數,但是你可以把它們傳遞給CCSequence來創建動作隊列。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM