轉自:http://blog.csdn.net/ufolr/article/details/7447773
在cocos2d中,系統提供了CCMove、CCJump、CCBezier(貝塞爾曲線)等讓精靈移動的action,但是有時候,為了讓程序看上不不是那么的呆板,或者為了實現某些特定的功能,我們需要讓精靈按照我們自己設定的路徑(曲線運動)來移動。這就是這位篇文章我們需要討論的話題。
自己開始也很糾結cocos2dx沒有提供更多的action動作,比如說我們要做個拋物線什么的,雖然可以用貝塞爾曲線來模擬。
用貝塞爾曲線扔個飛鏢什么的倒是還不錯,但當你需要重復執行action時,問題就出來了,再第二次重復貝塞爾曲線動作到時候,精靈就會飛到別的地方去了。(出現這個問題的原因,猜測貝塞爾曲線是沒有起點和終點了,在第一次執行了動作之后,之前的曲線動作並沒有被釋放,第二次再延續這個動作,就會延為執行的那段曲線移動,當然,只是猜測,未深入研究。后來覺得不是這個原因,但具體原因未明。)
如果我們要做一個橢圓的軌跡,有人說用3~4條貝塞爾曲線來模擬,但實驗證明,在兩天貝塞爾曲線的銜接點Action會有停頓,所以效果簡直可以用魯迅先生的“目不忍視”來形容。
於是,我們考慮自己定義曲線的路徑,讓精靈按照我們自己的定義來行動。
需求:
將自己設定的路徑封裝成一個action,讓精靈執行,這里以橢圓軌跡為例。
先來兩張效果圖:
實現:
單獨建一個自己的動作模塊:LRActionInterval。{LRActionInterval.h&LRActionInterval.cpp}
基於cocos2d-x的CCActionInterval來封裝自己的動作,所以:
LRActionInterval.h
#include "CCActionInterval.h"//包含系統延時類動作頭文件 using namespace cocos2d;
想一想確定一個橢圓的條件,初中老師告訴我們,去頂一個橢圓我們需要知道他的空間位置(中心點坐標)、長半軸(a)、和短半軸(b)(或者知道半焦距(c))。也就是我們需要三個量來確定一個橢圓,所以在LRActionInterval.h中定義一個包含三個成員的結構來作為我們生成橢圓的參數:
// 定義一個結構來包含確定橢圓的參數 typedef struct _lrTuoyuanConfig { //中心點坐標 CCPoint centerPosition; //橢圓a長,三角斜邊 float aLength; //橢圓c長,三角底邊 float cLength; } lrTuoyuanConfig;
然后定義我們的橢圓的類:
class __declspec(dllexport) LRTuoyuanBy : public CCActionInterval { public: //用“動作持續時間”和“橢圓控制參數”初始化動作 bool initWithDuration(ccTime t, const lrTuoyuanConfig& c); virtual void update(ccTime time);//利用update函數來不斷的設定坐標 public: //用“動作持續時間”和“橢圓控制參數”創建動作 static LRTuoyuanBy *actionWithDuration(ccTime t, const lrTuoyuanConfig& c); protected: lrTuoyuanConfig m_sConfig; CCPoint m_startPosition; CCPoint s_startPosition; };
接下來是我們的實現部分:
LRActionInterval.cpp
其實設定路徑就是不斷的刷新,將路徑上的點賦給執行action的對象。
因此,既然我們要做一個橢圓的軌跡,我們就需要得到橢圓上每個點的坐標值,然后將其賦給執行action的對象。獲得橢圓的軌跡,再次回想初中老師的教導——橢圓標准方程:x^2/a+y^2/b=1。
但這是個2次方程,李勇這個方程求x、y的值的時候會需要開方,而開方后還需要確定正負,雖然可以實現功能,但是給自己增加了不少代碼量,也會浪費不少筆芯。所以我們要找一個更簡單的公式——橢圓參數方程。
參數方程:x=acos(θ)y=bsin(θ);利用這個一次方程可以直觀的計算出當前坐標點。
由橢圓的參數方程我們可以分別寫出返回X/Y坐標值的函數:
static inline float tuoyuanXat( float a, float bx, float c, ccTime t )//返回X坐標 { //參數方程 return -a*cos(2*3.1415926*t)+a; } static inline float tuoyuanYat( float a, float by, float c, ccTime t )//返回Y坐標 { float b = sqrt(powf(a, 2) - powf(c, 2));//因為之前定義的參數是焦距c而不是短半軸b,所以需要計算出b //參數方程 return b*sin(2*3.1415926*t); }
然后實現根據中心左邊、a、c確定橢圓:
// //TuoyuanBy // LRTuoyuanBy* LRTuoyuanBy::actionWithDuration(ccTime t, const lrTuoyuanConfig& c)//利用之前定義的橢圓的三個參數初始化橢圓 { LRTuoyuanBy *pTuoyuanBy = new LRTuoyuanBy(); pTuoyuanBy->initWithDuration(t, c); pTuoyuanBy->autorelease(); return pTuoyuanBy; } bool LRTuoyuanBy::initWithDuration(ccTime t, const lrTuoyuanConfig& c) { if (CCActionInterval::initWithDuration(t)) { m_sConfig = c; return true; } return false; } void LRTuoyuanBy::update(ccTime time) { if (m_pTarget) { CCPoint s_startPosition =m_sConfig.centerPosition;//中心點坐標 float a = m_sConfig.aLength; float bx = m_sConfig.centerPosition.x; float by = m_sConfig.centerPosition.y; float c = m_sConfig.cLength; float x = tuoyuanXat(a, bx, c, time);//調用之前的坐標計算函數來計算出坐標值 float y = tuoyuanYat(a, by, c, time); m_pTarget->setPosition(ccpAdd(s_startPosition, ccp(x-a, y)));//由於我們畫計算出的橢圓你做值是以原點為中心的,所以需要加上我們設定的中心點坐標 } }
這樣我們只需要在程序中像使用CCBezier一樣使用LRTuoyuan,讓精靈執行這個Action,他就會沿着我們設定的橢圓運動了。當然,只要你給出你自己的運動函數軌跡,精靈就會按照你自己設定的軌跡運動。