cocos2dx 3.2 學習篇之六(精靈運動,自定義運動軌跡(太極八卦))
好了,今天想要講的是如何讓精靈按照自己定義的路徑去運動,官方給我們了一個action類,裡面有很多運動的型別,比如bezier曲線運動,比如jump運動等等,設計好了運動之後,我們只要讓精靈調運runAction()即可。 本人呢是十足的秦時明月粉絲,特別喜歡裡面 的 少司命!!!簡直是我的女神呀,所以自然想和少司命搭上一點關係那,就記得當初少司命 搭救 盜跖 那次 耍了一招 用樹葉畫八卦陣放大招技能來著。 然後就讓我各種各種激動(原諒我愛少司命愛的那麼深沉!)
好的,轉入正題,首先我們可以看看利用 粒子系統 + 運動軌跡做出來效果如何。。。
這個是我靜態的粒子系統,是樹葉繞著中心轉,有一點點散開的效果:
這個是粒子系統在在八卦路線的時候樣子,如果有少司命的纖細小手的話技能就好看了:
由於本人用了粒子系統有散開效果,所有早先的路徑下葉子就有點散開了,也可以通過葉子濃度看出葉子經過的路徑方向。
首先我們來分析原理,最一開始,我們畫了一個 半徑為R,中心點為(a,b)的圓,等圓畫完以後緊接著畫一個半斤為 R/2 ,中心點為(a-R/2 , b)的半個圓(上半個圓弧),再接著,我們就畫半徑為 R/2 ,中心點為 (a+R/2 , b )的半圓弧(在圖中由於截圖過早的問題還沒有畫完整)。這樣,我們要的八卦效果就出來了。。。
由上面分析可知,我們必須要解決畫圓演算法:由初中還是高中知識可知,x^2 + y^2 = R^2。然後再做適當平移(a,b)就可以得到我們指定中心點為(a,b),半徑為R的圓了。 當然 x^2 + y^2 = R^2 求解由於正負的關係考慮比較複雜 ,所以我把它轉化成為 :
(R*cos(α))^2 + (R*sin(α))^2 = R^2
//定義一個結構體,包含裡中心點座標以及要畫圓的半徑長度
typedef struct _WuzhuangConfig
{
cocos2d::Point centerPosition;//中心點座標
float rudius;//半徑
}WuzhuangConfig;
//根據畫圓進度t以及圓的配置資訊計算出圓上點座標 static inline Vec2 PointAt(float t /*這裡的t代表畫圓進度,若單純順時針畫一個圓時,與動作進度相同 ,若逆時針或者畫半圓等需要一定對映關係*/ , const WuzhuangConfig c ) { float x = - c.rudius * cos(3.1415926 * t * 2 ) + c.centerPosition.x ; //上面加負號時因為從左手邊開始畫起 float y = c.rudius * sin(3.1415926 * t * 2 ) + c.centerPosition.y; return Vec2(x,y); }
好,上面解決了畫圓問題,下面我們來剖析我們畫圓的順序與 動作進度 t (0代表動作起點,1代表動作結束,0.5代表動作執行到了剛剛好一半) 之間關係 。
上面大圓的周長是全路徑的 2/3 (初中知識不解釋),小半圓分別佔了全路徑的 1/6 。
故 t < 2.0/3 && t>0 時 , 我們執行畫大圓操作。因為我們要畫的整個大圓 ,而不是2/3個圓,故我們需要把動作進度對映到畫圓進度上 即畫大圓的進度 p = t * 3/2 。這樣就可以畫出整個大圓了。
同理 t < 5/6 && t >2/3 時,p = (t- 2.0/3) * 3 ; 當 t >5/6 && t < 1 時,p = 7.0/2 - t * 3 ;
知道了這個對映關係以後,我們就可以寫出整個畫八卦的根據 動作進度 t 的函式
void Wuzhuang::update(float t)
{
if(_target)
{
if(t < 2.0/3)
{
_target->setPosition(PointAt( 3.0 / 2 * t, m_config ));
}
else
{
if(t < 5.0/6)
{
_target->setPosition(PointAt((t- 2.0/3) * 3 , m_config1/*小圓1配置資訊*/ ));
}
else
{
_target->setPosition(PointAt( 7.0/2 - t * 3, m_config2/*小圓2配置資訊*/ ));
}
}
}
}
介紹完了畫圓核心的,剩下的就是簡單設定配置屬性和定義一個呼叫介面了。
//小圓的配置資訊
WuzhuangConfig m_config1,m_config2;
//初始化
bool Wuzhuang::initWithDuration(float t, const WuzhuangConfig& c)
{
if (ActionInterval::initWithDuration(t))
{
m_config = c;
m_config1.rudius = c.rudius/2;
m_config2.rudius = c.rudius/2;
m_config1.centerPosition = c.centerPosition - Vec2(c.rudius/2,0);
m_config2.centerPosition = c.centerPosition + Vec2(c.rudius/2,0);
return true;
}
return false;
}
//外部呼叫的靜態介面
Wuzhuang* Wuzhuang::actionWithDuration(float t,const WuzhuangConfig& c)
{
Wuzhuang* pWuzhuang = new Wuzhuang();
pWuzhuang ->initWithDuration(t, c);
pWuzhuang ->autorelease();
return pWuzhuang;
}
基本整個程式碼就是這樣了。。這樣外部就可以呼叫了。呼叫的話就像呼叫bezierby一樣呼叫就好了。
WuzhuangConfig m_config;
m_config.centerPosition = Vec2(370, 500);
m_config.rudius = 200;
Wuzhuang* pAction = Wuzhuang::actionWithDuration(4, m_config);
p->runAction(pAction);
所有想要自定義的動作都可以通過這樣實現。如果不懂可以去看看BezierBy這樣的類他們時怎麼實現的,仿照他們這樣寫就可以了。。。